Function works as expected when called from function using XMLHttpRequest, but fails when called from a function using EventSource. Why is this? - javascript

I'm working on creating a basic messenger using Javascript. I have three functions in a separate js file called loadMessage, messageListener, and displayMessage.
The function loadMessage makes a call to my database for all existing messages, and then calls displayMessages to construct some divs which I use to show the messages I got from the server. These divs are created to appear under each other, with the bottom div being the newly created one showing the latest message.
Once all the messages have been created loadMessage then calls messageListener. This function 'listens' for any new messages which might appear on the database. If any appear then messageListener calls displayMessage. I expect this to create a new div at the bottom of my other divs as before, however when it calls displayMessage the behaviour is completely different than when loadMessage calls displayMessage.
Specifically, it does not create a new div but instead just changes the text in an existing div which appears anywhere within the newly created divs (for example, the div which displays the first message or one somewhere in the middle).
My HTML and PHP files all behave as expected, so I think my issue is somewhere in these three functions.
How can I fix this to behave as expected?
Code:
// Loads chat messages history and listens for upcoming ones.
function loadMessages(userID, contactID) {
contactIDGlobal = contactID;
//load existing messages
var today = new Date();
var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
var param = "userID="+userID+"&contactID="+contactID+"&date="+date;
xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST","Interface-getMessage.php?", true);
xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xmlhttp.send(param);
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//retrives a string of all past messages
var messageString = xmlhttp.responseText;
//parse string to get messages.
var parseMessageString = messageString.split('-');
for (var i = 0; parseMessageString[i] !== null && parseMessageString[i] !== ''; i = i+5){
var contactID = parseMessageString[i];
var senderID = parseMessageString[i+1];
var message = parseMessageString[i+2];
var time = parseMessageString[i+3];
var mID = parseMessageString[i+4];
displayMessage(userID, senderID, contactID, message, date, time, mID);
}
}
};
//listen for new messages
messageListener(userID, contactID);
}
function messageListener(userID, contactID){
if(typeof(EventSource)!=="underfined") {
var newMessage = new EventSource("testerfile.php?userID="+userID+"&contactID="+contactID);
newMessage.onmessage = function(event) {
var newMessageData = event.data;
var parseNewMessage = newMessageData.split('-');
//sender ID may be different to the userID due to the way that messages are stored on the server. Received messages have a different sender.
var senderID = parseNewMessage[0];
var contactID = parseNewMessage[1];
var message = parseNewMessage[2];
var date = parseNewMessage[3];
var time = parseNewMessage[4];
var messageID = parseNewMessage[5];
console.log(event.data);
displayMessage(userID, senderID, contactID, message, date, time, messageID);
};
}else {
document.getElementById("messages").innerHTML = "Your browser does not support this";
}
}
// Displays a Message in the UI.
function displayMessage(userID, senderID, contactID, nMessage, date, time, id){
var messageListElement = document.getElementById('messages');
var messageInputElement = document.getElementById('message');
// If an element for this message already exists, then get it
var id = id;
var div = document.getElementById(id);
// If an element for that message does not exists yet we create it.
if (!div) {
var container = document.createElement('div');
if (userID == senderID){
container.innerHTML = SENDER_MESSAGE_TEMPLATE;
}else{
container.innerHTML = MESSAGE_TEMPLATE;
}
div = container.firstChild;
div.setAttribute('id', id);
for (var i = 0; i < messageListElement.children.length; i++) {
var child = messageListElement.children[i];
}
messageListElement.insertBefore(div, child);
}
var messageElement = div.querySelector('.message');
messageElement.textContent = nMessage;
// Replace all line breaks by <br>.
messageElement.innerHTML = messageElement.innerHTML.replace(/\n/g, '<br>');
}
// Template for messages.
var SENDER_MESSAGE_TEMPLATE =
'<div class="sender_message-container">' +
'<div class="message"></div>' +
'</div>';
var MESSAGE_TEMPLATE =
'<div class="message-container">' +
'<div class="message"></div>' +
'</div>';

The problem was coming from the date being returned as y-m-d, and the parser using "-". This mean I was creating a time var which was the month of my date, and the message ID as the day. I made the alteration below to fix this...
var newMessageData = event.data;
var parseNewMessage = newMessageData.split('-');
//sender ID may be different to the userID due to the way that messages are stored on the server. Received messages have a different sender.
var senderID = parseNewMessage[0];
var contactID = parseNewMessage[1];
var message = parseNewMessage[2];
var date = parseNewMessage[3]+"-"+parseNewMessage[4]+"-"+parseNewMessage[5];
var time = parseNewMessage[6];
var messageID = parseNewMessage[7];

Related

AppScript: Google Calendar Remove Events

I have the following code that uploads my events from Google Sheet to Google Calendar. This code searches the rows that need to be synced to the calendar based on value in column E (status) x[4]. Events that are marked as Pending Schedule or Pending Removal will be synced to the calendar and then update the respective rows in the sheet with Scheduled or Removed.
This code runs just fine in terms of scheduling events. But when it comes to removing events it only works if the first row of the data is marked to be removed, but not on other rows. It gives me TypeError: Cannot read property 'deleteEvent' of undefined and when I investigated the deleteEvent object shows as CalendarEvent.
Seems like the way I have written the if() functions is somehow not appropriate and the else if() does not evaluate the whole data. I have tried replacing else if() with another if() but received the same error.
function calendarSync2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName('Test Sheet')
var settingsSheet = ss.getSheetByName('Calendar Settings');
var calendarId = settingsSheet.getRange('B2').getValues();
var eventCal = CalendarApp.getCalendarById(calendarId);
var eventArray = dataSheet.getRange('A2:I').getDisplayValues();
for (x = 0; x < eventArray.length; x++) {
var event = eventArray[x];
var eventName = event[1];
var eventDate = new Date(event[0]);
var timeZone = event[5];
var startTime = new Date(`${event[0]} ${event[2]} ${timeZone}`); ///Used Object literals of `` combined with ${}
var endTime = new Date(`${event[0]} ${event[3]} ${timeZone}`);
var status = event[4]; // Used in the following comparison expression instead of filter
if (status === 'Pending Schedule') { /// === undertakes strict comparison
var exisEvents = eventCal.getEvents(startTime, endTime, {search: eventName}); // prevents creation of duplicates
if (exisEvents.length == 0) {
Logger.log(`timed ${eventName} scheduled for: ${startTime} till ${endTime}`);
eventCal.createEvent(eventName, startTime, endTime);
eventArray[x][4] = "Scheduled"; // Updates the status
}
} else if (status === 'Pending Removal') {
Logger.log(`${eventName} at ${eventDate} REMOVED`)
var returnedEvent = eventCal.getEvents(startTime, endTime, {search: eventName});
var toRemove = returnedEvent[x];
Logger.log(`returnedEvent is: ${returnedEvent}`);
toRemove.deleteEvent();
eventArray[x][4] = "Removed";
}
}
dataSheet.getRange('A2:I').setValues(eventArray); // Overwrite the source data with the modified array
}
So with a little bit of digging I figured I need to create another loop for removing events. so the following code:
else if (status === 'Pending Removal') {
var returnedEvent = eventCal.getEvents(startTime, endTime, {search: eventName});
var toRemove = returnedEvent[x];
toRemove.deleteEvent();
eventArray[x][4] = "Removed";
}
Should be changed to:
else if (status === 'Pending Removal') {
var returnedEvents = eventCal.getEventsForDay(eventDate,
{search: eventName});
for (i = 0; i < returnedEvents.length; i++) {
var toRemove = returnedEvents[i];
toRemove.deleteEvent();
eventArray[x][4] = 'Removed';
}
}

update spreadsheet in serverside code run from client html javascript not working

I have an html where user requests add and enters data. The javascript in the body of the html calls the server side. I am unable to connect with the sheet either with saved ID or URL in order to add the row.
I cannot update of my spreadsheet despite #Serge insas comment that openById "it means "open for read and write". Am I making a simple mistake or is this impossible. The code initiated from the client side is running in the server.
const ssId = PropertiesService.getScriptProperties().getProperty('ssId');
var sheet = SpreadsheetApp.openById("[ssId]").getSheetByName('Sheet1');
const ssId = PropertiesService.getScriptProperties().getProperty('ssId');
var sheet = SpreadsheetApp.openById("ssId").getSheetByName('Sheet1');
Both get Error: Exception: Unexpected error while getting the method or property openById on object SpreadsheetApp.
const ssUrl = PropertiesService.getScriptProperties().getProperty('ssUrl');
var sheet = SpreadsheetApp.openByUrl("ssUrl").getSheetByName('Sheet1');
Gets error: Exception: Invalid argument: url
ABOVE IS THE IMPORTANT PART
/**
* this code is run from the javascript in the html dialog
*/
function addMbrCode(myAddForm) {
// removed logging
console.log("Beginning addMbrCode" );
paragraph = body.appendParagraph('Beginning addMbrCode.');
// Exception: Unexpected error while getting the method or property openById on object SpreadsheetApp.
// const ssId = PropertiesService.getScriptProperties().getProperty('ssId');
// var sheet = SpreadsheetApp.openById("[ssId]").getSheetByName('Sheet1');
// var sheet = SpreadsheetApp.openById("ssId").getSheetByName('Sheet1');
// Exception: Invalid argument: url
const ssUrl = PropertiesService.getScriptProperties().getProperty('ssUrl');
var sheet = SpreadsheetApp.openByUrl("ssUrl").getSheetByName('Sheet1');
myAddForm = [ fName, lName, inEmail, fallNum, winNum, sprNum];
var fName = myAddForm[0];
var lName = myAddForm[1];
var inEmail = myAddForm[2];
var fallNum = myAddForm[3];
var winNum = myAddForm[4];
var sprNum = myAddForm[5];
var retCd = '';
/**
* 10 - successful add
* 20 - duplicate - not added
*/
var combNameRng = sheet.getRange(2, 4, numRows).getValues();
var inCName = (fName + '.' + lName).toString().toLowerCase();
if (combNameRng.indexOf(inCName) > 0 ) {
console.log("Alert: Not adding duplicate "
+ fName + ' ' + lName + " retCd: " + 20 );
paragraph = body.appendParagraph("Not adding duplicate "
+ fName + ' ' + lName + " retCd: " + 20);
retCd = 20;
return retCd;
}
sheet.appendRow([fName.toString().toLowerCase()
, lName.toString().toLowerCase()
,
, inEmail.toString().toLowerCase()
]);
const currRow = sheet.getLastRow().toString();
);
retCd = 10;
return retCd;
}
If this makes a difference, here is the javascript from the body of my html in the dialog window.
<script>
document.querySelector("#myAddForm").addEventListener("submit",
function(e)
{
alert('begin addEventListener');
e.preventDefault(); //stop form from submitting
var retCd = google.script.run.addMbrCode(this); // client side validation
document.getElementById('errMsg').textContent = 'Successful member
return false; // do not submit - redisplay html
}
);
</script>
Removed unneeded coding detail
Per #iansedano I created an object/array to use instead of this and added the successhandler and failurehandler. In either case I want to see the html again with my message. This is the current script. Response is so doggy I am not seeing alerts, Logger.log, or console.log. Crazy shoppers using my internet!
<script>
document.querySelector("#myRmvForm").addEventListener("submit",
function(e)
// removed alerts and logging
// removed client side validation for simplicity
cSideValidate();
// Then we prevent the form from being submitted by canceling the event
event.preventDefault();
});
function cSideValidate() {
dataObj = [
document.getElementById('fName').value,
document.getElementById('lName').value,
document.getElementById('email').value
];
var retCd = google.script.run.withSuccessHandler(serverReply)
.withFailureHandler(serverReply)
.rmvMbrCode(dataObj); // server side validation
}
function serverReply {
// logic to set the correct message - this is an example
document.getElementById('errMsg').textContent
= 'Successful delete using email.';
}
</script>
Nothing is being added to my spreadsheet so the server side code is not working. I see my loggin so I know it is getting there.
You're getting ssId from the script properties and assigning it to the ssId variable, but then you pass a string ("ssId") to the openById() function, not the value of the variable.
Try the following please:
const ssId = PropertiesService.getScriptProperties().getProperty('ssId');
var sheet = SpreadsheetApp.openById(ssId).getSheetByName('Sheet1');

getChild of body of gmail email using Goole Apps Scripts (GAS)

Problem
I have automated emails coming to me with particular client details in them. I have the repetitive task of repling to each email with a template. I am wanting to automate the process utilising Google Apps Scripts.
Where I'm up to
I have worked out how to collect the body of the email I am replying to. I'm trying to get the third paragraph info and store this in a variable.
Here is my code:
function autoReply() {
//Capturing the automated email
var queryInbox = "is:unread from:(example#gmail.com)";
var locatedEmail = GmailApp.search(queryInbox);
for (var i in locatedEmail){
var thread = locatedEmail[i];
var messages = thread.getMessages();
var msgBody = messages[i].getBody();
var clientsEmail = msgBody.getChild('p')[3]; //Attempting to obtain the third paragraph of the body.
if(messages.length === 1) {
var body = "<p> The clients email is: " + clientsEmail + "</p>";
};
var options = { name: "Temp Name",htmlBody: body };
thread.reply(body, options);
thread.markRead();
thread.moveToArchive();
}
};
Note: Img attached for context.
I believe your goal as follows.
When the thread has one message, you want to retrieve the body of email.
You want to retrieve the 3rd paragraph from the message of email.
You want to reply the message including the retrieved 3rd paragraph.
Modification points:
At for (var i in unread){, I thought that you might use locatedEmail.
When you want to reply the message with messages.length === 1, var msgBody = messages[i].getBody(); is required to be modified. Because in your for loop, the index i is used for var thread = locatedEmail[i]; and var msgBody = messages[i].getBody();.
In your case, I think that getPlainBody() instead of getBody() might be suitable.
When above points are reflected to your script, it becomes as follows.
Modified script:
function autoReply() {
var queryInbox = "is:unread from:(example#gmail.com)";
var locatedEmail = GmailApp.search(queryInbox);
locatedEmail.forEach(thread => {
var messages = thread.getMessages();
if (messages.length === 1) {
var msgBody = messages[0].getPlainBody();
var clientsEmail = msgBody.split("\n")[2]; // Here, the 3rd paragraph is retrieved.
var body = "<p> The clients email is: " + clientsEmail + "</p>";
var options = { name: "Temp Name",htmlBody: body };
thread.reply(body, options);
thread.markRead();
thread.moveToArchive();
}
});
}
Reference:
getPlainBody()

How to concatenate values from a pop-up window choice with values from a database?

I am developing a project with the Kendo UI Framework, using more specically the Scheduler widget and I have the current issue:
On my database I have two tables one called Events and the other one called TypeOfEvents. Each type of event has got a specific color, a specific title plus defined values for startHour and endHour fields.
When the pop-up window to create an event is called, I can choose on two kendoMultiSelect the correspondent user and the type of event.
I can also choose the startDate and endDate. The default behavior of a Scheduler widget has got two datetimepickers also, however, I don't want that option on my pop-up window because the events will have defined hours that an user can't change.
My idea would be the following one:
Once I click save after choosing a specific event on my MultiSelectList, there would be some way to concatenate the startHour and endHour values I have defined in my database with the startDate and endHour field that I choosed on the pop-up window.
Right now, all my events startDate/endDate fields are saved on my DB with this format: 2015-03-01 00:00:00.000
I would like to substitute all those zeros with the values I defined in advance in my startHour/endHour fields of my TypeOfEvents table.
Here's my current CREATE script:
create: function (createEvent) {
var typeOfEventID = $("#selectEvent").val();
var usernameID = $("#selectUsername").val();
var dataStartTemp = $("#dataStart").val();
var dataEndTemp = $("#dataEnd").val();
var note = $("#note").val();
var res = $("#customViewScheduler").data("kendoScheduler");
var res1 = res.resources[1].dataSource.data();
var dataStart = convertToJSONDate(dataStartTemp);
var dataEnd = convertToJSONDate(dataEndTemp);
var changeSet = [];
var id = 0;
usernameID.forEach(function (userID) {
typeOfEventID.forEach(function (eventID) {
var titletemp = $.grep(res1, function (elem) {
if (elem.TypeOfEventID == eventID) {
return true;
}
})
if (titletemp.length > 0) {
note = titletemp[0].title;
}
var entityChange = {};
entityChange.Id = id;
entityChange.Entity = {
'__type': "Events:#BlahBlahWeb",
'UsernameID': userID,
'TypeOfEventID': eventID,
'startDate': dataStart,
'endDate': dataEnd,
'Title': note
};
entityChange.Operation = 2;
changeSet.push(entityChange);
id++
})
})
var changesetPayload = JSON.stringify({
"changeSet": changeSet
});
//Create jQuery ajax request
var Params = {}
Params.type = "POST";
Params.url = "./../Services/BlahBlahWeb-BlahDomainService.svc/JSON/SubmitChanges";
Params.dataType = "json";
Params.data = changesetPayload;
Params.contentType = "application/json";
Params.error = function (httpRequest, textStatus, errorThrown) {
//SendErrorByEmail(errorThrown, httpRequest.responseText)
}
Params.success = function (data) {
//createEvent.success(data);
var scheduler = $("#customViewScheduler").data("kendoScheduler");
var elem = tratanewelem(data.SubmitChangesResult[0].Entity)
scheduler.dataSource.read();
}
//Make the ajax request
$.ajax(Params);
},
Any idea of how can I accomplish that?

Inserting an element right after the sibling in Javascript

I'm making a posting system that when the user types in a message, it creates a few elements and puts it below the "robot" chat message. If the user inputs two different messages, the latest input message goes above the other user message. I want them to go from top to bottom in chronological order.
This doesn't seem to work: (the if/else part)
function submitUserMessage(){
var message = document.getElementById("user-input");
if(message.value){
// YOU
var you = document.createElement("h4");
var youText = document.createTextNode("You");
you.appendChild(youText);
you.className = "ytitle";
document.body.appendChild(you);
insertAfter(robotSays, you);
// User's message
var userMessage = document.createElement("span");
var userMessageText = document.createTextNode(message.value);
userMessage.appendChild(userMessageText);
userMessage.className = "umsg";
document.body.appendChild(userMessage);
insertAfter(you, userMessage);
} else if(userMessage){
userMessage.nextSibling.insertAfter(this);
}
}
Here's a fiddle of my post system :: http://jsfiddle.net/MatthewKosloski/YWWMW/
(Use the enter key to submit)
var userMessage = robotSays;
function submitUserMessage(){
var message = document.getElementById("user-input");
if(message.value){
// YOU
var you = document.createElement("h4");
var youText = document.createTextNode("You");
you.appendChild(youText);
you.className = "ytitle";
document.body.appendChild(you);
insertAfter(userMessage, you);
// User's message
userMessage = document.createElement("span");
var userMessageText = document.createTextNode(message.value);
userMessage.appendChild(userMessageText);
userMessage.className = "umsg";
document.body.appendChild(userMessage);
insertAfter(you, userMessage);
}
}

Categories