Thanks in advance. I'm making a website for a service I run with my partner called Laughter Yoga Dublin. I've come across a problem that's got me stuck. It looks like lots of code below but my question pertains mainly (I think) to the first and last few lines.
The first five lines are variable declarations as you can see. Those lines are followed by a jQuery function beginning ($.getJSON etc...) in which those variables are processed. And that function ends just before the last few lines of code given below.
My question is: how come the lines at the end of the code (console.log etc...) are showing that the variables' values are uninfluenced by the preceding function? I'm the first to admit that I'm a baby with this programming stuff but I thought that since I'd declared the variables outside the function, their values could be changed from within the function. Have I got that wrong? I'm finding it a bit hard to understand, and my own (limited) experience with this seems to contradict the effects of the bit of code below.
I'm willing to research and learn but I'd really appreciate even a nudge in the right direction with this problem.
Thanks again,
Niall, Dublin, Ireland.
var events = []
var theBitImInterestedIn
var prettyDate;
var nextEventString;
var JSONurl = 'https://www.google.com/calendar/feeds/avmknaehgjre8qjqhbi22fn8mo%40group.calendar.google.com/public/basic?alt=json&orderby=starttime&max-results=20&singleevents=true&sortorder=ascending&futureevents=true';
$.getJSON(JSONurl ,function(data){
theBitImInterestedIn = data.feed.entry;
$.each(theBitImInterestedIn, function(key, val){
var event = {};
var Content = val.content.$t;
var weekDayNumber, weekDay = '', Days=['Mon','Tue','Wed','Thu', 'Fri', 'Sat', 'Sun'];
var monthNumber, monthOfTheEvent = '', Months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var venue = theBitImInterestedIn[0].content.$t
venueBegin = venue.indexOf('Where');
venueMod = venue.substring(venueBegin);
venueEnd = venue.indexOf('<br') -2;
venue = venueMod.substring(7, venueEnd);
$.each(Days, function(key, val){
if (Content.match(Days[key])){
weekDay = val;
weekDayNumber = key + 1;
}
})
$.each(Months, function(key, val){
if (Content.match(Months[key])){
monthOfTheEvent = val;
monthNumber = key + 1;
}
})
var actualDay = Content.match(/\d+/)[0];
var year = Content.match(/\d+/g)[1];
event.Title = 'Laughter Yoga';
var tempDate = monthNumber + '/' + actualDay + '/' + year;
var prettyDate = weekDay + ' ' + actualDay + ' ' + monthOfTheEvent + ' ' + year;
event.Venue = venue;
event.PrettyDate = prettyDate;
event.Date = new Date(tempDate);
events.push(event);
})
nextEventString = 'Next class: ' + events[0].PrettyDate + ' at ' + events[0].Venue;
//$('p').html(nextClassString).appendTo('#next-class');
});
console.log(events); //these result in an empty [] and undefined
console.log(theBitImInterestedIn)
console.log(prettyDate)
console.log(nextEventString)
As mentioned, getJSON is asynchronous. In order for console.log to do anything with the changed variables, you would need to call them in a function after the asynchronous call.
var events = []
var theBitImInterestedIn
var prettyDate;
var nextEventString;
function callJSON() {
var JSONurl = 'https://www.google.com/calendar/feeds/avmknaehgjre8qjqhbi22fn8mo%40group.calendar.google.com/public/basic?alt=json&orderby=starttime&max-results=20&singleevents=true&sortorder=ascending&futureevents=true';
$.getJSON(JSONurl ,function(data){
...
///////////////////////////////////////////////////////////////
// call to show console log
///////////////////////////////////////////////////////////////
logConsole()
});
}
function logConsole() {
console.log(events); //these result in an empty [] and undefined
console.log(theBitImInterestedIn)
console.log(prettyDate)
console.log(nextEventString)
}
Related
The code below is a Google Ads script. It does a fairly simple thing: it grabs the ad group impression share values from any 2 given periods, and issues an alert for ad groups whose impression share values dropped by more than 10% between the "pre" and "post" periods. More details are in the comments in the code.
Here's the really, really weird part: everytime I preview the code (by pressing the "Preview" button in the Google Ads script console), it always fails the first time, but then in subsequent tries (2nd, 3rd, etc) it always works.
If I go back to the Google Ads main "Scripts" page and then open the script and preview it again, it seems to get "reset" and fails again, and then succeeds in the 2nd try onwards.
The exact error is: "TypeError: Cannot read property 'preImpressionShare' of undefined".
According to the Google Ads script console, the line that causes the error is the following:
if ((!isNaN(adgroup.preImpressionShare)))
My questions are:
One would think that if the code is wrong, it should always fail. Yet, it works most of the time, and only fails in the first try. Why is it unstable like that?
How do I fix the error so that it will always work? Is the isNaN() function being used incorrectly or what?
var config = {
campaignsContain: '', // The script will only look for campaigns that contain this string - leave blank to work on all campaigns
imprShareDropThreshold: 0.1, // The percent change to trigger an alert (0.1 = 10% change)
emails: 'email#myemail.com', // Comma-separated list of emails to alert
}
function main() {
/*Date settings for the "pre" period.*/
var preStartDate = new Date('January 1, 2022');
var preFormattedStartDate = Utilities.formatDate(preStartDate, AdsApp.currentAccount().getTimeZone(), 'EEE, MMM d, YYYY');
preStartDate = Utilities.formatDate(preStartDate, AdsApp.currentAccount().getTimeZone(), 'YYYYMMdd');
var preEndDate = new Date('January 31, 2022');
var preFormattedEndDate = Utilities.formatDate(preEndDate, AdsApp.currentAccount().getTimeZone(), 'EEE, MMM d, YYYY');
preEndDate = Utilities.formatDate(preEndDate, AdsApp.currentAccount().getTimeZone(), 'YYYYMMdd');
/*Date settings for the "post" period.*/
var postStartDate = new Date('February 1, 2022');
var postFormattedStartDate = Utilities.formatDate(postStartDate, AdsApp.currentAccount().getTimeZone(), 'EEE, MMM d, YYYY');
postStartDate = Utilities.formatDate(postStartDate, AdsApp.currentAccount().getTimeZone(), 'YYYYMMdd');
var postEndDate = new Date('February 28, 2022');
var postFormattedEndDate = Utilities.formatDate(postEndDate, AdsApp.currentAccount().getTimeZone(), 'EEE, MMM d, YYYY');
postEndDate = Utilities.formatDate(postEndDate, AdsApp.currentAccount().getTimeZone(), 'YYYYMMdd');
/*GAQL setup for the "pre" period query*/
var preCampaignFilter = config.campaignsContain.length > 0 ? ' AND CampaignName CONTAINS "' + config.campaignsContain + '" ' : '';
var preQuery = 'SELECT AdGroupId, CampaignName, AdGroupName, SearchImpressionShare FROM ADGROUP_PERFORMANCE_REPORT WHERE AdGroupStatus = ENABLED ' + 'DURING ' + preStartDate + ',' + preEndDate;
var preReport = AdsApp.report(preQuery);
var preAdgroups = [];
var preRows = preReport.rows();
/*GAQL setup for the "post" period query*/
var postCampaignFilter = config.campaignsContain.length > 0 ? ' AND CampaignName CONTAINS "' + config.campaignsContain + '" ' : '';
var postQuery = 'SELECT AdGroupId, CampaignName, AdGroupName, SearchImpressionShare FROM ADGROUP_PERFORMANCE_REPORT WHERE AdGroupStatus = ENABLED ' + 'DURING ' + postStartDate + ',' + postEndDate;
var postReport = AdsApp.report(postQuery);
var postAdgroups = [];
var postRows = postReport.rows();
/*Traverse the "pre" period query results, and add them to an array.*/
while (preRows.hasNext()) {
var preRow = preRows.next();
var preAdgroupid = preRow.AdGroupId;
var preAdgroup = preRow.AdGroupName;
var preCampaign = preRow.CampaignName;
var preImpressionShare = parseFloat(preRow.SearchImpressionShare.replace('%', '')) / 100;
let preAdGroupObject = {};
preAdGroupObject.adgroupid = preAdgroupid;
preAdGroupObject.adgroup = preAdgroup;
preAdGroupObject.campaign = preCampaign;
preAdGroupObject.preImpressionShare = preImpressionShare;
preAdgroups.push(preAdGroupObject);
}
/*Traverse the "post" period query results, and add them to an array.*/
while (postRows.hasNext()) {
var postRow = postRows.next();
var postAdgroupid = postRow.AdGroupId;
var postAdgroup = postRow.AdGroupName;
var postCampaign = postRow.CampaignName;
var postImpressionShare = parseFloat(postRow.SearchImpressionShare.replace('%', '')) / 100;
let postAdGroupObject = {};
postAdGroupObject.adgroupid = postAdgroupid;
postAdGroupObject.adgroup = postAdgroup;
postAdGroupObject.campaign = postCampaign;
postAdGroupObject.postImpressionShare = postImpressionShare;
//if(postImpressionShare > 0.1) {
postAdgroups.push(postAdGroupObject);
//}
}
/*Merge the "pre" query results with the "post" query results* and store everything into one single array*/
mergedAdGroups = mergeArrayObjects(preAdgroups, postAdgroups);
//Traverse the "mergedAdGroups" array and calculate the impression share difference between the pre vs. post period.
//Add the results to the "alerts" array only if the impression share difference is less than the threshold (10%)
var alerts = [];
mergedAdGroups.forEach(
function(adgroup)
{
if ((!isNaN(adgroup.preImpressionShare)))
{
//Logger.log(adgroup.preImpressionShare + " and " + adgroup.postImpressionShare);
var difference = (adgroup.postImpressionShare - adgroup.preImpressionShare) / adgroup.preImpressionShare;
//campaigns[campaign].difference = difference;
if (difference < -config.imprShareDropThreshold)
{
alerts.push(' - ' + adgroup.adgroup + " of the campaign " + adgroup.campaign + ': from ' + (adgroup.preImpressionShare * 100).toFixed(2) + '% to ' + (adgroup.postImpressionShare * 100).toFixed(2) +'%' + " [" + (difference * 100).toFixed(2) + '%' + " drop.]");
}
}
}
);
//Combine an intro message for the email alert with the contents of the "alerts" variable (see above) and store everything into the "message" variable.
var message =
'The following campaigns had impression share drops by more than ' + (Math.abs(config.imprShareDropThreshold) * 100).toFixed(2) + '% between the pre and post period' + '. \n\n' +
alerts.join('\n') + '\n\n' +
'---END OF MESSAGE---';
//This is for debugging (to see the results in the Google Ads script console)
//Logger.log(message);
//This line passes the "message" variable and sends it as an email alert.
//if (alerts.length > 0) {MailApp.sendEmail(config.emails, 'Impression Share Drop Alert!', message);}
}
//This function merges 2 arrays based on common adgroup IDs
function mergeArrayObjects(arr1,arr2){
return arr1.map((item,i)=>{
if(item.adgroupid === arr2[i].adgroupid){
//merging two objects
return Object.assign({},item,arr2[i])
}
})
}
Good day,
I'm coding a simple calculation price based on:
A single price
The frequency of that price (Weekly, daily, etc.).
I keep having the word "undefined" being printed out in the page. I've tried to redefine all the variable of that part. All variables are correctly defined. Do you guys see any error in my code below?
//parking cost
var parking_cost1 = 0;
var parkingCostUnity1 = 0;
var parkingCostFrequency1 = 0;
parking_cost1 = new Number(document.getElementById("parking_cost1").value);
parkingCostFrequency1 = document.getElementById("parking_cost1_frequence1").value;
if (parkingCostFrequency1 == "per_night") {
parkingCostUnity1 = result;
}
if (parkingCostFrequency1 == "per_week") {
parkingCostUnity1 = nrOfWeeks;
}
if (parkingCostFrequency1 == "per_month") {
parkingCostUnity1 = nrOfMonths;
}
var echoParkingCostFrequency1 = parkingCostFrequency1.replace("_", " ");
var totalParkingCost1 = 0;
totalParkingCost1 = parkingCostUnity1 * parking_cost1;
if (totalParkingCost1 > 0) {
var printOutTotalParkingCost1 = '<tr><td><b>Total parking cost</b></td><td>' + parking_cost1 + ' ' + currency + ' ' + echoParkingCostFrequency1 + '</td><td>' + totalParkingCost1 + ' ' + currency + '</td></tr>';
}
<i class="fas fa-parking"></i> <text class="form_basic_text" />Parking cost<br>
<input type="number" onChange="resume()" name="parking_cost1" id="parking_cost1" class="form_basic_field_bid" style="width:auto;" /><br/>
<center>
<select onChange="resume()" name="parking_cost1_frequence1" id="parking_cost1_frequence1" style="width:auto;height:25px;padding:0px;width:auto;">
<option value="per_night">Per night</option>
<option value="per_week">Per week</option>
<option value="per_month">Per month</option>
</select>
</center>
The following variables have already been defined in the document:
//var currency
var currency = document.getElementById("currency").value;
//This variable is always defined, as it's a dropdown.
//var result, nrOfWeeks and nrOfMonths are all under the same parameters
//check-in date
var checkInDate = new Date(document.getElementById("check_in_date").value);
var checkInDateResume = checkInDate.getDate() + '-' + (checkInDate.getMonth()+1) + '-' + checkInDate.getFullYear();
if(checkInDateResume=="NaN-NaN-NaN"){
checkInDateResume = "<text class='alert'>Not defined yet</text>";
result = 1;
nrOfWeeks = 1;
nrBiWeeks = 1;
nrOfMonths = 1;
}
var checkOutDate = new Date(document.getElementById("check_out_date").value);
var checkOutDateResume = checkOutDate.getDate() + '-' + (checkOutDate.getMonth()+1) + '-' + checkOutDate.getFullYear();
if(checkOutDateResume=="NaN-NaN-NaN"){
checkOutDateResume = "<text class='alert'>Not defined yet</text>";
result = 1;
nrOfWeeks = 1;
nrBiWeeks = 1;
nrOfMonths = 1;
}
//Since the nr of months, week and result (the number of nights) is the difference between the check-in and check-out date - Those aren't defined except as mentionned up here. We then define the rest of the vars
var difference = Math.abs(checkOutDate-checkInDate);
var result = difference/(1000*3600*24);
var nrOfMonths = Math.ceil(result/30.41);
var nrOfWeeks = Math.ceil(result/7);
Thank you!
Edit: It's my first time posting here. Thank you for your patience and your comments on how to make my question more understandable!
I did some extended research and discovered my mistake:
I forgot to add an else to the the last IF statement.
if(totalParkingCost1>0){
var printOutTotalParkingCost1 = 'XYZ';
}
The var printOutTotalParkingCost1 was used in any scenario. I added the following else
else{
var printOutTotalParkingCost1 = "";
}
Now, the variable printOutTotalParkingCost1 is defined whatever the scenario is. Thank you all for your kind help! Thanks to #ivar for his extensive explanations.
This code is for internal, offline, single user use, IE only. The code looks at a folder, and lists all files including those in subfolders. It sorts through the data based on some date fields and datelastmodified. It also uses and if to throw out thumbs.db entries. All of the data is put into a table.
My issue is that this script can take a long time to get the data. I would like to add a progress bar but the progress bar cant update while the script is running. After some research it looks like SetTimeOut can allow the page elements to be updated as the script runs, therefore allowing the progress bar to work and looking overall cleaner. However I can not figure out of to implement SetTimeOut into my existing code.
<script type="text/javascript">
var fso = new ActiveXObject("Scripting.FileSystemObject");
function ShowFolderFileList(folderspec) {
var beginningdate = new Date(startdate.value);
var finishdate = new Date(enddate.value);
var s = "";
var f = fso.GetFolder(folderspec);
var subfolders = new Enumerator(f.SubFolders);
for (subfolders.moveFirst(); !subfolders.atEnd(); subfolders.moveNext()) {
s += ShowFolderFileList(subfolders.item().path);
}
// display all file path names.
var fc = new Enumerator(f.files);
for (i = 0; !fc.atEnd(); fc.moveNext()) {
if (fc.item().name != "Thumbs.db") {
var dateModified = fc.item().DatelastModified;
if (dateModified >= beginningdate && dateModified <= finishdate) {
Date.prototype.toDateString = function () {
return [this.getMonth() + 1, '/', this.getDate(), '/', this.getFullYear()].join('');
}
var dateModifiedClean = (new Date(fc.item().DatelastModified).toDateString());
s += "<table border=0 width=100% cellspacing=0><tr " + ((i % 2) ? "" : "bgcolor=#EBF1DE") + "><td width=75%><font class=find><b>" + fc.item().ParentFolder.name + "</b>" + " - " + fc.item().name + "</font></td><td width=25% align=right><font class=find>" + dateModifiedClean + "</font></td></tr>";
i++;
}
}
}
var results = s + "</table>";
return results;
}
function listFiles() {
outPut.innerHTML = ShowFolderFileList('*Path to scan*');
}
</script>
outPut is the ID of a div tag where the results table is displayed. A button calls the listfiles function.
Can you please say whats wrong with this? I have a javascript function called which creates a new events array and tries to refresh fullcalendar.
var events=new Array();
var numberofevents = this.serviceVariableGetDates.getTotal();
for (i=0;i<numberofevents;i++)
{
//alert("numbrr:" + i);
var dates=this.serviceVariableGetDates.getItem(i);
console.log(dates.getData());
var start_date = dates.getValue("c0");
var end_date = dates.getValue("c1");
var event_name = dates.getValue("c2");
//var EventEntry = [ 'title: '+ event_name, 'start: '+ start_date,'end: '+ end_date ];
events['title'] = event_name;
events['start'] = start_date;
events['end'] = end_date;
events['color'] = "blue";
this.label1.setCaption(start_date);
//EventArray.push(EventEntry);
console.log(events['title']);
}
$('#calendar').fullCalendar('addEventSource',events);
$('#calendar').fullCalendar('rerenderEvents');
The calendar does not refresh or show the events in the events array....Through different debug methods I am sure that the events array is populated with the correct data. The start_date is for example "1307318400000" which is in the unix timestamp format. The fullcalendar is being initialized somewhere else in the begining (when the page load) and it stays unchanged even though addeventsource and rerenderevents methods are called.
according to the docs you need to put array of events to the addEventSource function
event must be an Event Object with a title and start at the very
least.
var events=new Array();
var numberofevents = this.serviceVariableGetDates.getTotal();
for (i=0;i<numberofevents;i++)
{
//alert("numbrr:" + i);
var dates=this.serviceVariableGetDates.getItem(i);
console.log(dates.getData());
var start_date = dates.getValue("c0");
var end_date = dates.getValue("c1");
var event_name = dates.getValue("c2");
//var EventEntry = [ 'title: '+ event_name, 'start: '+ start_date,'end: '+ end_date ];
event = new Object();
event.title = event_name; // this should be string
event.start = start_date; // this should be date object
event.end = end_date; // this should be date object
event.color = "blue";
event.allDay = false;
this.label1.setCaption(start_date);
//EventArray.push(EventEntry);
console.log(events['title']);
events.push(event);
}
$('#calendar').fullCalendar('addEventSource',events);
//$('#calendar').fullCalendar('rerenderEvents');
Hope this will help!
Let's say I have:
var directions = [ "name", "start_address", "end_address", "order_date" ];
I'm trying to find a slick, fast way to turn that array into this:
data: {
"directions[name]" : directions_name.val(),
"directions[start_address]" : directions_start_address.val(),
"directions[end_address]" : directions_end_address.val(),
"directions[order_date]" : directions_order_date.val()
}
Notice the pattern. The name of the array "directions" is the prefix to the values.
I'm interested how people can either do this or at least suggest a way for me to try.
Any tips would be appreciated.
Thanks!
EDIT **
Thanks for the suggestions so far. However, I forgot to mention that the array "directions" needs to be dynamic.
For example, I could use:
places = ["name", "location"]
should return
data: {
"places[name]" : places_name.val(),
"places[location]" : places_location.val()
}
alpha = ["blue", "orange"]
should return
data: {
"alpha[blue]" : alpha_blue.val(),
"alpha[orange]" : alpha_orange.val()
}
So basically I could just pass an array into a function and it return that data object.
var directions = ["name", "start_address", .... ];
var data = someCoolFunction( directions );
Hope that makes sense.
** EDIT **************
I want to thank everyone for their help. I ended up going a different route. After thinking about it, I decided to put some meta information in the HTML form itself. And, I stick to a naming convention. So that an HTML form has the information it needs (WITHOUT being bloated) to tell jQuery where to POST the information. This is what I ended up doing (for those interested):
// addBox
// generic new object box.
function addBox(name, options) {
var self = this;
var title = "Add " + name.charAt(0).toUpperCase() + name.slice(1);
var url = name.match(/s$/) ? name.toLowerCase() : name.toLowerCase() + "s";
allFields.val(""); tips.text("");
$("#dialog-form-" + name).dialog( $.extend(default_dialog_options, {
title: title,
buttons: [
{ // Add Button
text: title,
click: function(){
var bValid = true;
allFields.removeClass( "ui-state-error" );
var data = {};
$("#dialog-form-" + name + " input[type=text]").each(function() { // each is fine for small loops :-)
var stripped_name = this["name"].replace(name + "_", "");
data[name + "[" + stripped_name + "]"] = $("#dialog-form-" + name + " #" + name + "_" + stripped_name).val();
});
// verify required fields are set
$("#dialog-form-" + name + " input[type=text].required").each(function() {
bValid = bValid && checkLength( $(this), $(this).attr("name").replace("_", " "), 3, 64 );
});
// find sliders
$("#dialog-form-" + name + " .slider").each( function() {
data[name + "[" + $(this).attr("data-name") + "]"] = $(this).slider( "option", "value" );
});
data["trip_id"] = trip_id;
if(options != null) { $.extend(data, options); } // add optional key/values
if(bValid) {
$(".ui-button").attr("disabled", true);
$.ajax( { url : "/" + url, type : "POST", data : data } );
}
}
},
{ text: "Cancel", click: function(){$( this ).dialog( "close" );} }
]
}));
}
It's really unclear what you want here. Perhaps you should give the interface to the function you want, and an example of some code which sets up some sample variables and calls the function.
What you seem to be asking for is to dynamically find variables which you have already declared in the environment, such as directions_name and directions_start_address, and call the val() method on each of them, then construct a dictionary mapping strings to those results. But the keys of the dictionary contain JavaScript syntax. Are you sure that's what you want?
function transform(name)
{
var data = {};
var names = window[name];
for (var i=0; i<names.length; i++)
{
data[name + "[" + names[i] + "]"] = window[name + "_" + names[i]].val();
}
return data;
}
Edit: To use JQuery to look up objects by ID instead of the above approach (which looks up global variables by name):
function transform(name)
{
var data = {};
var names = $("#" + name);
for (var i=0; i<names.length; i++)
{
data[name + "[" + names[i] + "]"] = $("#" + name + "_" + names[i]).val();
}
return data;
}
This will look up the name in the global space of the window (which will work in a browser anyway). You call that function with "directions" as the argument. For example:
var directions = [ "name", "start_address", "end_address", "order_date" ];
var directions_name = {"val": function() {return "Joe";}};
var directions_start_address = {"val": function() {return "14 X Street";}};
var directions_end_address = {"val": function() {return "12 Y Street";}};
var directions_order_date = {"val": function() {return "1/2/3";}};
data = transform("directions");
Is that what you want?
(Note: I see someone else posted a solution using $ and "#" ... I think that's JQuery syntax, right? This works without JQuery.)
Edit: Note that this lets you use a dynamic value for "directions". But I'm still not sure why you want those keys to be "directions[name]", "directions[start_address]", instead of "name", "start_address", etc. Much easier to look up.
Edit: I fixed my sample code to use functions in the values. Is this really what you want? It would be easier if you weren't calling val() with parens.
Like this:
var data = { };
for(var i = 0; i < directions.length; i++) {
var name = directions[i];
data["directions[" + name + "]"] = $('#directions_' + name).val();
}
Or,
data["directions[" + name + "]"] = directionsElements[name].val();
EDIT: You can pass an array and a prefix.