I'm trying to create a weather app, sending Ajax requests to OpenWeatherMap. I've got an error in w.getWeatherFunc, when I'm giving the function sendRequest the parameter of w.weather and then giving the same parameter to the function displayFunc, which I'm calling next.
Here is what I've got in the console:
Uncaught TypeError: Cannot read property 'weather' of undefined
at displayFunc (weather.js:46)
at weather.js:78
How can I fix this and make it work?
function Weather () {
var w = this;
var weatherUrl = 'http://api.openweathermap.org/data/2.5/weather?';
var appid = '&appid=c0a7816b2acba9dbfb70977a1e537369';
var googleUrl = 'https://maps.googleapis.com/maps/api/geocode/json?address=';
var googleKey = '&key=AIzaSyBHBjF5lDpw2tSXVJ6A1ra-RKT90ek5bvQ';
w.demo = document.getElementById('demo');
w.place = document.getElementById('place');
w.description = document.getElementById('description');
w.temp = document.getElementById('temp');
w.humidity = document.getElementById('humidity');
w.getWeather = document.getElementById('getWeather');
w.addCityBtn = document.getElementById('addCity');
w.rmCityBtn = document.getElementById('rmCity');
w.icon = document.getElementById('icon');
w.wind = document.getElementById('wind');
w.time = document.getElementById('time');
w.lat = null;
w.lon = null;
w.cityArray = [];
w.weather = null;
function sendRequest (url, data) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.send();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
data = JSON.parse(request.responseText);
console.log(data);
return data;
} else {
console.log(request.status + ': ' + request.statusText);
}
}
}
function displayFunc (obj) {
console.log('obj ' + obj);
w.icon.src = 'http://openweathermap.org/img/w/' + obj.weather[0].icon + '.png';
var timeNow = new Date();
var hours = timeNow.getHours();
var minutes = timeNow.getMinutes() < 10 ? '0' + timeNow.getMinutes() : timeNow.getMinutes();
w.time.innerHTML = hours + ':' + minutes;
w.place.innerHTML = 'Place: ' + obj.name;
w.description.innerHTML = "Weather: " + obj.weather[0].description;
w.temp.innerHTML = "Temperature: " + w.convertToCels(obj.main.temp) + "°C";
w.humidity.innerHTML = "Humidity: " + obj.main.humidity + '%';
w.wind.innerHTML = 'Wind: ' + obj.wind.speed + ' meter/sec';
}
w.convertToCels = function(temp) {
var tempC = Math.round(temp - 273.15);
return tempC;
}
w.getWeatherFunc = function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(location){
w.lat = location.coords.latitude;
w.lon = location.coords.longitude;
var url = weatherUrl + 'lat=' + w.lat + '&lon=' + w.lon + appid;
var result = sendRequest(url, w.weather);
console.log(result);
displayFunc(result);
});
} else {
alert('Browser could not find your current location');
}
}
w.addCityBtn.onclick = function() {
var newCity = prompt('Please insert city', 'Kiev');
var gUrl = googleUrl + newCity + googleKey;
var newCityWeather = null;
sendRequest(url, newCityWeather);
var location = newCityWeather.results[0].geometry.location;
var newUrl = weatherUrl + 'lat=' + location.lat + '&lon=' + location.lng + appid;
sendRequest(newUrl, w.weather);
displayFunc(newCity);
w.cityArray.push(newCity);
}
window.onload = w.getWeatherFunc;
setInterval(function() {
w.getWeatherFunc();
}, 900000);
}
Your ajax return returns into the browsers engine. As its async you need to create a callback:
function sendRequest(url,data,callback){
//if the data was received
callback(data);
}
Use like this
sendRequest("yoururl",data,function(data){
displayFunc(data);
});
The first time you pass the obj to the function it will save it one scope higher. after that, if you don;t pass the object the one you saved earlier will be used.
var objBkp;
function displayFunc (obj) {
if(undefined === obj) obj = objBkp;
else objBkp = obj;
// rest of code here
}
In your sendRequest you are passing only the value of w.weather, not its reference. JavaScript doesn't pass variables by value or by reference, but by sharing. So if you want to give the value to your variable you should do this inside your function sendRequest:
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
w.weather = JSON.parse(request.responseText);
console.log(data);
return data;
} else {
console.log(request.status + ': ' + request.statusText);
}
}
Also, if you are using the attributes, you don't have to pass them in the function as arguments. Besides that fact, it would be good if you also create get() and set()
What does the console.log(result); in getWeatherFunc gives you?
The problem as I see it is that in the displayFunc the parameter passed is undefined.
Related
So I have some Javascript that does an XMLHttpRequest (xhr), on receival of response it does a second response (xhr2) to get remaining data.
But I have now split this second call into batches so that it has to call multiple times depending on how much data there. After the first request I know how many calls I haven to make
var batches = Math.ceil((counter.innerText.substring(0,counter.innerText.indexOf(" ")) - 100) / 1000);
But then I am just making multiple calls to xdr2, but this doesn't work because it closes after first call. So I realize I probably need to initialize multiple XMLHttpRequest based on the value of batches, but how do I define the onreadystatechange function succinctly.
function get_tracklist_data(path, cid, title)
{
var xhr = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();
xhr.onreadystatechange = function()
{
if (this.readyState == 4 && this.status == 200)
{
var counter = document.getElementById("counter");
counter.innerHTML = xhr.responseText.substring(0, xhr.responseText.indexOf(":"));
var data = document.getElementById("data");
data.innerHTML = xhr.responseText.substring(xhr.responseText.indexOf(":") + 1);
//Work out how many calls we need to make
var batches = Math.ceil((counter.innerText.substring(0,counter.innerText.indexOf(" ")) - 100) / 1000);
for(i=1; i<batches;i++)
{
xhr2.open('GET',path + '?cid=' + cid + "&title=" + title+"&batch=" + i, true);
xhr2.send();
}
}
};
xhr2.onreadystatechange = function()
{
if (this.readyState == 4 && this.status == 200)
{
var data = document.getElementById("tbody");
data.innerHTML+=xhr2.responseText;
}
};
xhr.open('GET',path + '?cid=' + cid + "&title=" + title +"&batch=0", true);
xhr.send();
};
Update to make xhr2 local variable in loop, but doesnt seem to work properly Im getting the same data back multiple times.
function get_tracklist_data(path, cid, title)
{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function()
{
if (this.readyState == 4 && this.status == 200)
{
var counter = document.getElementById("counter");
counter.innerHTML = xhr.responseText.substring(0, xhr.responseText.indexOf(":"));
var data = document.getElementById("data");
data.innerHTML = xhr.responseText.substring(xhr.responseText.indexOf(":") + 1);
//Work out how many calls we need to make
var batches = Math.ceil((counter.innerText.substring(0,counter.innerText.indexOf(" ")) - 100) / 1000);
for(i=1; i<batches;i++)
{
var xhr2 = new XMLHttpRequest();
xhr2.onreadystatechange = function()
{
if (this.readyState == 4 && this.status == 200)
{
var data = document.getElementById("tbody");
data.innerHTML+=xhr2.responseText;
}
};
xhr2.open('GET',path + '?cid=' + cid + "&title=" + title+"&batch=" + i, true);
xhr2.send();
}
}
};
xhr.open('GET',path + '?cid=' + cid + "&title=" + title +"&batch=0", true);
xhr.send();
};
here example using await fetch() and fetch().then()
function listenForButtonCollapse(buttonId, collapseId, buttonText) {
let button = document.getElementById(buttonId);
let section = document.getElementById(collapseId);
if (section != null) {
section.addEventListener('show.bs.collapse', function() {
button.innerText = 'Hide ' + buttonText;
});
section.addEventListener('hide.bs.collapse', function() {
button.innerText = 'Show ' + buttonText;
});
}
}
async function get_tracklist_data(path, cid, title) {
let xhr = await fetch(path + '?cid=' + cid + "&title=" + title + "&batch=0");
let responseText = await xhr.text()
let counter = document.getElementById("counter");
counter.innerHTML = responseText.substring(0, responseText.indexOf(":"));
let data = document.getElementById("data");
data.innerHTML = responseText.substring(responseText.indexOf(":") + 1);
listenForButtonCollapse('show_focus_button', 'focus_id', 'Spotlight');
listenForButtonCollapse('show_albums_button', 'albums_id', 'Albums');
listenForButtonCollapse('show_tracks_button', 'tracks_id', 'Tracks');
listenForButtonCollapse('show_works_button', 'works_id', 'Works');
//Work out how many calls we need to make
let batches = Math.ceil((counter.innerText.substring(0, counter.innerText.indexOf(" ")) - 100) / 1000);
data = document.getElementById("tbody");
for (i = 1; i < batches; i++) {
fetch(path + '?cid=' + cid + "&title=" + title + "&batch=" + i)
.then(resp => resp.text())
.then(responseText => {
data.innerHTML += responseText;
})
}
}
Using XMLHttpRequest: save xhr object into array then add parameter to the callback listXHR[i].onreadystatechange = function(x) then call x.target for current xhr object
function get_tracklist_data(path, cid, title) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var counter = document.getElementById("counter");
counter.innerHTML = xhr.responseText.substring(0, xhr.responseText.indexOf(":"));
var data = document.getElementById("data");
data.innerHTML = xhr.responseText.substring(xhr.responseText.indexOf(":") + 1);
//Work out how many calls we need to make
var batches = Math.ceil((counter.innerText.substring(0, counter.innerText.indexOf(" ")) - 100) / 1000);
var listXHR = []
for (i = 1; i < batches; i++) {
listXHR[i] = new XMLHttpRequest();
listXHR[i].onreadystatechange = function(x) {
if (x.target.readyState == 4 && x.target.status == 200) {
var data = document.getElementById("tbody");
data.innerHTML += x.target.responseText;
}
};
listXHR[i].open('GET', path + '?cid=' + cid + "&title=" + title + "&batch=" + i, true);
listXHR[i].send();
}
}
};
xhr.open('GET', path + '?cid=' + cid + "&title=" + title + "&batch=0", true);
xhr.send();
};
This is my code:
let info = document.getElementById('info');
let week = document.getElementById('week');
/*Initializer Function*/
window.addEventListener('load', () => {
let long;
let lat;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(position => {
long = position.coords.longitude;
lat = position.coords.latitude;
const proxy = 'https://cors-anywhere.herokuapp.com/';
const api = `${proxy}https://api.darksky.net/forecast/d571a1e2483b31605b94edaae84c647e/${lat},${long}`;
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
let obj = JSON.parse(this.responseText);
console.log(obj);
document.getElementById('temperature-degree').innerHTML = obj.currently.apparentTemperature;
document.getElementById('location').innerHTML = obj.timezone;
document.getElementById('temperature-description').innerHTML = obj.currently.summary;
setIcons(obj.currently.icon, document.getElementById('currentDayIcon'));
for (let i = 0; i < obj.hourly.data.length; i++) {
info.innerHTML += `<div id='hour${i + 1}' class='hourly'>` + `<canvas id='hourIcon${i + 1}'></canvas>` + `<h3 id='hourTemp${i + 1}'></h3>` + `<p id='hourSummary${i + 1}'></p>` + '</div>';
setIcons(obj.hourly.data[i].icon, document.getElementById(`hourIcon${i + 1}`));
document.getElementById(`hourTemp${i + 1}`).innerHTML = obj.hourly.data[i].temperature;
document.getElementById(`hourSummary${i + 1}`).innerHTML = obj.hourly.data[i].summary;
}
for (let i = 0; i < 5; i++) {
week.innerHTML += `<div id='day${i + 1}' class='daily'>` + `<canvas id='dayIcon${i + 1}'></canvas>` + `<h2 id='dayTemp${i + 1}'></h2>` + `<p id='daySummary${i + 1}'></p>` + '</div>';
setIcons(obj.daily.data[i].icon, document.getElementById(`dayIcon${i + 1}`));
document.getElementById(`dayTemp${i + 1}`).innerHTML = obj.daily.data[i].temperatureMax;
document.getElementById(`daySummary${i + 1}`).innerHTML = obj.daily.data[i].summary;
}
}
};
xhr.open("GET", api, true);
xhr.send();
});
}
function setIcons(icon, iconId) {
const skycons = new Skycons({color: 'white'});
const currentIcon = icon.replace(/-/g, '_').toUpperCase();
skycons.play();
return skycons.set(iconId, Skycons[currentIcon]);
}
;
});
...and the problem is that when it comes to the icons being loaded and displayed it's only the last one that actually displays. I don't know what the problem is because if it's successful for the last one, then it should be successful for the previous 47 since the last is the same iteration done for the 48th time. Any ideas?
I'm trying to create a proxy JS so every request to external resource will be proxied like XHR or any other resource.
so for XHR request i manage to do it with this code:
if(window.XMLHttpRequest)
{
var XMLHttpRequest = window.XMLHttpRequest;
var startTracing = function () {
XMLHttpRequest.prototype.uniqueID = function() {
// each XMLHttpRequest gets assigned a unique ID and memorizes it
// in the "uniqueIDMemo" property
if (!this.uniqueIDMemo) {
this.uniqueIDMemo = Math.floor(Math.random() * 1000);
}
return this.uniqueIDMemo;
}
// backup original "open" function reference
XMLHttpRequest.prototype.oldOpen = XMLHttpRequest.prototype.open;
var newOpen = function(method, url, async, user, password) {
//console.log(url);//new
if (url.includes("somesample")) {
//Debug Only
/*console.log("[" + this.uniqueID() + "] intercepted open (" +
method + " , " +
url + " , " +
async + " , " +
user + " , " +
password + ")");*/
urlParts = /^(?:\w+\:\/\/)?([^\/]+)(.*)$/.exec(url);
hostname = urlParts[1]; // www.example.com
path = urlParts[2]; // /path/to/somwhere
proxyprefix = "http://myproxy.com/xhr.php?url="
fullurl = "http://" + hostname + path;
rewrittenUrl = proxyprefix + btoa(encodeURIComponent(fullurl));
url = rewrittenUrl;
}
this.oldOpen(method, url, async, user, password);
}
XMLHttpRequest.prototype.open = newOpen;
// backup original "send" function reference
XMLHttpRequest.prototype.oldSend = XMLHttpRequest.prototype.send;
var newSend = function(a) {
//console.log("[" + this.uniqueID() + "] intercepted send (" + a + ")");
var xhr = this;
var onload = function() {
/*console.log("[" + xhr.uniqueID() + "] intercepted load: " +
xhr.status +
" " + xhr.responseText);*/
};
var onerror = function() {
/* console.log("[" + xhr.uniqueID() + "] intercepted error: " +
xhr.status);*/
};
xhr.addEventListener("load", onload, false);
xhr.addEventListener("error", onerror, false);
this.oldSend(a);
}
XMLHttpRequest.prototype.send = newSend;
}
startTracing();
}
else if (window.ActiveXObject) {
var ActualActiveXObject = ActiveXObject;
var ActiveXObject = function(progid) {
var ax = new ActualActiveXObject(progid);
if (progid.toLowerCase() == "msxml2.xmlhttp") {
var o = {
_ax: ax,
_status: "fake",
responseText: "",
responseXml: null,
readyState: 0,
status: 0,
statusText: 0,
onReadyStateChange: null
};
o._onReadyStateChange = function() {
var self = o;
return function() {
self.readyState = self._ax.readyState;
if (self.readyState == 4) {
self.responseText = self._ax.responseText;
self.responseXml = self._ax.responseXml;
self.status = self._ax.status;
self.statusText = self._ax.statusText;
}
if (self.onReadyStateChange) self.onReadyStateChange();
}
}();
o.open = function(bstrMethod, bstrUrl, varAsync, bstrUser, bstrPassword) {
/*console.log("intercepted open (" +
bstrMethod + " , " +
bstrUrl + " , " +
varAsync + " , " +
bstrUser + " , " +
bstrPassword + ")");*/
varAsync = (varAsync !== false);
this._ax.onReadyStateChange = this._onReadyStateChange
return this._ax.open(bstrMethod, bstrUrl, varAsync, bstrUser, bstrPassword);
};
o.send = function(varBody) {
return this._ax.send(varBody);
};
}
else
var o = ax;
return o;
}
}
and its working fine.
and because i have got some js that manipulate the dom and adds elements i need to intercept that to.
so i started by rewriting appendChild with that code:
window.callbackFunc = function(elem, args) {
//console.log(elem);
for (var key in elem) {
if (elem.hasOwnProperty(key) && elem[key].src)
{
elem[key].src = "http://myptoxy/proxy.php?u=" +encodeURIComponent(elem[key].src);
}
}
}
window.f = Element.prototype.appendChild;
Element.prototype.appendChild = function() {
window.callbackFunc.call(this, arguments);
return window.f.apply(this, arguments);
};
and thats also working well but i encounter a problem with createlement.
some js code on my site creates an iframe with src that i'm unable to intercept with this code:
document.createElement = function(create) {
return function() {
var ret = create.apply(this, arguments);
//console.log(ret);
for (var key in ret) {
if (ret.hasOwnProperty(key) && ret[key].src) {
ret[key].src = "http://myproxy.com/proxy.php?u=" + encodeURIComponent(elem[key].src);
}
}
return ret;
};
}(document.createElement)
I mean I can intercept the creation but the attributes are empty obviously the script adds the attribute after the element creation)
I tried to use MutationObserver but with no luck...
Any help?
Another approach to the entire problem will be great!
I am writing a REST client to remote api. And I am using xmlHTTPRequest to get information about images.I need to refresh my images in every 30 seconds. My implementation of setTimeout function doesn't work. Anyone can help me? Thank you in advance.
Here is my code: Image.js
function Camera(id, name, ip, port) {
var button = document.createElement("button");
button.classList.add("camera");
button.innerHTML += "<h3>" + name + "</h3><br>";
var ismin = true;
this.id = id;
this.name = name;
this.ip = ip;
this.port = port;
this.getURL = function getURL(min) {
var url = 'http://' + ip + ":8080/api";
return min ? url + '/miniature/' + id + '?t=' + new Date().getTime() : url + '/image/' + id + '?t=' + new Date().getTime();
};
this.appendImg = function appendImg(url) {
button.innerHTML = "<h3>" + name + '</h3><br><img src="' + url + '"/>';
setTimeout(appendImg(url),30000);
};
this.showElement = function showElement(url) {
this.appendImg(url);
var that = this;
document.querySelector('#camera-section').appendChild(button);
button.addEventListener('click', function () {
ismin = !ismin;
that.appendImg(that.getURL(ismin), false);
});
};}
And a part of main.js:
function showImage(response) {
response = JSON.parse(sessionStorage.getItem('camera'));
console.log(response);
for (var i = 0; i < response.length; i++) {
var a = response[i];
var camera = new Camera(a.cameraId, a.name, ip, port, true);
var curl = camera.getURL(true);
camera.showElement(curl);
}
}
xml.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var response = JSON.parse(this.responseText);
sessionStorage.setItem('camera',JSON.stringify(response));
//console.log(sessionStorage.getItem('camera'));
showImage(sessionStorage.getItem('camera'));
}
};
xml.open('GET', mainUrl);
xml.send(null);
Regarding the comment of Pranay Kumar, first part of your code could be like this::
function Camera(id, name, ip, port) {
var button = document.createElement("button");
button.classList.add("camera");
button.innerHTML += "<h3>" + name + "</h3><br>";
var ismin = true;
this.id = id;
this.name = name;
this.ip = ip;
this.port = port;
this.getURL = function getURL(min) {
var url = 'http://' + ip + ":8080/api";
return min ? url + '/miniature/' + id + '?t=' + new Date().getTime() : url + '/image/' + id + '?t=' + new Date().getTime();
};
this._appendImg = function(url) {
return function() {
button.innerHTML = "<h3>" + name + '</h3><br><img src="' + url + '"/>';
}
};
this._timerHandle = 0;
this.appendImg = function(url) {
if (this._timerHandle) {
clearInterval(this._timerHandle);
}
this._timerHandle = setInterval(this._appendImg(url),30000);
}
this.showElement = function showElement(url) {
this.appendImg(url);
var that = this;
document.querySelector('#camera-section').appendChild(button);
button.addEventListener('click', function () {
ismin = !ismin;
that.appendImg(that.getURL(ismin), false);
});
}
}
You want refresh image every 30 seconds.
So use setInterval instead of setTimeout
I tried to run this but it doesn't work.
It is intended to return a variable assigned inside a function, that was passed as callback to sendRequest(), which is retrieving data from the Internet through XMLHttpRequest asynchronously.
Can anyone tell me why this is not working and always returning ""?
function sendRequest(requestCode, args, callback){
var req = requestEngineUrl + "?req=" + requestCode + ";" + args;
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function(){
if(xmlHttp.readyState == 4)
{
if(callback != null){
callback(xmlHttp.responseText);
}
}
};
xmlHttp.open("GET", req, true);
xmlHttp.send(null);
}
this.assembleProcess = function(){
if(!isNull(this.id) && !isNull(this.titles)){
var titles = this.titles;
var id = this.id;
c = "";
sendRequest('304', id,
function(result){
var res = result.split("/");
var title = res[0];
var possibilities = res[1];
var fcontent = title + '<br><div>';
if(titles.length != possibilities){
console.log("WARNING: [SURVEYCARD].titles has not the same length as possibilities");
}
for(i = 0; i < possibilities; i++){
fcontent += '<div><a onclick="sendRequest("301",' + id + ',' + i + ',null)">' + titles[i] + '</a></div>';
}
fcontent += '</div>';
c = fcontent;
});
return c;
}
As an XMLHttpRequest is async, you should write an async function for that matter, like this
this.assembleProcess = function(callback){
if(!isNull(this.id) && !isNull(this.titles)){
var titles = this.titles;
var id = this.id;
c = "";
sendRequest('304', id,
function(result){
var res = result.split("/");
var title = res[0];
var possibilities = res[1];
var fcontent = title + '<br><div>';
if(titles.length != possibilities){
console.log("WARNING: [SURVEYCARD].titles has not the same length as possibilities");
}
for(i = 0; i < possibilities; i++){
fcontent += '<div><a onclick="sendRequest("301",' + id + ',' + i + ',null)">' + titles[i] + '</a></div>';
}
fcontent += '</div>';
c = fcontent;
callback(c)
});
}
and then, instead of using this.assembleProcess as a function with a result, you should pass a function as parameter:
Instead of
console.log(this.assembleProcess);
do this
this.assembleProcess(function(c){console.log(c)});