Friday, March 23, 2012

Refresh the Parent showModalDialog

There are many blogs showing how to refresh the parent window when returning from a SharePoint modal window using SP.UI.ModalDialog.showModalDialog.  However, I could not find one that worked adequately  for my needs.  The use case discussed here is for launching an application page within a modal.  When the application page is submitted, the parent window should refresh.  When the modal is closed (i.e. x'd out of from upper right of the window), the parent page should not refresh.  In addition, the submit happens within an SPLongOperation using block in the code behind of the application page.  For some reason, calling the following at the end of the using statement did not work.


  operation.EndScript("window.frameElement.commitPopup();"); 


Hence the fix was performed in the JavaScript where the modal was originally launched from.  A callback function was added to the options of SP.UI.ModalDialog.showModalDialog.  This code is as follows:


  function refreshParent(result, target) {
    window.parent.location = window.location.href;
  }


The problem with this was that when the modal was canceled out of (i.e. not submitted), the parent would still refresh.  Although this is not the end of the world, it clearly lessens the user's experience.  By adding a check for the result, this can be remedied.  The entire script is as follows:


  function launchModal(url) {
    var options = SP.UI.$create_DialogOptions();
    options.url = url;
    options.dialogReturnValueCallback = Function.createDelegate(null, refreshParent);
    SP.UI.ModalDialog.showModalDialog(options);
  }


  function refreshParent(result, target) {
    if(result == 1) window.parent.location = window.location.href;
  }


Now the parent window refreshes as expected.

Monday, March 19, 2012

Get the Selected Date/Time within a SharePoint 2010 Calendar using jQuery

The selected date in a SharePoint 2010 calendar may be needed when writing custom solutions. This can be retrieved using JavaScript and jQuery.  First, we need to determine if we are in the month, week, or day scope of the calendar.  There is probably a better way to do this, but the following seems to work:



// make these global
var selectedDate = 'no date selected';
var selectedTime = 'no time selected';


// put the rest of the code in a function
function GetSelectedDate() {

  var dayScope = $('table.ms-acal-detail');
  var weekScope = $('table.ms-acal-detail');
  var monthScope = $('table.ms-acal-month');


  var isDayScope = dayScope.length > 0 ? dayScope.attr('summary').indexOf('Daily') > -1 : false;
  var isWeekScope = weekScope.length > 0 ? weekScope.attr('summary').indexOf('Weekly') > -1 : false;
  var isMonthScope = monthScope.length > 0 ? monthScope.attr('summary').indexOf('Monthly') > -1 : false;


Now lets get the selected element:


var selecteddateelement = $('.ms-acal-vitem');


Now that we know which view we are in, and have the selected element, we can determine the selected date if we are in the monthly scope.


  if(isMonthScope) {
var tditem = selecteddateelement.parent();
var tritem = selecteddateelement.parent().parent();
var prevtr = tritem.prev();
var indx = tritem.children().index(tditem) + 2;
var dttd = prevtr.children(':nth-child(' + indx + ')');
if(selecteddateelement.length > 0) selectedDate = dttd.attr('date');
  }

Next lets handle the weekly  scope .

  else if(isWeekScope) {
var weektritem = selecteddateelement.parent();
var weekdayindx = weektritem.children().index(selecteddateelement) + 1;
var weekselectedhalfhourstarttime = $('[class^=ms-acal-hour]').index(weektritem) / 2;
var weekdttd = $('.ms-acal-week-top').children(':nth-child(' + weekdayindx + ')');
if(weekdttd.length > 0) selectedDate = weekdttd.attr('date');
if(weekselectedhalfhourstarttime >= 0) selectedTime = weekselectedhalfhourstarttime;
  }

And finally the daily  scope .

  else if(isDayScope) {
var verbosedaydate = $('.ms-acal-display').text();
var daydatesplit = verbosedaydate.split(/,| /);
var month = daydatesplit[1];
var dayscopemonth = (new Date()).getMonth() + 1; // default to current month


if(month == "January") dayscopemonth = 1;
else if(month == "February") dayscopemonth = 2;
else if(month == "March") dayscopemonth = 3;
else if(month == "April") dayscopemonth = 4;
else if(month == "May") dayscopemonth = 5;
else if(month == "June") dayscopemonth = 6;
else if(month == "July") dayscopemonth = 7;
else if(month == "August") dayscopemonth = 8;
else if(month == "September") dayscopemonth = 9;
else if(month == "October") dayscopemonth = 10;
else if(month == "November") dayscopemonth = 11;
else if(month == "December") dayscopemonth = 12;


var dayscopeday = daydatesplit[2];
var dayscopeyear = daydatesplit[3];


selectedDate = dayscopemonth + '/' + dayscopeday + '/' + dayscopeyear;


var daytr = selecteddateelement.parent();
var dayselectedhalfhourstarttime = $('[class^=ms-acal-hour]').index(daytr) / 2;
if(dayselectedhalfhourstarttime >= 0) selectedTime = dayselectedhalfhourstarttime;
  }

Now the selectedDate and selectedTime will be populated.  Echo these to the console to see the values:

  console.log("selectedDate:    " + selectedDate); // change this to do something with the date
  console.log("selectedTime:    " + selectedTime); // change this to do something with the time
  // end the function
}

The selectedDate and selectedTime variables should be global variables.  This allows the preceding code to be put into a function, say "GetSelectedDate()" and bound to a click event on the calendar.  The issue with not doing this is if you are trying to call this code from a button or anchor click outside of the calendar, when clicked, first the calendar looses focus, then the ms-acal-vitem class is stripped.  The result is that nothing is selected when the code would run, coming back with "no date selected" every time.  Binding to click would look like this:



$(document).ready(function () {
$('.ms-acal-rootdiv').unbind("click").click(function () {
GetSelectedDate();
});
});



Doing so will set the global variables selectedDate and selectedTime every time a click is made.  Another thing you may want to do is create a function to set these variables to the current date and time.  This could be called first, then the preceding code, resulting in selectedDate and selectedTime always having a date populated.


It seems like a long way to go to get the selected date, and does not handle localization.  I would love to see what others are doing to solve this issue.  I know there are some xslt solutions out there too.


Cheers!