I am trying to implement GTM and new Google analytics web + app and I can see its taking 5sec to push data to google analytics.
I have tested with a fresh site and simple tracking event on click in link.
https://drive.google.com/file/d/1ILJLROq05OQwx3oXXCrOVXDeoxtbdKVw/view?usp=sharing
document.addEventListener('click', function (event) {
// If the clicked element doesn't have the right selector, bail
if (!event.target.matches('.btn-GA')) return;
// Don't follow the link
event.preventDefault();
// Log the clicked element in the console
console.log('Click......');
dataLayer.push({
"event": "wa_trigger",
"wa_event": "bounce_out",
"wa_action": "bounce_action",
"wa_label": "bounce_label"
});
}, false);
<a class="btn-GA" href="#">TEST CLICK</a>
================================================
UPDATE::::
Thanks for your answer. I noticed its really fast if I set true for the GA debug https://chrome.google.com/webstore/detail/google-analytics-debugger/jnkmfdileelhofjcijamephohjechhna and the hit comes fast and first others hit.. but when its off the hit comes at then end after 5sec.
And here screenshot from console:
https://drive.google.com/file/d/1a4n1WldESjbwTYkwiQ9PPX9RijigDApt/view?usp=sharing
Here the push and hit
Here push =====>
a.js:6 Google - dataLayer.push(): event: wa_trigger
a.js:6 ~ Object:
{
event: "wa_trigger",
wa_event: "logo-click"
}
Currently in memory: {event: "wa_trigger", wa_event: "logo-click", eventCallback: ƒ}
a.js:6 ~ Call Stack
a.js:6 ~ 20.140 s since Document Start
And here the hit: ====>
~ Data: {
events: [
{
en: "-logo-click",
_et: "2258",
ep.transport: "beacon"
},
{
en: "page_view",
_et: "36",
ep.transport: "beacon"
}
],
v: "2",
tid: "G-xxxxxx",
gtm: "xxxx",
_p: "109194814",
sr: "1440x900",
ul: "sv-se",
cid: "2129079382.1587074095",
dl: "http://www.site.lo/",
dr: "",
dt: "Site title",
sid: "1587312754",
sct: "5",
seg: "1",
_s: "2",
statusCode: 204
}
~ Network Hit: {frameId: 0, fromCache: false, initiator: "http://www.site.lo", ip: "216.58.207.206", method: "POST", …}
dlc.js:1208 ~ 26.208s since Document Start
dlc.js:1208 ~ 4.316s since last hit
It looks to be "by design":
One thing you might have noticed is the delay it takes for the hit to
be sent. When you load the page, you can see how the browser waits a
few seconds before dispatching the hit to GA. This is because GAv2
batches requests automatically, which, again, is a great feature
upgrade.
One thing you might have noticed is the delay it takes for the hit to
be sent. When you load the page, you can see how the browser waits a
few seconds before dispatching the hit to GA. This is because GAv2
batches requests automatically, which, again, is a great feature
upgrade.
By batching multiple hits into a single request, browser resources are
saved for other things. The delay is due to the browser waiting to see
if other hits should be dispatched together with the Page View.
https://www.simoahava.com/analytics/getting-started-with-google-analytics-app-web/
Be confident that the hit will fire before the user leaves the page !
Ok, I did my best to fix the issue and I used fetch to send the data directly to google server and skip the dataLayer and by using Callback function and a bit delay 1sec to wait for GA to be loaded.
Here how I did it and I am not sure if this is the best way or not but its working 100% and I got exact data on both old and old data.
BounceOut is the old data GA1
Bounce_out and Bounce_out_source is the new one GA2
trackAndBounceOut(url, callback) {
var action = this.state.bouncer.meta.partnerName
var label = this.state.bouncerId
var type = Session.page
window.dataLayer = window.dataLayer || []
var checkGA
setTimeout(function () {
if (ga && typeof ga.getAll == 'function') {
checkGA = true
} else {
checkGA = false
}
if (checkGA) {
var clientId = ga.getAll()[0].get('clientId');
var adSenseId = ga.getAll()[0].get('adSenseId')
var screenResolution = ga.getAll()[0].get('screenResolution')
var userLanguage = ga.getAll()[0].get('language')
var docReferrer = ga.getAll()[0].get('referrer')
var docLocation = ga.getAll()[0].get('location')
var debug = false // Enable for debug!!
var doctTitle = document.title
var stringUrlData = {
v: 2,
tid: 'G-XXXXXXXXXX',
gtm: 'XXXXXX',
_p: adSenseId,
sr: screenResolution,
ul: userLanguage,
cid: clientId,
dl: docLocation,
dr: docReferrer,
dt: doctTitle,
_dbg: debug ? 1 : '',
}
var urlString = Object.keys(stringUrlData)
.filter(analyticsKey => stringUrlData[analyticsKey])
.map(analyticsKey => analyticsKey + '=' + encodeURIComponent(stringUrlData[analyticsKey]))
.join('&')
var bounce_out = 'en=Bounce_Out&ep.Action=' + action
var bounce_out_source = 'en=Bounce_Out_source&ep.Action=' + type + '_' + action + '&ep.Label=' + type
fetch('https://www.google-analytics.com/g/collect?' + urlString + '&' + bounce_out, { method: 'POST' })
fetch('https://www.google-analytics.com/g/collect?' + urlString + '&' + bounce_out_source, { method: 'POST' })
if (Session.page == 'Partner') {
window.dataLayer.push(
{ 'event': 'pageEvent', 'Category': 'Partner page', 'action': 'Bounce Out', 'label': Session.page + '_' + action },
)
var partner_bounce_out = 'en=Partner_bounce-out&ep.Action=' + action
fetch('https://www.google-analytics.com/g/collect?' + urlString + '&' + partner_bounce_out, { method: 'POST' })
}
}
callback(action, type, label, url)
}.bind(this), 1000);
}
trackAndBounceOutRedirect(action, type, label, url) {
window.dataLayer.push(
{
'event': 'bounceOut',
'action': action,
'label': type + '_' + label,
'eventCallback': () => {
window.location.href = url
},
'eventTimeout': 1000
}
)
}
// And here then we call the main function and send the callback one:
this.trackAndBounceOut(this.state.bouncer.url, this.trackAndBounceOutRedirect)
Related
I'll try to be as short as possible since there's no need for code - I put an iframe tag in my Electron app and I want JavaScript to retrieve some information from it but due to same-origin policy, I can't do that. Can I disable it? Note: I am the owner of the page in the iframe.
var validate = document.getElementById('validate');
validate.onclick = function() {
var input = document.getElementById('serial').value;
var validator = document.createElement('iframe');
validator.src = "http://**********.com/?wvkey=" + input;
body.appendChild(validator);
var status = validator.contentWindow.document.getElementById('status').innerHTML;
if (status === "Key Status: Active") {
window.location.assign("../dashboard/index.html");
}
else {
window.location.assign("../support/keys/invalid/index.html");
}
}
When clicking a button on the page, the code is supposed to check for a text with the value of "Key Status: Active" in the iframe. If the condition is true, it should redirect the user to another page. If false, it should redirect him to an error page. In my app, however, the iframe just appears in the background and doesn't do anything - no redirects.
main.js (part of it):
win = new BrowserWindow({
height: 700,
resizable: false,
show: false,
webPreferences: {
nodeIntegration: true,
webSecurity: false
},
width: 790
})
EDIT (Updated)
var validate = document.getElementById('validate');
validate.onclick = function() {
var input = document.getElementById('serial').value;
var validator = document.createElement('iframe');
validator.onload = function() {
var status = validator.contentWindow.document.getElementById('status').innerHTML;
if (status === "Key Status: Active") {
window.location.assign("../dashboard/index.html");
}
else {
window.location.assign("../support/keys/invalid/index.html");
}
};
validator.src = "http://awebsite.com/?wvkey=" + input;
body.appendChild(validator);
}
Since your question is how to disable security features like CORS in an Electron window, here is the setting you can use:
win = new BrowserWindow({
webPreferences: {
webSecurity: false
}
});
I'd however only use this as a last resort and rather have your website send the appropriate CORS headers.
* Edit *
After seeing your source code I believe there is an issue with your code:
var validator = document.createElement('iframe');
validator.src = "http://**********.com/?wvkey=" + input;
This will load your website into the <iframe> object however this happens asynchronously. Means you need to wait for the page to finish loading before you can continue with:
var status = validator.contentWindow.document.getElementById('status').innerHTML;
Change your code to something like this:
var validator = document.createElement('iframe');
validator.onload = function() {
var status = validator.contentWindow.document.getElementById('status').innerHTML;
[...]
};
validator.src = "http://**********.com/?wvkey=" + input;
body.appendChild(validator);
Note that you will still need the webSecurity or CORS settings to be able to access the validator frame content.
I am moving from Leaflet to Mapbox GL and have some data issues. My webApi is proven but I cannot smoothly integrate them.
The approach I gave up on, based upon their examples and my own research, looks like:
map = new mapboxgl.Map({
container: 'mapdiv',
style: 'mapbox://styles/mapbox/streets-v10'
, center: start
, zoom: $scope.zoom
, transformRequest: (url, resourceType) => {
if (resourceType === 'Source' && url.startsWith(CONFIG.API_URL)) {
return {
headers: {
'Authorization': 'Bearer ' + localStorageService.get("authorizationData")
, 'Access-Control-Allow-Origin': CONFIG.APP_URL
, 'Access-Control-Allow-Credentials': 'true'
}
}
}
}
});
This is passing my OAuth2 token (or at least I think it should be) and the Cross site scripting part CORS.
Accompanying the above with:
map.addSource(layerName, { type: 'geojson', url: getLayerURL($scope.remLayers[i]) });
map.getSource(layerName).setData(getLayerURL($scope.remLayers[i]));
Having also tried to no avail:
map.addSource(layerName, { "type": 'geojson', "data": { "type": "FeatureCollection", "features": [] }});
map.getSource(layerName).setData(getLayerURL($scope.remLayers[i]));
Although there are no errors Fiddler does not show any requests being made to my layer webApi. All the others show but Mapbox does not appear to raising them.
The Url looks like:
http://localhost:49198/api/layer/?bbox=36.686654090881355,34.72821077223763,36.74072742462159,34.73664000652042&dtype=l&id=cf0e1df7-9510-4d03-9319-d4a1a7d6646d&sessionId=9a7d7daf-76fc-4dd8-af4f-b55d341e60e4
Because this was not working I attempted to make it more manual using my existing $http calls which partially works.
map = new mapboxgl.Map({
container: 'mapdiv',
style: 'mapbox://styles/mapbox/streets-v10'
, center: start
, zoom: $scope.zoom
, transformRequest: (url, resourceType) => {
if (resourceType === 'Source' && url.startsWith(CONFIG.API_URL)) {
return {
headers: {
'Authorization': 'Bearer ' + localStorageService.get("authorizationData")
}
}
}
}
});
map.addSource(layerName,
{
"type": 'geojson',
"data": { "type": "FeatureCollection", "features": [] }
});
The tricky part is to know when to run the data retrieval call. The only place I could find was on the maps data event which now looks like:
map.on('data', function (e) {
if (e.dataType === 'source' && e.isSourceLoaded === false && e.tile === undefined) {
// See if the datasource is known
for (var i = 0; i < $scope.remLayers.length; i++) {
if (e.sourceId === $scope.remLayers[i].name) {
askForData(i)
}
}
}
});
function askForData(i) {
var data = getBBoxString(map);
var mapZoomLevel = map.getZoom();
if (checkZoom(mapZoomLevel, $scope.remLayers[i].minZoom, $scope.remLayers[i].maxZoom)) {
mapWebSvr.getData({
bbox: data, dtype: 0, id: $scope.remLayers[i].id, buffer: $scope.remLayers[i].isBuffer, sessionId
},
function (data, indexValue, indexType) {
showNewData(data, indexValue, indexType);
},
function () {
// Not done yet.
},
i,
0
);
}
}
function showNewData(ajxresponse, index, indexType) {
map.getSource($scope.remLayers[index].name).setData(ajxresponse);
map.getSource($scope.remLayers[index].name).isSourceLoaded = true;
}
This is all working with one exception. It keeps firing time and time again. Some of these calls return a lot of data for a web call so its not a solution at the moment.
Its like its never satisfied with the data even though its showing it on the map!
There is a parameter on the data event, isSourceLoaded but it does not get set to true.
I have searched for an example, have tried setting isSourceLoaded in a number of places (as with the code above) but to no avail.
Does anyone have a method accomplishing this basic data retrieval function successfully or can point out the error(s) in my code? Or even point me to a working example...
I have spent too long on this now and could do with some help.
After a bit of a run around I have a solution.
A Mapbox email pointed to populating the data in the load event - which I am now doing.
This was not however the solution I was looking for as the data needs refreshing when the map moves, zooms etc - further look ups are required.
Following a bit more a examination a solution was found.
Using the code blow on the render event will request the information when the bounding box is changed.
var renderStaticBounds = getBoundsString(map.getBounds());
map.on('render', function (e) {
if (renderStaticBounds != getBoundsString(map.getBounds())) {
renderStaticBounds = getBoundsString(map.getBounds());
for (var i = 0; i < $scope.remLayers.length; i++) {
askForData(i);
}
}
});
function getBoundsString(mapBounds) {
var left = mapBounds._sw.lng;
var bottom = mapBounds._sw.lat;
var right = mapBounds._ne.lng;
var top = mapBounds._ne.lat;
return left + ',' + bottom + ',' + right + ',' + top;
}
This hopefully will save someone some development time.
I'm not sure if it's because I'm testing in the alexa developer console but it appears the session is restarted after every intent.
In the below code, if I invoke SetMyVarA it will write out the correct value to cloudwatch (or the terminal when using serverless), but if I then invoke SetMyVarB immediately after then I'll get "Hmm, I don't know that one" (running locally will just give me undefined for the value).
I've also tried following the advice in this question but it didn't seem to have an effect: adding alexa skill session attributes from nodejs
/*jslint es6 */
"use strict";
const Alexa = require(`alexa-sdk`);
module.exports.handler = (event, context, callback) => {
console.log(`handler: ${JSON.stringify(event.request)}`);
const alexa = Alexa.handler(event, context, callback);
alexa.appId = process.env.APP_ID;
alexa.registerHandlers(handlers);
alexa.execute();
};
const handlers = {
"LaunchRequest": function() {
console.log(`LaunchRequest`);
this.emit(`AMAZON.HelpIntent`);
},
"SetMyVarA": function() {
console.log(`Set MyVarA`);
var myVarA = this.event.session.attributes.myVarA = this.event.request.intent.slots.myVarA.value;
console.log(`MyVarA is ${myVarA}.`);
var speechOutput = `MyVarA has been set to ` + myVarA + `.`;
this.response.speak(speechOutput);
this.emit(`:responseReady`);
},
"SetMyVarB": function() {
console.log(`Set MyVarB`);
var myVarB = this.event.session.attributes.myVarB = this.event.request.intent.slots.myVarB.value;
console.log(`MyVarB is ${myVarB}.`);
var myVarA = this.event.session.attributes.myVarA
console.log(`MyVarA is ${myVarA}.`);
var speechOutput = {
"type": `SSML`,
"ssml": `<speak>MyVarB is ` + myVarB + `.</speak>`,
};
this.response.speak(speechOutput);
this.emit(`:responseReady`);
},
"AMAZON.HelpIntent": function() {
var speechOutput = `This is a skill.`;
var reprompt = `Help here.`;
speechOutput = speechOutput + reprompt;
this.response.speak(speechOutput)
.listen(reprompt);
this.emit(`:responseReady`);
},
"AMAZON.CancelIntent": function() {
},
"AMAZON.StopIntent": function() {
},
"AMAZON.RepeatIntent": function() {
console.log(`RepeatIntent`);
this.emit(`LaunchRequest`);
},
"Unhandled": function() {
// handle any intent in interaction model with no handler code
console.log(`Unhandled`);
this.emit(`LaunchRequest`);
},
"SessionEndedRequest": function() {
// "exit", timeout or error. Cannot send back a response
console.log(`Session ended: ${this.event.request.reason}`);
},
};
In SetMyVar if you use speak() and then emit a responseReady the session gets closed by default, so you're already out of it when you try to call your 2nd intent.
If you want to do exactly the same thing you're doing in SetMyVarA but not close the session immediately you need to set this.response.shouldEndSession to false. The Alexa Dev Console can handle skill sessions with no problems, so don't worry about that.
By the way, you're using ASK v1 which is outdated. Please switch to v2 code like this:
https://developer.amazon.com/blogs/alexa/post/decb3931-2c81-497d-85e4-8fbb5ffb1114/now-available-version-2-of-the-ask-software-development-kit-for-node-js
https://ask-sdk-for-nodejs.readthedocs.io/en/latest/ASK-SDK-Migration-Guide.html
I would like to open and close MDL toast rather than use the timeout property as indicated in the MDL usage guide. The reason is that I want the toast to remain while geolocation is occuring, which sometimes takes 10+ seconds and other times happens in 1 second.
Any idea how this could be done?
A q&d solution i found, invoke cleanup_ method on the sb object.
With this solution i can show the sb, click action handler to hide it, then re trigger the action to show it without any problem.
var snackbar = form.querySelector("[class*='snackbar']");
if (snackbar) {
var data = {
message: 'Wrong username or password',
timeout: 20000,
actionHandler: function(ev){
// snackbar.classList.remove("mdl-snackbar--active")
snackbar.MaterialSnackbar.cleanup_()
},
actionText: 'Ok'
};
snackbar.MaterialSnackbar.showSnackbar(data);
}
As cleanup_ is not part of the public api, i guess it worth to enclose this with some small checks to avoid a disaster.
snackbar.MaterialSnackbar.cleanup_
&& snackbar.MaterialSnackbar.cleanup_()
!snackbar.MaterialSnackbar.cleanup_
&& snackbar.classList.remove("mdl-snackbar--active")
Got it working as so: I basically set a 30 second timeout on the toast assuming my geolocation and georesults (GeoFire) will take no more than 30 seconds.
I get the length of the returned array of map markers and multiply that by the javascript timeout events. I finally remove mdl-snackbar--active which hides the toast. So, basically - it works.
UPDATED
The above actually had a major problem in that additional toasts would not display until that long timeout completed. I could not figure out how to apply the clearTimeout() method to fix it so I found a solution that works - trigger the toast up and down by just toggling the mdl-snackbar--active class - no timer setting necessary.
So to call toast as normal using this code, simply tools.toast('hello world',error,3000). To programatically open and close toast call tools.toastUp('hey') and tools.toastDown(), respectively. So, you might call tools.toastDown after a promise resolves or something...
var config = (function() {
return {
timeout: 50, //in milliseconds
radius: 96, //in kilometers
};
})();
var tools = (function() {
return {
toast: function(msg,obj,timeout){
var snackbarContainer = document.querySelector('#toast'); //toast div
if(!obj){obj = ''}
if(!timeout){timeout = 2750}
data = {
message: msg + obj,
timeout: timeout
};
snackbarContainer.MaterialSnackbar.showSnackbar(data);
},
toastUp: function(msg){
var toast = document.querySelector('#toast');
var snackbarText = document.querySelector('.mdl-snackbar__text');
snackbarText.innerHTML = msg;
toast.classList.add("mdl-snackbar--active");
},
toastDown: function(count) {
setTimeout(function () {
var toast = document.getElementById("toast");
toast.classList.remove("mdl-snackbar--active");
}, config.timeout * count);
},
};
})();
In case you want to fire tools.toastDown after a timeout loop, you can do:
function drop(filteredMeetings) {
tools.clearMarkers(true);
for (var i = 0; i < filteredMeetings.length; i++) {
//drop toast once markers all dropped
if(i === filteredMeetings.length - 1) {
tools.toastDown(i);
}
tools.addMarkerWithTimeout(filteredMeetings[i], i * config.timeout);
}
}
I am writing an extension, where I want my own cookies store mycookies in background.js which stores domain, path, key and flags. In my background.js, when an HTTP response is received, I call chrome.extension.sendMessage() for each Set-Cookie header of the the response. So each response call this function many times within few m-seconds. After the response, only the last one or two cookies are stored, the rest are lost.
I did the following experiment. I connected a button with the function update_mycookies. A click on button calls the function immediately and displays the content of mycookies after 2500ms. If I read the store after 2500ms, all four cookies are there if I introduce the delays (as below) in update_mycookies but if I remove the delays, only the last one or two cookies are updated, the rest are lost. The chrome.extension.sendMessage is using `chrome.storage.local.set(). I want my store must be updated with all the cookies in response.
function update_mycookies(){
chrome.extension.sendMessage({"cdomain":".google.com", "path":"/", "key":"PREF"});
setTimeout(function() {
chrome.extension.sendMessage({"cdomain":".google.com", "path":"/", "key":"NID"});}, 1000);
setTimeout(function(){
chrome.extension.sendMessage({"cdomain":".google.it", "path":"/", "key":"PREF"}); }, 1500);
setTimeout(function(){
chrome.extension.sendMessage({"cdomain":".google.it", "path":"/", "key":"NID"}); }, 2000);
}
The onMessage listenr is the following:
chrome.extension.onMessage.addListener(
function(request,sender,sendResponse) {
if (request["cdomain"] && request["path"] && request["key"]) {
chrome.storage.local.get("mycookies", function (items) {
if (items["mycookies"]) {
var found = false;
var entries = items["mycookies"].split(";");
entries.forEach (function (entry) {
var dk = entry.split(",");
if (dk[0] == request["cdomain"] && dk[1] == request["path"] && dk[2] == request["key"])
found = true;
});
if (!found)
chrome.storage.local.set({"mycookies": items["mycookies"] + ";" + request["cdomain"] + "," + request["path"] + ","
+ request["key"] });
}
else
chrome.storage.local.set({"mycookies": request["cdomain"] + "," + request["path"] + "," + request["key"] });
});
}
}