Retry Geolocation request without refreshing the browser - javascript

I have a specific issue with the Geolocation api. This is my scenario:
User landed on a page ( in Chrome - Android ) with GPS location disabled.
There is a button on the page, and onClick of the button triggers the Geolocation.getCurrentPosition
Geolocation goes to the error callback with error message “User denied Geolocation”
User goes to Android settings ( mostly in notification drawer ) and turn on the location
User click the button again ( at this time, location is available ) to get the coordinates
However, Geolocation api still throws the error “User denied Geolocation”
—
At this time, the geolocation request will work only if the user refreshes the page and press the button ( note: the location is still enabled )
Is there a way to make this work without the browser refresh ?
Here is the jsbin link: https://output.jsbin.com/pihenud

Finally, I was able to solve this problem using an iFrame hack.
Here is the solution:
Instead of asking the permission in the main window, create an iFrame dynamically and call the geolocation.getCurrentPosition inside it. Now, we can use the window.postMessage to pass the position data to the parent browser window.
When we have to retry the geolocation request again, we just need to reload the iframe -- This will have the new site settings.
Here is the iframe code if anyone wants to try:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Namshi Geolocation</title>
</head>
<body>
<script type="text/javascript">
var sendMessage = function(result){
window.postMessage(JSON.stringify(result), window.location.origin);
};
var triggerGeolocationRequest = function(){
var options = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
};
var result;
if(window.navigator.geolocation){
window.navigator.geolocation.getCurrentPosition(function(position){
var result = {
type: 'success',
data: {lat: position.coords.latitude, lng: position.coords.longitude}
};
sendMessage(result);
}, function(err){
var result = {
type: 'error',
data: { message: err.message, code: err.code }
};
sendMessage(result);
}, options)
} else {
result = {
type: 'error',
data: { message: 'No Geolocation API' }
};
sendMessage(result);
}
};
window.addEventListener('load', triggerGeolocationRequest);
</script>
</body>
</html>
And in your application code, you can have a utility to inject the iFrame. See the below code:
utils.getCurrentPosition = function(){
return new Promise(function(resolve, reject){
var ifr = document.createElement('iframe');
ifr.style.opacity = '0';
ifr.style.pointerEvents = 'none';
ifr.src = location.origin + '/geo.html'; // the previous html code.
document.body.appendChild(ifr);
ifr.contentWindow.addEventListener('message', function(message){
message = JSON.parse(message.data);
if(message.type === 'success'){
resolve(message.data);
} else {
reject(message.data);
}
document.body.removeChild(ifr);
});
});
};

Related

Location popup not working after select block option

I am working on Php and ajax, I have button and whenever we click on that button,then
"Location popup" ( allow site to access your location) showing after select "allow" code is working fine
but whenever we click on "block" then futher code is not working (function error) not working,How can i do this ?
<script>
$(document).ready(function () {
$(".in").click(function () {
const options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
function success(pos) {
const crd = pos.coords;
var lats = crd.latitude;
var longs = crd.longitude;
var userId = <?php echo $ids=$_GET['eid']; ?>
$.ajax({
// further code
});
}
function error(err) {
var pathname = window.location.href;
// Now want to go futher even click on "block" button
}
navigator.geolocation.getCurrentPosition(success, error, options);
});
});
<button name="intime" class="clcbtn in">Clock in</button>
You can have another ajax call in your error function, but if the first error was caused by a problem with ajax, then the second ajax call might also run into an error.
So, you should make sure, that the error was because the user declined the access adn then you can have another ajax call.
function error(err) {
if (error.code == error.PERMISSION_DENIED) {
// Put your second ajax call here
}
}

chrome.runtime.sendMessage not working on the 1st click when running normally. it works while debugging though

I have a function in the context.js which loads a panel and sends a message to panel.js at the last. The panel.js function updates the ui on receiving that msg. But it is not working for the first click i.e. it just loads normal ui, not the one that is expected that is updated one after the msg is received. while debugging it works fine.
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [{
"all_frames": false,
"matches": ["<all_urls>"],
"js":["context.js"]
}],
"permissions": ["activeTab","<all_urls>", "storage","tabs"],
"web_accessible_resources":
"panel.html",
"panel.js"
]
context.js - code
fillUI (){
var iframeNode = document.createElement('iframe');
iframeNode.id = "panel"
iframeNode.style.height = "100%";
iframeNode.style.width = "400px";
iframeNode.style.position = "fixed";
iframeNode.style.top = "0px";
iframeNode.style.left = "0px";
iframeNode.style.zIndex = "9000000000000000000";
iframeNode.frameBorder = "none";
iframeNode.src = chrome.extension.getURL("panel.html")
document.body.appendChild(iframeNode);
var dataForUI = "some string data"
chrome.runtime.sendMessage({action: "update UI", results: dataForUI},
(response)=> {
console.log(response.message)
})
}
}
panel.js - code
var handleRequest = function(request, sender, cb) {
console.log(request.results)
if (request.action === 'update Not UI') {
//do something
} else if (request.action === 'update UI') {
document.getElementById("displayContent").value = request.results
}
};
chrome.runtime.onMessage.addListener(handleRequest);
background.js
chrome.runtime.onMessage.addListener((request,sender,sendResponse) => {
chrome.tabs.sendMessage(sender.tab.id,request,function(response){
console.log(response)`
});
});
panel.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="panel.css" />
</head>
<body>
<textarea id="displayContent" rows="10" cols="40"></textarea>
</body>
</html>
Any suggestions on what I am doing wrong or what can I do instead?
An iframe with a real URL loads asynchronously so its code runs after the embedding code finishes - hence, your message is sent too early and is lost. The URL in your case points to an extension resource so it's a real URL. For reference, a synchronously loading iframe would have a dummy URL e.g. no src at all (or an empty string) or it would be something like about:blank or javascript:/*some code here*/, possibly srcdoc as well.
Solution 1: send a message in iframe's onload event
Possible disadvantage: all extension frames in all tabs will receive it, including the background script and any other open extension pages such the popup, options, if they also have an onMessage listener.
iframeNode.onload = () => {
chrome.runtime.sendMessage('foo', res => { console.log(res); });
};
document.body.appendChild(iframeNode);
Solution 2: let iframe send a message to its embedder
Possible disadvantage: wrong data may be sent in case you add several such extension frames in one tab and for example the 2nd one loads earlier than the 1st one due to a bug or an optimization in the browser - in this case you may have to use direct DOM messaging (solution 3).
iframe script (panel.js):
chrome.tabs.getCurrent(ownTab => {
chrome.tabs.sendMessage(ownTab.id, 'getData', data => {
console.log('frame got data');
// process data here
});
});
content script (context.js):
document.body.appendChild(iframeNode);
chrome.runtime.onMessage.addListener(
function onMessage(msg, sender, sendResponse) {
if (msg === 'getData') {
chrome.runtime.onMessage.removeListener(onMessage)
sendResponse({ action: 'update UI', results: 'foo' });
}
});
Solution 3: direct messaging via postMessage
Use in case of multiple extension frames in one tab.
Disadvantage: no way to tell if the message was forged by the page or by another extension's content script.
The iframe script declares a one-time listener for message event:
window.addEventListener('message', function onMessage(e) {
if (typeof e.data === 'string' && e.data.startsWith(chrome.runtime.id)) {
window.removeEventListener('message', onMessage);
const data = JSON.parse(e.data.slice(chrome.runtime.id.length));
// process data here
}
});
Then, additionally, use one of the following:
if content script is the initiator
iframeNode.onload = () => {
iframeNode.contentWindow.postMessage(
chrome.runtime.id + JSON.stringify({foo: 'data'}), '*');
};
document.body.appendChild(iframeNode);
if iframe is the initiator
iframe script:
parent.postMessage('getData', '*');
content script:
document.body.appendChild(iframeNode);
window.addEventListener('message', function onMessage(e) {
if (e.source === iframeNode) {
window.removeEventListener('message', onMessage);
e.source.postMessage(chrome.runtime.id + JSON.stringify({foo: 'data'}), '*');
}
});
one possible way that worked for me is by using functionality in setTimeout() method.
in context.js
setTimeout(() => {
chrome.runtime.sendMessage({action: "update UI", results: dataForUI},
(response)=> {
console.log(response.message)
}
)
}, 100);
But I am not sure if this is the best way.

Load another html page without changing URL

I have some problem with page load on desktop browser and mobile browser. In this case I have 4 html page one is home.html, second is home-mobile.html, third is product.html and fourth is product-mobile.html. My problem is I don't know to switch the html page if opened in mobile browser. For the example is when I open www.example.com in desktop the page will call home.html but when I open www.example.com in mobile the page will call home-mobile.html and so with product page, when I open www.example.com/product in desktop it will call product.html but when I open www.example.com/product in mobile it will call product-mobile.html. The point is how could I open one link and it will detect opened in desktop browser or mobile browser and call different html page.
Which I have been done now but still not working is :
<script>
window.mobilecheck = function() {
var check = false;
if(window.innerWidth<768){
check=true;
}
return check;
}
if(window.mobilecheck()){
window.location.href="home-mobile.html";
}
else {
window.location.href="home.html";
}
</script>
But with that script the URL was changing and not be the same.
Please anyone know how to do this could help me. Thanks.
This script allows you to change the page content without redirecting the browser.
window.mobilecheck = function() {
var check = false;
if (window.innerWidth < 768) {
check = true;
}
return check;
}
if (window.mobilecheck()) {
$.ajax({
'type': 'POST',
'url': 'home_mobile.html',
'data': postData,
'success': function(response) {
$("html").html(response);
}
});
}
modify the code as you need.
If your device mobile it will automatically redirect on mobile page. Simple use this code. No need for else condition. just check mobile device.
if ($(window).width() < 767) {
function getMobileOperatingSystem() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
"Android"
if (/windows phone/i.test(userAgent)) {
return "Windows Phone";
}
if (/android/i.test(userAgent)) {
return "Android";
}
if (/iPhone|iPod/.test(userAgent) && !window.MSStream) {
return "iOS";
}
return "unknown";
}
if(getMobileOperatingSystem()){
window.location="\home-mobile.html";
}
}
Let's say your page is home.html.
And when the page is loaded, you can run a script to detect client type( desktop or mobile), then send an Ajax call to corresponding page to get the content you want.
Then when the Ajax call returned, you can simply replace current content with the returned value.
In this way, your URL will not change at all, and the content can change according to current client type.
home.html:
<html>
<head>
<script language="javascript">
function loadContent(){
var clientType
//your code to get client type here
//Below block to get Ajax client object according to your browser type
var XHTTP;
try
{
//For morden versions Internet Explorer
XHTTP = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (ex)
{
try
{
try
{
//For old versions Internet Explorer
XHTTP=new ActiveXObject("Microsoft.XMLHTTP");
}
catch(exxx)
{
XHTTP = new XMLHttpRequest("Msxml2.XMLHTTP");
}
}
catch(exx)
{
try
{
//For browsers other than Internet Explorer
XHTTP= new XMLHttpRequest();
}
catch(eexx)
{
//This means ActiveX is not enabled.
//To enabled go to your browser settings and enabled the ActiveX.
alert("Not Supported, It might be previous version browser");
}
}
}
if(clientType=="mobile"){
XHTTP.open("GET","/home_mobile.html");
}else{
XHTTP.open("GET","/home_desktop.html")
}
XHTTP.onreadystatechange= function()
{
if (XHTTP.readyState==4)
{
var result=XHTTP.responseText.toString();
document.getElementById("content").innerHTML=result;
}
}
//This finlly send the request.
XHTTP.send(null);
}
</script>
</head>
<body onload="javascript:{loadContent();}">
<div id="content"></div>
</body>
</html>
This script will change the html content of a current page with requested page html without changing url. (AJAX)
var xhr = new XMLHttpRequest(); // Create XMLHttpRequest object
xhr.onload = function() { // When response has loaded
// The following conditional check will not work locally - only on a server
// if(xhr.status === 200) { // If server status was ok
document.getElementsByTagName("html")[0].innerHTML= xhr.responseText; // Update
//}
};
xhr.open('GET', 'html u want to replace ', true); // Prepare the request
xhr.send(null); // Send the request
});

Coordinate recording in Cordova/PhoneGap produces odd results. Does my phone or the GPS satellite have anything to do with it?

Edit - I plotted the reuslt using mapcustomizer
I took the code from these geolocation docs, hooked up my phone and went around the block:
Here's the code (barely modified from this geoloc tutorial page):
<!DOCTYPE html>
<html>
<head>
<title>Device Properties Example</title>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8">
// Wait for device API libraries to load
//
// document.addEventListener("deviceready", onDeviceReady, false);
var watchID = null;
// device APIs are available
//
function onDeviceReady() {
// Get the most accurate position updates available on the
// device.
var options = { maximumAge: 3000, timeout: 10000, enableHighAccuracy: true };
navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
}
// onSuccess Geolocation
//
function onSuccess(position) {
var element = document.getElementById('geolocation');
element.innerHTML = position.coords.latitude + ',' + position.coords.longitude +
'<hr />' + element.innerHTML;
}
// clear the watch that was started earlier
//
function clearWatch() {
alert('STOPPING!');
if (watchID != null) {
navigator.geolocation.clearWatch(watchID);
watchID = null;
}
}
// onError Callback receives a PositionError object
//
function onError(error) {
var element = document.getElementById('geolocation');
element.innerHTML = '<h1 style="color:red;">ERRORED!</h1>' +
'<hr />' + element.innerHTML;
}
</script>
</head>
<body>
<button onclick="clearInterval(intervalio);alert('I WIN', 'SHOULD HAVE STOPPED')" style="color:red;font-size:25px;">STOP RECORDING YOU TARD</button>
<script type="text/javascript">
intervalio = setInterval(function() {
onDeviceReady();
}, 1000);
</script>
<p id="geolocation">Watching geolocation...</p>
<button onclick="ondeviceReady();" style="color:green;font-size:25px;">STOP YOU IDIOT</button>
</body>
</html>
tl;dr I used a setInterval and clearInterval on my own instead of using the example; I'd prefer to have used .watchPosition and .clearWatch, but to my surprise .watchPosition returns undefined and therefore I can't stop it that way.
Questions:
With the interval of 1000ms, the entire route (I was going like 10-20 mph like a responsible citizen) should be LITTERED with endpoints. Instead, my house is full of tags but the rest of the route is sparse.
Is it because I lost WiFi connectivity? How does a router know my position in 3D? Do the options I've specified:
var options = { maximumAge: 3000, timeout: 10000, enableHighAccuracy: true };
Have something to do with the few points I'm seeing there? Does the GPS satellite have some kind of a ping with which it gets back to one's phone?

navigator.geolocation.getCurrentPosition always fail in chrome and firefox

I got strange behavior when I tried to test my
"navigator.geolocation.getCurrentPosition" web page. Here is my
testing result and code:
my code:
function detectLocation()
{
if (navigator.geolocation)
{
navigator.geolocation.getCurrentPosition(geocodePosition, onError, { timeout: 30000 });
navigator.geolocation.watchPosition(watchGeocodePosition);
}
else
{
onError();
}
}
this function was run when "body" onload event was called. I had tried to change the timeout to 10000 and 20000, but I still got same result. I also allowed crome and firefox to get my location.
result:
Using chrome (v 17.0.963.79 m), result always went to onError
function when navigator.geolocation.getCurrentPosition was called.
Using Firefox (v 10.0.2), result always went to onError function
when navigator.geolocation.getCurrentPosition was called.
Using IE (v 9), result was fantastic, I got my current location.
can anyone help me in this strange situation? I really didn't have any idea to solve this problem and I was in hurry on my project deadline. Thanks before.
EDIT :
For this couple days I got some progress, the error code code is 2 with a message "Network location provider at 'https://maps.googleapis.com/maps/api/browserlocation/json?browser=chromium&sensor=true' : Response was malformed". Still unsolved, does anyone know how to solve this?
I simulated this problem and found that the success callback functions were only called when the html page was hosted on a web server and not when opened from a filesystem.
To test I opened the file directly from my C: drive and it the callbacks didn't work and then hosted the file on Internet Information Services (IIS) and the callbacks did work.
<html>
<body onload="detectLocation()">
<!-- This html must be hosted on a server for navigator.geolocation callbacks to work -->
<div id="status"></div>
<script type="text/javascript">
function detectLocation()
{
log("detectLocation() starting");
if (navigator.geolocation)
{
log("navigator.geolocation is supported");
navigator.geolocation.getCurrentPosition(geocodePosition, onError, { timeout: 30000 });
navigator.geolocation.watchPosition(watchGeocodePosition);
}
else
{
log("navigator.geolocation not supported");
}
}
function geocodePosition(){
log("geocodePosition() starting");
}
function watchGeocodePosition(){
log("watchGeocodePosition() starting");
}
function onError(error){
log("error " + error.code);
}
function log(msg){
document.getElementById("status").innerHTML = new Date() + " :: " + msg + "<br/>" + document.getElementById("status").innerHTML;
}
</script>
</body>
</html>
I also got this message:
message: "Network location provider at 'https://www.googleapis.com/' : Returned error code 404.", code: 2
I could solve it by switching on my wifi adapter
I had the same issue. Chrome browser wan not returning a position on 30000 miliseconds timeout. Firefox was not returning a position too. I added the option enableHighAccuracy and set it to false but nothing changed(false is the default option). When i change it to true then geolocation started working!
This is my final code,
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function(position) {
// Get current cordinates.
positionCords = {"lat": position.coords.latitude, "lng": position.coords.longitude};
},
function(error) {
// On error code..
},
{timeout: 30000, enableHighAccuracy: true, maximumAge: 75000}
);
}
You need to be using https, not http.
The Chrome reference for this is here - https://developers.google.com/web/updates/2016/04/geolocation-on-secure-contexts-only
I know this is old topic but recently I had this error also:
message: "Network location provider at 'https://www.googleapis.com/' : Returned error code 404.", code: 2
The fix is to get api key for google maps and use it in your code
<script src='https://maps.googleapis.com/maps/api/jscallback=initMap
&signed_in=true&key=YOUR-API-KEY' async defer></script>
Here you can get API KEY: https://developers.google.com/maps/documentation/javascript/get-api-key#key
I had same issue and solution was to increase the timeout duration as mobile network are slower than wired network
{timeout: 30000, enableHighAccuracy: true, maximumAge: 75000}
along with enabling cellular positioning
In the Device Settings turn on "Wifi and Cellular positioning" option.
This will print the Latitude and Longitude of your Location
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<script>
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
}
function showPosition(position) {
document.getElementById('idLatitude').value = position.coords.latitude;
document.getElementById('idLongitude').value = position.coords.longitude;
}
</script>
</head>
<body onload="getLocation()">
<form action="HelloWorld" method="post">
<input id="idLatitude" type="text" name="strLatitude">
<input id="idLongitude" type="text" name="strLongitude">
</form>
</body>
</html>

Categories