I am trying to push weather data into Google Analytics. (I am following a tutorial online; I am not proficient in Javascript but I know Python.) I have set up datalayer variables, tags and triggers, but I have a custom HTML tag that calls Openweathermap API, and pushes all this data to the datalayer, so my tag can then take this information and fire back to Google Analytics.
Could someone please have a look at this code and tell me why I get a "Unexpected Token on Line 28 ({)" error?
<script>
(function() {
var fetchWeatherData = function(longitude, latitude) {
// Open Weather Map
var owmAppKey = '<baeb0853a54bef1870ecdd0345bb0f5e>';
jQuery.getJSON('https://api.openweathermap.org/data/2.5/weather?lat=' + latitude + '&lon=' + longitude + '&units=metric&APPID=' + owmAppKey)
.done(function(data) {
window.dataLayer.push({
event: 'weatherDone',
weather: data.weather[0].main,
temperature: data.main.temp.toFixed(0) + ' °C'
});
}).fail(function(jq, status, msg) {
console.log('Weather request failed: ' + status + ' - ' + msg);
});
};
var geoLocateUser = function() {
$.getJSON('//extreme-ip-lookup.com/json/')
.done(function(data) {
fetchWeatherData(data.lon, data.lat);
}).fail(function(status, msg) {
console.log('IP request failed: ' + status + ' - ' + msg);
});
};
if (typeof {{Session alive}} === 'undefined') {
geoLocateUser();
}
// Reset "session" cookie with a 30-minute expiration
var d = new Date();
d.setTime(d.getTime()+1800000);
var expires = "expires="+d.toGMTString();
document.cookie = "session=1; "+expires+"; path=/";
})();
</script>
I am guessing this is a really basic Syntax error that is easy to fix, but I am not proficient with Javascript and cannot figure this out.
Many thanks!
A few things:
You're getting an error because typeof {{...}} is improper syntax.
Also, Session alive isn't anything. If it's a variable it should be one word like Session_alive or Session-alive or sessionAlive.
Also, double curly braces {{...}}(moustache template) are usually used in some JS frameworks but are not a feature of vanilla JS.
Single curly braces indicate an object (ex: {a: 'one', b: 'two'}.
If sessionAlive was a variable of some kind and you wished to verify if it was an object you'd write typeof sessionAlive.
But if you're checking to see if the value Session is alive then you'd write a conditional such as if (Session === 'alive') ...,
or check if Session is undefined such as if (Session === undefined) ...
Can you check the "Session alive" tag is properly set up in Google Tag Manager?
Related
var val = 3;
var code = "var a = 5; if (a >= val) { console.log(a + ' >= ' + val); a; } else { console.log(a + ' < 3 ' + val); val; }";
console.log(eval(code));
This is the scenario where an alternative to eval() is required.
The Server can send any kind of JS code which could be run on a particular block.
Do not use eval(code) or new Function(code) as both are basically the same thing and should be blocked by CSP.
Just return your content from the server as content-type: text/javascript then get it into your page with a <script> block or import.
On the server you would have something like (pseudo code, as I don't know what tech stack you're on):
[Route("serverActionReturningCode")]
public string ActionReturningCode()
{
// return the content as JS
Response.Headers.Add("content-type", "text/javascript");
// build the response object as JS
return "window.latestResult = {" +
"a: '" + a + "', " +
"b: '" + b + "', " +
"generatedCode: function() { ... }" +
"};";
}
Then in your page:
<script src="serverActionReturningCode"></script>
<script>
// Now the script above has run and set window.latestResult
console.log('a', window.latestResult.a);
console.log('b', window.latestResult.b);
console.log('function output', window.latestResult.generatedCode());
</script>
This will let you dynamically generate JS functions on the server.
However, if you can avoid the functions and just need to pass values it is a lot simpler to use JSON instead.
It seems to be like there is no way other than to live with eval or change the entire design of the application. Even if we look for any other alternatives, it's going to be the change in the name and syntax. But the security issues are going to be the same. Its the design of the application that JS CodeGen tool in the server will generate JS code snippets and send it via JSON in certain fields which has to be picked and executed in the front-end. But in this design, we can assure one thing that the JS code is generated only at the design time of the user and not at the runtime.
Thanks for your help.
You can do it like this. Using Eval() is not recommended.
function looseJsonParse(obj){
return Function('"use strict";return (' + obj + ')')();
}
console.log(looseJsonParse(
"{a:(4-1), b:function(){}, c:new Date()}"
))
Refer this MDN article https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
to dig more into it.
EDIT: The following was not my true issue. See answer.
I'm working on a simple html/javascript chat client using Google firebase. The following code is intended to be a rudimentary system of registering and logging in users, wherein the function is provided with an array ($usr) containing a username and password at the 1 and 2 positions.
The local username and password $usr[1-2] are then checked against the database's result (getuser variable, structured as user obj) to determine whether or not a username has already been taken or if the user's credentials are valid. Please note that this is a personal project, the data is not intended to be sensitive.
//Registers user credentials with input [cmd, user, pass]
var auth = function ($usr) {
var db = firebase.database().ref('chatapp/users/' + $usr[1]);
var getuser;
user = {'name': $usr[1], 'pass': $usr[2]};
db.once('value').then(function(snapshot) {
getuser = snapshot.val();
if ($usr[0] === "/register") {
if (getuser.name !== $usr[1]) {
db.set(user);
notify('Registered ' + $usr[1] + ' with pass "' + $usr[2] + '"');
} else {
notify('Username "' + $usr[1] + '" already taken, try again');
}
} else if ($usr[0] === "/login") {
if (getuser.name !== $usr[1] || getuser.pass !== $usr[2]) {
notify('Invalid credentials ' + $usr[1] + ':' + $usr[2]);
} else {
notify($usr[1] + ' logged in');
}
}
});
};
The issue comes into play at db.once(). The data is being retrieved but is delayed. If a user is attempting to register (getuser.name !== $usr1) will always return True because the variable is set to undefined. However, the login command works flawlessly because by then getuser has been set to the value retrieved from Firebase.
I have tried using .once() only to set the variable, or as a function returning snapshot.val(). I have tried including all of my code within the callback for .once() and using snapshot.val()[name] and [pass] rather than storing it to a variable. The only solution to this seems to be a manual break in the program flow.
In addition, I've also found that using getuser[name] does not work in instances where getuser.name does work, which makes no sense and further infuriates me. Please help, it's 2:12am here.
Here is the official documentation.
Here is a relevant Stackoverflow question, which may be the solution I'm looking for but I don't understand it.
What really confounds me is that the function following .then is supposedly reliant on the data being confirmed, which obviously isn't the case.
This is the code that worked for me:
//Registers user credentials with input [cmd, user, pass]
var auth = function ($usr) {
var db = firebase.database().ref('chatapp/users/' + $usr[1]);
var getuser;
user = {'name': $usr[1], 'pass': $usr[2]};
db.once('value').then(function(snapshot) {
getuser = snapshot.val();
if ($usr[0] === "/register") {
if (getuser === null) {
db.set(user);
notify('Registered ' + $usr[1] + ' with pass "' + $usr[2] + '"');
} else {
notify('Username "' + $usr[1] + '" already taken, try again');
}
} else if ($usr[0] === "/login") {
if (getuser.name !== $usr[1] || getuser.pass !== $usr[2]) {
notify('Invalid credentials ' + $usr[1] + ':' + $usr[2]);
} else {
notify($usr[1] + ' logged in');
}
}
});
};
The question was not actually my issue. What led me to believing that retrieval of the data was being delayed is that my code had initially been outside of the .once() method's callback function. If you use .once()'s callback function to write snapshot.val() to a variable, and then write the rest of your code outside of the callback function, the data is written asynchronously and your variable will be set to undefined until the true value can be retrieved from the server. I believe that a workaround for this is calling a function with the parameter snapshot.val() from within the callback, or simply writing your code within the callback (above).
The actual issue with my code was with the line if (getuser.name !== $usr[1]). This causes an error if the value of getuser is null. The fixed code is above.
Trying to create a firefox addon that accesses the browser cookies. Following googled tutorials I've written the following function but looks like the Services.jsm is not accessible?
Components.utils.import("resource://gre/modules/Services.jsm");
var myExtension = {
myListener: function(evt) {
//I get here
alert("Received from web page: " +
evt.target.getAttribute("attribute1") + "/" +
evt.target.getAttribute("attribute2"));
//I dont see anything dumped
let enum = Services.cookies.getCookiesFromHost("example.com");
while (enum.hasMoreElements()) {
var cookie = e.getNext().QueryInterface(Ci.nsICookie2);
dump(cookie.host + ";" + cookie.name + "=" + cookie.value + "\n");
}
}
}
document.addEventListener("MyExtensionEvent", function(e) { myExtension.myListener(e); }, false, true);
Thanks #Shakur I didn't catch that e and yep you're right it needs to be fixed to enum.
I'm not familiar with cookie service, I would have to read up on it but you're on right track.
This is because you have not defined Ci you use Ci in the while loop. You can replace Ci with Components.interfaces and it should fix it up. :) If you want to use the C* short forms it is typically done by adding to the top: const {Cc:classes, Cu:utils, Ci:interfaces, Cr:results, CC: Constructor} = Components as seen in this example here: https://codereview.stackexchange.com/questions/56821/improvements-to-nsizipreader-and-nsiscriptableinputstream/56824#56824
I am not sure how correct I am on this so if any experts are able to correct me on this it would also be appreciated. My current understanding is that observables are lazy and do not produce values until subscribed to. If an error occurs the observable sends no more values. In a lot of cases this is not what is wanted.
In the code sample below I am getting the weather for perth and london and if an error occurs returning an object indicating the error has occured. This pretty much means that the error block on the subscriber will not get called but the success will and I will have to look at whether it failed and change logic. Is this the best way to do this????
Also does the zip operator wait for all obs to produce a value regardless of ordering and return when a single value from all has been produced?
Code as below:
window.application = application || {};
window.application.stocks = (function(){
function getWeather(city, country){
return $.ajaxAsObservable({
url: 'http://api.openweathermap.org/data/2.5/weather?q=' + city + ',' + country,
dataType: 'jsonp'
}).map(function(v){
return {
city : city,
country : country,
temperature : v.data.main.temp
};
});
}
var requests = Rx.Observable
.interval(500)
.timestamp()
.map(function(v){
var perth = getWeather('perth','au');
var london = getWeather('london','uk');
return Rx.Observable.zip(perth,london, function(a,b){
return {
a : a,
b : b,
timestamp : v.timestamp,
failure : false,
};
}).retry(3)
.catch(Rx.Observable.return({ failure: true }));
})
.concatAll();
var selector = $('#weather');
var subscriber = requests.forEach(
function(data){
if(data.failure){
$('<li>Failure in receiving the temperature</li>').appendTo(selector);
return;
}
$('<li> at: ' + data.timestamp + ' for: ' + data.a.city + ' - ' + data.a.temperature + '</li>').appendTo(selector);
$('<li> at: ' + data.timestamp + ' for: ' + data.b.city + ' - ' + data.b.temperature + '</li>').appendTo(selector);
},
function(error){
selector.text('');
$('<li>Error: ' + error + '</li>').appendTo('#weather');
},
function(){
}
);
});
Any suggestions and tips is greatly appreciated.
Thanks in advance.
Blair.
You probably should replace .forEach() with .subscribe() since one is an alias of the other, and .subscribe() is more common and more explicit in this case.
subscribe() can accept three functions as arguments onNext (first function), onError (second function, optional), onComplete (third function, optional). Hence, you probably will achieve what you want by just providing a second function in the subscribe(), to handle errors. Then you don't need the .catch() operator to insert a flag-like event.
Also, for your specific use case, where perth and london Observables are conceptually independent (they have no dependencies with one another), you want to use .combineLatest() instead of .zip(). See this answer for details on the difference between the two.
Interesting as this post says that you do need to flag errors so that the steam does not complete.
Idiomatic way to recover from stream onError
From my current understanding and testing of RX if an error is thrown during process the onError handler of the subscription will get called but it will complete the stream.
Meaning that the subscriber will receive no further messages.
While attempting to retrieve whether a steam game is multiplayer or not using the Steam API, we ran into some confusing issues.
<script>
var key = atob("MjEyNzlCNzdFNjFCOTI3MEQ1MDVEODEyNDYzRDFDOTA=");
var app = {};
function getGameName(AppID) {
var gameURL = 'http://store.steampowered.com/api/appdetails/?appids=' + AppID;
var yql = 'https://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from json where url="' + gameURL + '"') + '&format=json&callback=?';
$.getJSON(yql, function(data) {
var _x1 = String(AppID);
_x1 = _x1.substring(1);
_x1 = "_" + _x1;
if (data.query.results[_x1].data.catergories[0].id == '1') {
app.gameName[AppID] = data.query.results[_x1].data.name;
}
});
}
</script>
To run the code run in console getGameName(40). This should return the name of the game, as it is multiplayer, however is comes up with an uncaught type error.
This is very confusing when line 13 (app.gameName[AppID]...) works, yet line 12 (if (...) does not work.
I'm just wondering if anyone could point me in the right direction? Thanks in advanced.
As requested, the exact error message is "Uncaught Type Error: Cannot read property '0' of undefined"
And the JSON that should be returned can be found here although, since is has gone through the yahoo API it has some padding.