I'm hoping to find some help retrieving the Google Calendar attachment fileId through FullCalendar v2.9.0.
The Google calendar is dedicated to this project and is public as is the Drive folder containing image files which are the attachments - one image per event. I am new to the details of Javascript, but I've searched and researched quite a bit. I have not been able to find an example or tutorial that is close enough to what I'm trying to do that I can understand.
My project involves a month view FullCalendar where a user clicks an event, the event background highlights and a sidebar div populates with title, dateTime, description, location, and attachment image file. The user would browse through multiple events with the sidebar updating accordingly.
Here's what I've done so far:
The Google calendar elements are populating and updating correctly except for the image attachment.
I can hard code the HTML in gcal.html with a URL appended with the attachment fileId and get the image in the sidebar. The image, of course, is static though.
Using Google API Explorer calendar.events.get and entering calendarId, eventId, and simple API key, all of which are retrievable from FullCalendar, API Explorer returns the attachment fileId,
(Note that the fileUrl below works in a browser pulling up a viewer, but does not work in my HTML. This one does though: "https://drive.google.com/uc?export=view&id=0B5Nk_tOCzCISaWdqYy1DYnF6SzA")
From API Explorer
Execute without OAuth
calendar.events.get executed 16 minutes ago time to execute: 282 ms
Request
GET https://www.googleapis.com/calendar/v3/calendars/c6kag4dlhqs7m160s3t3lfggak%40group.calendar.google.com/events/u9a5fuoqkfmkm2c20vpn75krf4?fields=attachments(fileId%2CfileUrl)&key={YOUR_API_KEY}
Response
200
- Show headers -
{
"attachments": [
{
"fileUrl": "https://drive.google.com/file/d/0B5Nk_tOCzCISaWdqYy1DYnF6SzA/view?usp=drive_web",
"fileId": "0B5Nk_tOCzCISaWdqYy1DYnF6SzA"
}
]
}
I've tried what seems like endless combinations of statements, basically guessing, at the right syntax in a gcal.html eventClick function. I've also been trying to add code to events.push in gcal.js. This is the only place where I can find other event elements referenced.
My current setup with "alert(event.fileid);" under eventClick ingcal.html and "attachments: entry.fileid" under events.push in gcal.js returns an alert "undefined". In gcal.js a few lines down I notice "successArgs = [ events ].concat(Array.prototype.slice.call(arguments, 1)); // forward other jq args". I'm wondering if FullCalendar returns all fields for an event?
gcal.html (head)
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../fullcalendar.css' rel='stylesheet' />
<link href='../fullcalendar.print.css' rel='stylesheet' media='print' />
<script src='../lib/moment.min.js'></script>
<script src='../lib/jquery.min.js'></script>
<script src='../fullcalendar.min.js'></script>
<script src='../gcal.js'></script>
<script>
$(document).ready(function() {
$('#calendar').fullCalendar({
eventLimit: 4,
googleCalendarApiKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
events: {
googleCalendarId: 'c6kag4dlhqs7m160s3t3lfggak#group.calendar.google.com'
},
//$('#calendar'.fullCalendar( 'clientEvents' [, filter ] )
// Need to highlight next upcoming event on page load
// var filter = (events, event.start > getdate(new))
eventClick: function(event, events ) {
alert(event.fileid);
//eventRender: function(event, element, view) {
// Need to reset highlight on prevoiusly clicked event
// element.css('background-color', '#5777c8');
//}
//$('#calendar').fullCalendar( 'updateEvents' );
$( "#sidebar2" ).html(event.title);
$( "#sidebar3" ).html(event.start.format('dddd MMM. Do'));
$( "#sidebar4" ).html(event.start.format('h:mm a'));
$( "#sidebar5" ).html(event.description);
$( "#sidebar6" ).html(
'<a style= color:#a2cadc; href=http://' +
event.location + ' target="_blank">' +
"More Band Info" + '</a>'
);
$(this).css('background-color', '#5777c8');
return false;
console.log
},
loading: function(bool) {
$('#loading').toggle(bool);
}
});
});
</script>
gcal.js (line 122 to end)
return $.extend({}, sourceOptions, {
googleCalendarId: null, // prevents source-normalizing from happening again
url: url,
data: data,
startParam: false, // `false` omits this parameter. we already included it above
endParam: false, // same
timezoneParam: false, // same
success: function(data) {
var events = [];
var successArgs;
var successRes;
if (data.error) {
reportError('Google Calendar API: ' + data.error.message, data.error.errors);
}
else if (data.items) {
$.each(data.items, function(i, entry) {
var url = entry.htmlLink || null;
// make the URLs for each event show times in the correct timezone
if (timezoneArg && url !== null) {
url = injectQsComponent(url, 'ctz=' + timezoneArg);
}
events.push({
id: entry.id,
title: entry.summary,
start: entry.start.dateTime || entry.start.date,
// try timed. will fall back to all-day
end: entry.end.dateTime || entry.end.date, // same
url: url,
location: entry.location,
description: entry.description,
attachments: entry.fileid // tryiing to find the attachment reference
});
});
// call the success handler(s) and allow it to return a new events array
successArgs = [ events ].concat(Array.prototype.slice.call(arguments, 1));
// forward other jq args
successRes = applyAll(success, this, successArgs);
if ($.isArray(successRes)) {
return successRes;
}
}
return events;
}
});
}
// Injects a string like "arg=value" into the querystring of a URL
function injectQsComponent(url, component) {
// inject it after the querystring but before the fragment
return url.replace(/(\?.*?)?(#|$)/, function(whole, qs, hash) {
return (qs ? qs + '&' : '?') + component + hash;
});
}
});
Any help to get this working would be greatly appreciated.
Related
Previous post which lead to this issue: Fullcalendar using resources as a function with select menu
Based on my previous post, I have an issue using fullcalendar 4. When I am using resources as a function, my all-day blocks do not line up with my scheduler time slots. You can see it in the picture.
Here's my resources function:
resources: function(fetchInfo, successCallback, failureCallback) {
// Filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
successCallback(filteredResources);
},
Here's my toggleresource function:
// menu button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
visibleResourceIds = [];
//if select all... see if undefined from loading on initial load = true
if ((resourceId == '') || (resourceId === undefined)) {
$.map( resourceData, function( value, index ) {
visibleResourceIds.push(value.id);
});
}
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
Other related code (when the menu changes, the resources of the selected menu item show only in fullcalendar):
var resourceData = [];
var visibleResourceIds = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
});
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
The resources show/hide just fine based on the selected menu resource, but look at the allday blocks - they don't line up after the resources are refetched for some reason. They correct themselves as the user navigates the scheduler though!
UPDATE BELOW
After looking around it looks like when refetchevents is called, the class .fc-week loses the following css:
style="border-right-width: 1px; margin-right: 20px;"
Here's a full pic of the calendar on initial load:
After I click a one of the navigation arrow, the all-day lines meet up with the rest of the calendar times because that style is applied to .fc-week.
I don't have any special css applied to the calendar and I am not using any themes that would get rid of this: at least not that I see now.
Here's the html that houses the calendar:
<div class="portlet-body">
<div class='loader'></div>
<div class="row">
<div id="calendar_full" style="padding-left: 10px; padding-right: 15px;"></div>
</div>
</div>
In order to fix this, I can add this following line after the resources are refetched in my toggleResources function:
$('#calendar_full .fc-week').css('border-right-width', '1px').css('margin-right', '20px');
I am going to keep looking as to why this css disappears after the resources are refetched. I wonder if it could be a glitch?
In my Next.js app, I'm setting the <title> tag for individual pages using the recommended method:
import Head from 'next/head'
export default () => <>
<Head><title>My page title</title></Head>
</>
Here's the problem: when the history change event fires, the value of document.title doesn't always match the current URL.
You can test it yourself:
Router.events.on("routeChangeComplete", () => {
if ('browser' in process) {
console.log('--------');
console.log(window.location.href);
console.log(document.title);
}
});
Navigating between pages, you should observe that URL & title are often mismatched. The value of URL is always right, but the value of title is all over the place. It can have:
the right value
the value it had on the previous page
no value at all
This is an issue when using analytics, specifically GTM - Google Tag Manager, which uses the current URL & page title to uniquely identify visited pages.
I've had this issue with Next.js 7, and upgrading to 8 hasn't fixed it.
Do you know of any way to solve this problem? Maybe delaying the history change event until the first render of a component under /pages/?
Thanks!
This is a small hack. setTimeout(()=>{console.log(document.title)}, 0)
I found a work-around by intercepting events sent to window.dataLayer.push and adding a one-second delay in case the event is gtm.historyChange.
Here's my GtagScript component that I'm adding to <Head> under _document.js:
export const GtagScript = () => {
function intercept() {
const scriptTag = document.querySelector('#gtm-js');
if (scriptTag !== null)
scriptTag.addEventListener('load', () => {
window.dataLayer.pushOrig = window.dataLayer.push;
window.dataLayer.push = (e) => {
if (e.event === 'gtm.historyChange') {
setTimeout(function () {
window.dataLayer.pushOrig(e);
console.log(`URL: ${window.location.href} Title: ${document.title}`);
}, 1000);
} else {
window.dataLayer.pushOrig(e);
}
};
});
}
return <>
<script
id="gtm-js"
async
src={`https://www.googletagmanager.com/gtm.js?id=${GA_TRACKING_ID}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}');
${intercept.toString()}
intercept();`
}}
/>
</>
};
I'll wait a while to see if an official fix comes from the ZEIT team. My solution doesn't actually answer the question. It doesn't set <title>, it just defers the event, which isn't optimal.
You can use setInterval
const firstPageViewEvent = setInterval(() => {
if (document.title) {
// send pageview event
clearInterval(firstPageViewEvent);
}
}, 200);
I have a header.php, that appears in every single page on my blog, with a navbar that looks like this:
<nav>
<ul>
<li>Logistics</li>
<li>Contact</li>
</ul>
</nav>
But when I click the anchor tag linking to #contact, which is located in page with id 5, as you can see by the php code, nothing happens. I tried using a slash (/#contact) but I keep getting the same behavior. Isn't this the correct way of linking to a specific id on another page?
EDIT: I also have some smooth scrolling code (below) which I think may be related to my issue.
<script>
$( document ).ready( function () {
// Add smooth scrolling to all links
$( "a" ).on( 'click', function ( event ) {
// Make sure this.hash has a value before overriding default behavior
if ( this.hash !== "" ) {
// Prevent default anchor click behavior
event.preventDefault();
// Store hash
var hash = this.hash;
// Using jQuery's animate() method to add smooth page scroll
// The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
$( 'html, body' ).animate( {
scrollTop: $( hash ).offset().top
}, 800, function () {
// Add hash (#) to URL when done scrolling (default click behavior)
window.location.hash = hash;
} );
} // End if
} );
} );
</script>
Thanks!
You have a javascript error.
<script>
$( document ).ready( function () {
// Add smooth scrolling to all links
$( "a" ).on( 'click', function ( event ) {
// Make sure this.hash has a value before overriding default behavior
if ( this.hash !== "" ) {
// Prevent default anchor click behavior
event.preventDefault();
// Store hash
var hash = this.hash;
// Using jQuery's animate() method to add smooth page scroll
// The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
$( 'html, body' ).animate( {
scrollTop: $( hash ).offset().top
}, 800, function () {
// Add hash (#) to URL when done scrolling (default click behavior)
window.location.hash = hash;
} );
} // End if
} );
} );
</script>
Here is the problem. On the home page you have a div with id #contato. On the other pages it is not there.
$( 'html, body' ).animate( {
scrollTop: $( hash ).offset().top
}, 800, function () {
// Add hash (#) to URL when done scrolling (default click behavior)
window.location.hash = hash;
} );
Your hash var's value is "#contato", but when js tries to get it's offset you recieve an error saying that it's impossible to read property top of 'undefined'. So, you can try to remove that part of the script from the other pages and only keep it on home. This should work like a charm. If you have any question, please ask.
LE: I'd recommend this method for smooth scroll (I didn't tested it for your case, but it always worked for me)
$(function() {
$('a[href*="#"]:not([href="#"])').click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('html, body').animate({
scrollTop: target.offset().top
}, 1000);
return false;
}
}
});
});
use
<?php echo get_page_link(5)."#contact"; ?>
instead of get permalink function
When I use ajax, I noticed that Jquery effects don't work. The reason is "The new HTML you're adding to the DOM (page) didn't exist when your jquery ran the first time "
another similar question
Therefore, I changed my code to following but still I don't get the jquery effects.
Where I have done the mistake?
Previous code
$("#sendemp").click(function(e) {
e.preventDefault();
var submit_val = $("#searchbox").val();
//alert('submitval is ' + submit_val);
$.ajax( {
type : "POST",
//dataType :"jason",
url : "./wp-admin/admin-ajax.php",
data : {
action : 'employee_pimary_details',
user_name : submit_val
},
success : function(data) {
// alert('hhh');
$('#accordion21').html(data);
// $( "#searchbox" ).autocomplete({
// source: data
// });
}
});
});
New code
$("button").on( "click", "#accordion3",function(){
$.ajax( {
type : "POST",
dataType : "json",
url : "./wp-admin/admin-ajax.php",
data : {
action : 'employee_deatils_search',
user_name : submit_val
},
success : function(data) {
// alert('hhh');
$('#accordion3').html(data);
$("tr:odd").css("background-color", "#F8F8F8");
$("#accordion3").accordion({ heightStyle: "fill", active: 0 });
// $( "#searchbox" ).autocomplete({
// source: data
// });
}
});
} );
I have following submit button
<input type="submit" id="sendemp" value="Search" />
I don't think your click binding is correct, if you want to handle clicks on button inside #accordion3 change it to:
$("#accordion3").on( "click", "button",function(){...});
It is hard to tell without your html, but it looks like in your old code you are replacing the sendemp button. In your new code your event delegation is incorrectly specified. You are applying delegation to a button element (which doens't exist since your sendemp button is an input element).
Apply delegate to something that is the parent of #sendemp like so:
$('body').on('click', '#sendemp', function() {
// your ajax call
});
I could fix the issue, I tried the above solution that is using on method. However, it doesn't make sense to my problem.
As following artical explain I think, Accordion is already instantiated and effects are persistance. When it is called second time, it won't create again since there is already the effects.
Therefore, I needed to destroy it and recreate accordion.
support link
I changed the code on success as follows
success : function(data) {
// alert('hhh');
$('#accordion3').accordion('destroy');
$('#accordion3').html(data);
$("tr:odd").css("background-color", "#F8F8F8");
//$("#accordion3").accordion( "disable" );
$("#accordion3").accordion({ active: 0 });
}
And out of $(document).ready(function() {
I added
$(function() {
$("#accordion3").accordion({
// heightStyle: "fill",
active: 0 });
});
I have the following situation with an autocomplete plugin on an .aspx page. It is working fine. The result from the autocomplete search yields a product id and a product description is concatenated with it (i.e. 2099 -- A Product). I know that I need to use split() with this but where do I put it? I'm still rather new to jQuery and javascript.
$(document).ready(function() {
$('.divAutoComplete').autocomplete("LookupCodes.aspx?type=FC", {
mustMatch: true
});
});
If it's the same autocomplete I've used (by Tomas Kirda) you should be able to add an onSelected event like so:
$(document).ready(function() {
$('.divAutoComplete').autocomplete("LookupCodes.aspx?type=FC", {
mustMatch: true,
onSelect: function(value, data) { autoCompleteSelected(value, data); }
});
});
function autoCompleteSelected(value, data) {
var parts = data.split("--");
... do something with parts
}
Obviously, if it's not the then it will have different events
In JavaScript, any string can be split using the split function like so:
"Pandas enjoy tasty bamboo".split(' ')
The above splits the string on spaces returning the following array:
["Pandas", "enjoy", "tasty", "bamboo"]
Any string can be fed to the split function and it'll cope with multi-character strings just fine.
Now as for your question with the jQuery autocomplete plugin, you'll need to have your .aspx page return a JS array of options in order for it to work. Alternatively, you can load the data some other way and then pass an array to autocomplete. If the server returns an array like the following then you can pass it directly:
["1234 -- Chicken", "4321 -- Noodle", "1432 -- Irrational Monkeys"]
The point is that autocomplete uses an array for matching.
The docs for the autocomplete plugin seem decent enough.
do this code for splitting
<script type="text/javascript">
$(function() {
var availableTags = ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "perl"];
function split(val) {
return val.split(/,\s*/);
}
function extractLast(term) {
return split(term).pop();
}
$("#tags").autocomplete({
minLength: 0,
source: function(request, response) {
// delegate back to autocomplete, but extract the last term
response($.ui.autocomplete.filter(availableTags, extractLast(request.term)));
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join(", ");
return false;
}
});
});
</script>