Message callback synchronization error - javascript

In my extension's content script, I request data from background.js like so:
fireOnNewTopic (); // Initial run on cold start or full reload.
window.addEventListener ("hashchange", fireOnNewTopic, false);
function fireOnNewTopic () {
/*-- For the pages we want, location.hash will contain values
like: "#!newtopic/{group title}"
*/
if (location.hash) {
var locHashParts = location.hash.split ('/');
if (locHashParts.length > 1 && locHashParts[0] == '#!newtopic') {
var subjectStr = '';
var bodyStr = '';
switch (locHashParts[1]) {
case 'opencomments-site-discussions':
chrome.extension.sendMessage({name:"domain"},
function(response)
{
subjectStr = response.domain;
});
chrome.extension.sendMessage({name:"url"},
function(response)
{
bodyStr = "URL of last page visited: " + response.url;
});
break;
default:
break;
}
if (subjectStr && bodyStr) {
runPayloadCode (subjectStr, bodyStr);
}
}
}
}
Unfortunately, since sendMessage() runs asynchronously with the callback, at the time the code reaches runPayloadCode(), subjectStr and bodyStr are still null, since the code in background.js hasn't completed. What's the best way to synchronize the code so that subjectStr and bodyStr are filled in by the time runPayloadCode() is called?

To elaborate on what Sudarshan said about combining the two requests into one (sorry, need code so couldn't just comment) here's what you could do...
send
chrome.extension.sendMessage({url: true, domain:true}, function(response) {
console.debug('The url is "'+response.url+'" and the domain is"'+response.domain+'"');
if (repsone.url && response.domain) {
runPayloadCode (subjectStr, "URL of last page visited: " + response.domain);
}
});
or, if you wanted it more like your case way for some reason, maybe this is the sorta thing you'd like...
if(location.hash) {
var locHashParts = location.hash.split('/');
if(locHashParts.length > 1 && locHashParts[0] == '#!newtopic') {
var subjectStr = '';
var bodyStr = '';
var request = {};
switch(locHashParts[1]) {
case 'opencomments-site-discussions':
request = {
url: true,
domain: true
}
break;
default:
break;
}
chrome.extension.sendMessage(request, function(response) {
console.debug('The url is "' + response.url + '" and the domain is"' + response.domain + '"');
if(repsone.url && response.domain) {
runPayloadCode(subjectStr, "URL of last page visited: " + response.domain);
}
});
}
}
}
listen
chrome.extension.onMessage.addListener(
function(request, sender, sendResponse) {
var response = {};
if(request.url) {
response.url = "someURL";
}
if(request.domain) {
response.domain = "someDomain";
}
sendResponse(response);
});

You can try the following code:
case 'opencomments-site-discussions':
chrome.extension.sendMessage({
name: "domain"
},
function (response) {
subjectStr = response.domain;
if(subjectStr){
chrome.extension.sendMessage({
name: "url"
},
function (response) {
bodyStr = "URL of last page visited: " + response.url;
if (bodyStr) {
runPayloadCode(subjectStr, bodyStr);
}
});
}
});
However, can't you merge both message's chrome.extension.sendMessage({name: "url"}) and chrome.extension.sendMessage({name: "domain"}) into single message(Because i see them independent) and eliminate multiple nesting?

Related

How can I conditionally allow or prevent submission execution?

The situation
I have a page in which I have multiple forms keeping track of the attendance and one progress_update.
On submit of the progress_update form I have got it so that ajax sends the attendance form submissions separately having used the preventdefault() method to stop the original submission, however I would like to on the condition that no errors were returned by the ajax methods allow the original submission that was originally prevented.
What I have so far:
The ajax function:
function send_attendance(name, lesson, form_id, i) {
var url = '/attendance/' + name + '/' + lesson
$('#error-' + i).hide('slow')
$('#error-' + i).html('')
$.ajax({
type: "POST",
url: url,
data: {
attended: $('#attended' + i).val(),
score: $('#score' + i).val(),
writing: $('#writing' + i).val(),
speaking: $('#speaking' + i).val()},
success: function(data) {
if (data.data.message == undefined) {
allow=false;
if (data.data.score[1] == undefined) {
var error_data = data.data.score[0]
} else {
var error_data = data.data.score[1]
}
$('#error-' + i).show('slow')
$('#error-' + i).html('<p style="color:red;">' + error_data + '</p>')
} else {
console.log(data.data.message) // display the returned data in the console.
}
}
});
}
The Intention:
The intention behind this ajax is to send the forms to a separate route for validation and then on success "receiving data.data.message == 'submitted'" pass to the next form in the loop, while on error set the allow variable to false and display the message in hopes to prevent the final form being submitted at the same time.
The call:
$('#update_form').submit(function (e) {
var allow = true;
for (var i = 0; i < studentcount ; i++) {
send_attendance(name=st[i], lesson=lesson, form_id='attendance-' + i, i=i)
}
if (allow == true){
} else {
e.preventDefault();
}
});
The Problem
In doing what I have done I have ended up with a situation of it either submits the ajax submitted forms and that is that preventing the submit form or it submits the form whether errors occured in the ajax that need to be displayed, now how do I get this to work in the way expected? I have tried the methods involved in these previous questions:
How to reenable event.preventDefault?
How to unbind a listener that is calling event.preventDefault() (using jQuery)?
which revolve around using bind and unbind but this doesn't seem to work as needed and results in a similar error.
Any advice would be greatly appreciated.
Edit:
I have adjusted the code based on the comment below to reflect, however it still seems to be evaluating the allow before the ajax have completed. either that or the ajax function isn't changing the allow variable which is set in the submit() call how could i get this to change the allow and evaluate it after the ajax calls are complete?
The Ajax call
function send_attendance(name, lesson, form_id, i) {
var url = '/attendance/' + name + '/' + lesson
$('#error-' + i).hide('slow')
$('#error-' + i).html('')
var form = $('#' + form_id)
$.ajax({
type: "POST",
url: url,
data: $('#'+ form_id).serialize(),
context: form,
success: function(data) {
console.log('done')
if (data.data.message == undefined) {
allow = false;
if (data.data.score[1] == undefined) {
var error_data = data.data.score[0]
} else {
var error_data = data.data.score[1]
}
$('#error-' + i).show('slow')
$('#error-' + i).html('<p style="color:red;">' + error_data + '</p>')
} else {
console.log(data.data.message) // display the returned data in the console.
}
}
});
The function is being called here:
$('#update_form').submit(function (e) {
e.preventDefault();
var allow = true;
var deferreds = [];
for (var i = 0; i < studentcount ; i++) {
deferreds.push(
send_attendance(st[i], lesson, 'attendance-' + i, i));
}
$.when(...deferreds).then(function() {
if (allow == true){
console.log('True')
} else {
console.log('False')
}
});
I also tried:
$('#update_form').submit(function (e) {
e.preventDefault();
var allow = true;
var deferreds = [];
for (var i = 0; i < studentcount ; i++) {
deferreds.push(
send_attendance(st[i], lesson, 'attendance-' + i, i));
}
$.when.apply(deferreds).done(function() {
if (allow == true){
console.log('True')
} else {
console.log('False')
}
});

Prevent sending data to DB/server if form validation is false (VueJS)

I want to stop sending information if form validation is false.
I have a button Save with two functions in it:
<span class="logInBTN" v-on:click="validationFields(); function2(model)">Save</span>
The form validation is being proccessed in validationFields():
validationFields() {
if (this.model.codePerson == '') {
document.getElementById('codePerson').style.borderColor = "red";
this.errors.push("Choose a type!\n");
falseValidation = true;
} else {
document.getElementById('codePerson').style.borderColor = "#CCCCCC";
}
if (falseValidation == true) {
alert("Form validation:\n" + this.errors.join(""));
}
}
So if it's not chosen a type from the input field, function2() must not continue.
Update1:
<script>
export default {
components: {
},
data(){
return {
errors: [];
},
},
methods: {
validationFields() {
this.errors = [];
var falseValidation = false;
if (this.model.codePerson == '') {
document.getElementById('codePerson').style.borderColor = "red";
this.errors.push("Choose a type!\n");
falseValidation = true;
} else {
document.getElementById('codePerson').style.borderColor = "#CCCCCC";
}
if (falseValidation == true) {
alert("Form validation:\n" + this.errors.join(""));
}
if(falseValidation == false){
this.createEori(eoriData);
}
}
createEori(eoriData) {
eoriData.state = '1';
eoriData.username = this.$session.get('username');
console.log("updateEori state: " + JSON.stringify(eoriData));
const url = this.$session.get('apiUrl') + 'registerEORI';
this.submit('post',
url,
eoriData
);
},
submit(requestType, url, submitData) {
this.$http[requestType](url, submitData)
.then(response => {
console.log('EORI saved!');
console.log('Response:' + response.data.type);
if("E" == response.data.type){
alert(response.data.errorDescription);
} else {
alert("Saved!");
}
})
.catch(error => {
console.log('EORI rejected!');
console.log('error:' + error);
});
},
},
}
</script>
createEORI is the function2
Update2
Now it works, but the data from the fields it's not send to the server. That's all fields from the page, some are datepickers or an ordinary input text field. Before the change in the browser console show this, if I write a name in the first field it will show up in c1_name etc:
{"state":"1","c1_form":"","c1_identNumber":"","c1_name":"","c1_shortName":"","c1_8_street":"","c1_8_pk":"","c1_8_name":"","c1_8_city":"","c1_8_codeCountry":"","c1_identNumber1":"","c3_name":"","c3_nameShort":"","c3_city":"","c3_codeCountry":"","c3_street":"","c3_pk":"","c3_phone":"","codePerson":"","codeActivity":"","c1_date":"","c5_date":"","c7_date":"","dateFrom":"","dateTo":"","c8_date":"","c1_numberVAT":"","c8_provider":"","c8_number":"","codeMU":"","agreed1":"","agreed2":"","username":"testuser"}
However, after the change the sent data or at least the seen data is only:
{"state":"1","username":"testuser"}
The log is from
console.log("updateEori state: " + JSON.stringify(eoriData));
from createEORI() function
I think it would be better practice to only call one function from the HTML. Something like this:
<span class="logInBTN" v-on:click="submit(model)">Save</span>
submit(model) {
if (this.validateForm(model) == true)
{
// submission process here (maybe call function2())
}
}
validateForm(model) {
if (this.model.codePerson == ''){
document.getElementById('codePerson').style.borderColor = "red";
this.errors.push("Choose a type!\n");
this.handleFalseValidation();
return false;
}
document.getElementById('codePerson').style.borderColor = "#CCCCCC";
return true;
}
handleFalseValidation() {
alert("Form validation:\n" + this.errors.join(""));
}
Ok I fixed the problems with sending the data.
It was my fault.
I will copy the Chris answer. That worked.
When you call this.createEori(eoriData);, eoriData is undefined. It doesn't exist. Use this.createEori(); instead, and in the createEori function, remove the parameter and add var eoriData = {}; as first line. (note this is very basic javascript, how functions and variables work, and completely unrelated to Vue or server requests)

Javascript callback function executed 2 times

users can sign in to my system using google sign in so when use pressing google sign in button his account will be create in mysql database
my problem is every users account created two time when user trying to sign in by google
in other words function of create account executed two time for every user
here is my html code
<a id="gp_login" href="javascript:void(0)" onclick="javascript:googleAuth()">Login using Google</a>
this is javascript code
function gPOnLoad(){
// G+ api loaded
document.getElementById('gp_login').style.display = 'block';
}
function googleAuth() {
gapi.auth.signIn({
callback: 'gPSignInCallback',
clientid: '636950137786-j3siaftgshtf9iamovisf603pplv7jf1.apps.googleusercontent.com',
cookiepolicy: "single_host_origin",
requestvisibleactions: "http://schema.org/AddAction",
scope: "https://www.googleapis.com/auth/plus.login email https://www.googleapis.com/auth/user.phonenumbers.read https://www.googleapis.com/auth/user.birthday.read"
})
}
function gPSignInCallback(e) {
if (e["status"]["signed_in"]) {
gapi.client.load("plus", "v1", function() {
if (e["access_token"]) {
getProfile()
} else if (e["error"]) {alert(e['error'])
console.log("There was an error: " + e["error"])
}
})
} else {alert(e["error"]);
console.log("Sign-in state: " + e["error"])
}
}
function getProfile() {
//var e = googleData.getBasicProfile();
var e = gapi.client.plus.people.get({
userId: "me"
});
e.execute(function(e) {
if (e.error) {alert(e.message)
console.log(e.message);
return
} else if (e.id) {var msgs=JSON.stringify(e);
alert(e.displayName);
update_user_data(e);
// save profile data
}
})
}(function() {
var e = document.createElement("script");
e.type = "text/javascript";
e.async = true;
e.src = "https://apis.google.com/js/client:platform.js?onload=gPOnLoad";
var t = document.getElementsByTagName("script")[0];
t.parentNode.insertBefore(e, t)
})()
function update_user_data(response)
{
// var dataString = JSON.stringify(response);
var email=response.emails[0]['value'];
var displayName=response.displayName;
//ar
$.ajax({
type: "POST",
data: {email:email,displayName:displayName},
url: 'Save.php?id=check_user',
success: function(msg) {
var array = msg.split(',');
var email =array[0];alert(email);
var password = array[1];alert(password);
$('#username').val(email);$('#password').val(password);
document.getElementById("modal4c").click();
},
error: function(XMLHttpRequest,textStatus,errorThrown) {//alert(JSON.stringify(msg));
}
});
}
update_user_data() function is to insert account into mysql database but this function is called twice per user.
Not sure why you function runs twice but,
one way to ensure a function runs only once would be make some global flag like this
runOnce = false;
function gPSignInCallback(e) {
if(runOnce) return;
runOnce = true;
// ... rest of the function
}
If you want to avoid global vars you could return a closure like this
function update_user_data(e){
var runOnce = false
return function(){
if(runOnce) return;
runOnce = true;
// ... rest of the function
}
}
And call it like this update_user_data()(e)

FCC wikipedia viewer not receiving data

I'm have some serious problems getting any response data through from the mediawiki api.
I'm trying to do the freecodecamp wikipedia viewer challenge and I'm coding it here:
https://codepen.io/dceaser334/pen/zpQXOJ
All i'm trying to do so far is GET the data and print it to the console using the following request:
$('.search-button').on('click', function() {
var searchInput = $('.search-input').val();
$.getJSON('https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=' + searchInput + '&format=json&callback=?', function(data) {
console.log(data);
});
});
All i'm trying to do so far is GET the data and print it to the console using the that request.
I'm getting this error in firefox:
Loading failed for the with source
“https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=jordan&format=json&callback=jQuery32105036538970753343_1518470620925&_=1518470620926”.
index.html:1
Nothing loads to the console and it seems like the request is blocked.
I've tried using origin=* which also makes no difference.
I'm a bit lost because this project has similar code for the GET request and works perfectly:
https://codepen.io/luckyguy73/pen/GqPzZO?editors=1010
$("#searchWiki").click(function(){
var q = document.getElementById("searchid").value;
$('#results').html('');
$.getJSON("https://en.wikipedia.org/w/api.php?action=query&format=json&gsrlimit=15&generator=search&origin=*&gsrsearch=" + q, function(data){
$('#results').append('<h2>Top 15 Wiki Search Results for "' + q + '"</h2>');
$.each(data.query.pages, function (i) {
$('#results').append("<p><a href='https://en.wikipedia.org/?curid=" + data.query.pages[i].pageid +
"' target='_blank'>" + data.query.pages[i].title + "</a></p>");
});
});
});
Any ideas on what I'm doing wrong here?
Thanks
I did it like this. Maybe you will be inspired by analyzing my code?
( function ( $ ) {
"use strict";
$(document).ready(function(){
function loadData() {
$(".information").text(""); // Reset data before new search.
$(function whiteFirst() {
const query = $(".wiki_query").val();
const myFirstWikiUrl = "https://en.wikipedia.org/w/api.php?action=opensearch&search=";
const mySecondWikiUrl = "&format=json&callback=wikiCallback";
const wikiUrl = myFirstWikiUrl + query + mySecondWikiUrl;
// MY WIKIPEDIA AJAX GOES HERE - TOP
const wikiRequestTimeout = setTimeout(function() {
$(".small-information").html("An error occurred! Application couldn't get Wikipedia resources!");
}, 5000); // This is 5 seconds!
$.ajax({
url: wikiUrl,
dataType: "jsonp",
type: "GET",
}).done(function(result) {
const itemsOne = [];
const itemsTwo = [];
const itemsThree = [];
$(result[1]).each(function(index, value) {
itemsOne.push(value);
});
$(result[2]).each(function(index, value) {
itemsTwo.push(value);
});
$(result[3]).each(function(index, value) {
itemsThree.push(value);
});
$(".information").hide();
$(".results").hide();
for (let i = 0; i < itemsOne.length; i++) {
$(".information").append("<a class='title' href=" + itemsThree[i] + " target='_blank'><div class='result'><p class='title' id='boldTitle'>" + itemsOne[i] + "</p><p>" + itemsTwo[i] + "</p></div></a>");
}
if (itemsOne.length === 0) {
$(".information").html("Nothing found!");
}
$(".results").show();
$("body,html").animate({
'scrollTop': $(".results").offset().top
}, 2000);
$(".information").fadeIn("slow");
clearTimeout(wikiRequestTimeout); // This will prevent timeout from happening!
});
// MY WIKIPEDIA AJAX GOES HERE - BOTTOM
});
return false;
};
$(".whiteButton").click(loadData);
$(".results").hide();
$(function() {
const offset = -50; // Optional offset
$(".back").click(function() {
$("html, body").animate({
scrollTop: $(".cover").offset().top + offset
}, 750);
});
});
});
} ( jQuery ) );

Prevent users from opening multiple instance of same website PHP

I need only one tab accessible for my website. When he tries to open in new tab or even tries to copy and paste the url in new tab should clear the user's session and logout from the application.
There are several reasons,
When a user opens a new tab connecting to the same application - the session id is the same.
Imagine that this user has reached a page X in the application flow from the first tab.
When he opens the second tab he might be in one of the following scenarios - depending how the second tab was opened - new tab, duplicate tab (this copies the URL to the newly opened tab), or new session.
All of the above will "confuse" the server as to what the next valid state of the application is, and could override data entered in different tab, without his/her knowledge
What I want is to prevent a single user to have several tabs in the same session, i.e. only one tab/window per user, per session.
Including the below script in dashboard.php after login
<script>
$(document).ready(function()
{
if(typeof(Storage) !== "undefined")
{
if (sessionStorage.pagecount)
{
sessionStorage.removeItem('pagecount');
window.location='logout.php';
}
else
{
sessionStorage.pagecount = 1;
}
}
else
{
sessionStorage.removeItem('pagecount');
window.location='logout.php';
}
});
Below code in other sub pages in the application
<script>
$(document).ready(function()
{
if(typeof(Storage) !== "undefined")
{
if (sessionStorage.pagecount)
{
sessionStorage.pagecount = Number(sessionStorage.pagecount) + 1;
}
else
{
sessionStorage.removeItem('pagecount');
window.location='logout.php';
}
}
else
{
sessionStorage.removeItem('pagecount');
window.location='logout.php';
}
});
</script>
Added the below script after I login(say dashboard.php)
<script>
$(document).ready(function()
{
$("a").attr("target", "");
if(typeof(Storage) !== "undefined")
{
sessionStorage.pagecount = 1;
var randomVal = Math.floor((Math.random() * 10000000) + 1);
window.name = randomVal;
var url = "url to update the value in db(say random_value)";
$.post(url, function (data, url)
{
});
}
else
{
var url = "url to remove random_value";
$.post(url, function (data, url)
{
sessionStorage.removeItem('pagecount');
sessionStorage.clear();
window.location = 'logout.php';
});
}
});
</script>
Added the below script in Header in rest of my pages - 'random_value' is from db for that user
<script>
$(document).ready(function()
{
$("a").attr("target", "_self");
if(typeof(Storage) !== "undefined")
{
if (sessionStorage.pagecount)
{
if('<?=$random_value?>' == window.name)
{
sessionStorage.pagecount = Number(sessionStorage.pagecount) + 1;
}
else
{
var url = "url to remove random_value";
$.post(url, function (data, url)
{
sessionStorage.removeItem('pagecount');
sessionStorage.clear();
window.location = 'logout.php';
});
}
}
else
{
var url = "url to remove random_value";
$.post(url, function (data, url)
{
sessionStorage.removeItem('pagecount');
sessionStorage.clear();
window.location = 'logout.php';
});
}
}
else
{
var url = "url to remove random_value";
$.post(url, function (data, url)
{
sessionStorage.removeItem('pagecount');
sessionStorage.clear();
window.location = 'logout.php';
});
}
});
</script>
<script>
$(document).ready(function()
{
$("a").attr("target", "");
if(typeof(Storage) !== "undefined")
{
sessionStorage.pagecount = 1;
var randomVal = Math.floor((Math.random() * 10000000) + 1);
window.name = randomVal;
var url = "url to update the value in db(say random_value)";
$.post(url, function (data, url)
{
});
}
else
{
var url = "url to remove random_value";
$.post(url, function (data, url)
{
sessionStorage.removeItem('pagecount');
sessionStorage.clear();
window.location = 'logout.php';
});
}
});
</script>

Categories