// ---------------------
// INIT GLOBALS VARS
// ---------------------
var gDebug_On = false; // Debug mode - true for development, false for production
var gBeforeLimit = 100; // limit on number of list events retrieved before target date
var gAfterLimit = 100; // limit on number of list events retrieved after target date
var g_rpt_master_id; // repeat master id of event being displayed, edited
var g_cw_id; // event occurrence id of event being displayed or edited
var gInitSerialize; // will hold serialized from data for ajax post
var gPHPSESSID = "?PHPSESSID=" + '75q801l80pn17jrbecslakf192'; // php session id for maintaining state
var gbanners_on = ""; // banners enabled flag
var gCalendar = ""; // calendar name / identifier
var gCalendarId = ""; // calendar database id
var gMulticat_on = ""; // multiple category in category filter flag
var gSkip_all = ""; // skip all option in category filter flag
var gLoggedIn; // user is logged in flag
var gCanAdd; // user can add events flag
var gIsAdmin; // user is calendar admin flag
var gIsManager; // user is category manager flag
var gNumCatsCalendar; // Total number of categories in calendar
var gNumCatsUser; // Number of categories viewable by user
var gData; // data returned from ajax
var cw_jsonStatus; // gData.status object returned from ajax
var monthEvents; // object with month days having events and event count
var gCameFromMonth = true; // flag that action came from month view
var gCameFromDetail = false; // flag that action came from detail view
var gCameFromUpdate = false; // flag that action came from update
var gRefreshAfterEdit = false; // flag that event save was done, help reduce list refresh
var gGetEventsDirection = 'beforeandafter'; // List view scroll lazy load direction (before, after and beforeandafter)
var gNoEventsBefore = false; // flag that no events were found on lazy load before
var gNoEventsAfter = false; // flag that no events were found on lazy load after
var gDestinationScreen; // contains destination screen (#edit only MAY NOT BE ACTIVE AT THIS TIME)
var introNoteMaxLength = 800; // limit for number of characters allowed in email note
var jRemindBodyDIV; // object reference to #reminderform
var gNoPublicCats = false; // flag if calendar has no public categories
var setpassword = false;
var last_saved_location_id = 0;
var btnclicked = 'none'; // Used to detect button mousedown event when blur event preceeds click
// From calendar options
var gIsMilitary; // flag for calendar in military (24hr) mode
var gLocationPrefix; // prefix for location display (e.g. @)
var gTimeZone; // text timezone specifier
var gTimeZoneOffset; // numeric timezone offset (e.g. -5 for EST)
var gEventTitleGoMore; // flag for touch event in list go to more info url
var gEventTitleGoAction; // action on going to more info link (e.g. target='_blank')
var gCalShowBusy; // flag to show events as busy if not allowed to edit
var gCalShowBusyText; // text to show in event title when show busy is on
var gBannersOn; // flag for banner events enabled in calendar
// Used to detect change in date for events list display
var prior_occ_event_day = '';
var prior_occ_event_month = '';
var prior_occ_event_year = '';
// Today's date vars
var today = new Date();
var todaydom = today.getDate();
var todaymonth = today.getMonth();
var todayyear = today.getFullYear();
var todaydatestr = ""+todayyear+('0' + (todaymonth+1)).slice(-2)+('0' + (todaydom)).slice(-2);
// Init current view as calendar grid with list under
// User can select 'list' view via icon touch
var currentView = 'calendar';
var gselectedDateClass = ''; // class of selected date (enabled if events on day, disabled if no events on day)
var gtodayDateClass = '';
var cal; // will become the calendar object (e.g. cal.date)
var touchCapable = false;
// Init element heights. May be reset by setElementHeights()
var navTodayAddLogInOutht = 35;
var calendarht = 272 + 8; // plus 8 for margin
var listportraitfullht = window.innerHeight - navTodayAddLogInOutht - 10;
var listlandscapefullht = window.innerWidth - navTodayAddLogInOutht;
var listcalendarht = window.innerHeight - navTodayAddLogInOutht - calendarht - 30;
var listcurrentfullht = listportraitfullht;
// Init center of grid vars
var gGridCenterX = 0;
var gGridCenterY = 0;
var gOrientation = 'portrait'; // Init as portrait
setOrientation(); // Set orientation var based on initial orientation
var gCurrentPlace = "events_cal_or_list"; // Init as calendar or list view
var calendarSwipe = false;
var dayListSwipe = false;
var fullListSwipe = false;
var gTimeLastAjax = new Date().getTime(); // Init - holds last time in msecs that server was called used for testing stale session
// Fields to populate with values in edit event form
var fieldsToPopulate = {"edittype":"input",
"approved":"input",
"rpt_last_flag":"input",
"reminder_id":"input",
"repeat_id":"input",
"event_id":"input",
"calendar_id":"input",
"actiontype":"input",
"imported":"input",
"create_user_id":"input",
"edit_user_id":"input",
"editfrom":"input",
"draft":"input",
"old_draft":"input",
"occ_changed":"input",
"old_occ_changed":"input",
"createtimestamp":"input",
"title":"input",
"allday":"checkbox",
"noend":"checkbox",
"rpt_sun":"checkbox",
"rpt_mon":"checkbox",
"rpt_tue":"checkbox",
"rpt_wed":"checkbox",
"rpt_thu":"checkbox",
"rpt_fri":"checkbox",
"rpt_sat":"checkbox",
"rpt_noenddate":"checkbox",
"description":"textarea",
"privatenote":"textarea",
"login_email":"span",
"category_id":"select",
"location_id":"select",
"location_name":"input",
"location_street":"input",
"location_city":"input",
"location_stateprovince":"input",
"location_zippostal":"input",
"location_country":"input",
"emonth":"select",
"eday":"select",
"eyear":"select",
"starthour":"select",
"startminute":"select",
"startampm":"select",
"endhour":"select",
"endminute":"select",
"endampm":"select",
"priortime":"select",
"rpt_type":"select",
"rpt_days":"select",
"rpt_weeks":"select",
"rpt_month_place":"select",
"rpt_end_month":"select",
"rpt_end_day":"select",
"rpt_end_year":"select"};
// ----------------------------
// END OF INIT GLOBAL VARS
// ----------------------------
// Function to set orientation global variable when called by orientation event listener
function setOrientation() {
if (Math.abs(window.orientation) === 90) {
// Landscape
gOrientation = 'landscape';
listcurrentfullht = listlandscapefullht;
$("#loginlogodiv").hide();
} else {
// Portrait
gOrientation = 'portrait';
$("#loginlogodiv").show();
listcurrentfullht = listportraitfullht;
}
}
function setElementHeights() {
// Get list heights in calendar and list views
navTodayAddLogInOutht = $("#navTodayAddLogInOut").height();
calendarht = $("#calendar").height() + 8; // plus 8 for margin
listportraitfullht = window.innerHeight - navTodayAddLogInOutht - 10;
listlandscapefullht = window.innerWidth - navTodayAddLogInOutht;
listcalendarht = window.innerHeight - navTodayAddLogInOutht - calendarht - 30;
listcurrentfullht = listportraitfullht;
}
// Listen for orientation changes. showCurrentView will always show list view if orientation landscape
window.addEventListener("orientationchange", function() { setTimeout(orientationChanged, 150); } );
function orientationChanged() {
setOrientation(); // Set new orientation, note: does not change currentView value
//alert('cv:'+currentView+' or: '+gOrientation+' cp: '+gCurrentPlace);
// If currently viewing calendar grid or full events list then do
// showCurrentView to set list height and refresh list if needed
if (gCurrentPlace == "events_cal_or_list") {
if (currentView == 'list') {
// If was already in list view and rotate to landscape then
// showCurrentView without refresh of list since events list
// not changed - save extra server hits
showCurrentView(currentView, false);
} else {
// Else if prior view was calendar then show current view
// with refresh of since events list changes between
// calendar and list views when orientation changes
showCurrentView(currentView, true);
}
}
if (gCurrentPlace == 'eventdetails') {
// Adjust height of event detail view div and set scroll to top
$("#event").height(listcurrentfullht - 30).scrollTop(0);
display_action_row();
} else if (gCurrentPlace == 'eventedit') {
$("#eventform").height(listcurrentfullht -30).scrollTop(0);
$("#eventform").hide();
$("#eventform").show();
display_action_row();
} else if (gCurrentPlace == 'categoryfilter') {
$("#category_filter_list").height(listcurrentfullht - 30).scrollTop(0);
display_action_row();
}
}
// Single place ot hide and show navigation bars for all views. This
// is called often to keep navigation bars visible. iPhone virtual
// keyboard keeps hiding navigation bars. Nobounce also helps keep
// navigation bars from dissappearing
function display_action_row() {
$(".action_row").hide(); // First hide all acition rows
if (gCurrentPlace == "events_cal_or_list") { // calendar or list view
// Show / hide navigation icons and links based on custom url options
if (nolognavbar == 1) {
// This used to hide entire navigation bar but now
// just hides add (+) icon and login link
$("#navTodayAddLogInOut #a1").hide();
$("#navTodayAddLogInOut #loginlink").hide();
} else if ((gCalShowBusy == 1) && (!gLoggedIn)) {
// Do not show add button if show busy set and not logged in
$("#navTodayAddLogInOut #a1").hide();
} else {
if (gLoggedIn) {
$("#loginlink").hide();
$("#logoutlink").show();
$("#navTodayAddLogInOut #a1").show();
} else {
$("#loginlink").show();
$("#logoutlink").hide();
$("#navTodayAddLogInOut #a1").hide();
}
}
// Show navigation bar
$("#navTodayAddLogInOut").show();
} else if (gCurrentPlace == "eventdetails") { // event detail view
$("#eventdetailactionrow").show();
} else if ((gCurrentPlace == "eventedit") || (gCurrentPlace == "eventadd")) { // adding or editing event
$("#eventeditactionrow").show();
} else if (gCurrentPlace == "sendemail") { // sending event email
$("#sendemailactionrow").show();
} else if (gCurrentPlace == "setreminder") { // setting event reminder
$("#setreminderactionrow").show();
} else if (gCurrentPlace == "categoryfilter") { // showing category filter
$("#categoryfilteractionrow").show();
} else if (gCurrentPlace == "chooseedit") { // choose edit occurrence or all repeats
$("#chooseeditactionrow").show();
}
// Scroll body to top in .5 seconds
setTimeout(function(){
$("body").scrollTop(0);
}, 500);
}
//##################################### Initialization ################################################
function init(){
// Display prompt to get calendar name if calendar not found
// Or calendar identifier blank - this was also fixed by giving
// templage calendars a value for calendar_redirect
if ((gCalendarId == 0) || (gCalendar == '')) {
$("#cal_id").val(gCalendar);
if(cal){
$(cal.grid).hide();
}
$("#promptCalendar").show();
$("#navTodayAddLogInOut").hide();
return false;
}
// Get calendar identifier from url into global
if(window.location.href.match(/crd=([^]+)/i)){
gCalendar = RegExp.$1;
}
// Init Touch Controls
if ('ontouchstart' in document.documentElement) {
touchCapable = true;
}
if (touchCapable) {
$("body").touchwipe({
wipeLeft: function() { swipeHandler("next"); },
wipeRight: function() { swipeHandler("prev"); },
preventDefaultEvents: false
});
$("#calendar").touchwipe({
wipeLeft: function() { calendarSwipe = true; swipeHandler("next"); },
wipeRight: function() { calendarSwipe = true;; swipeHandler("prev"); },
preventDefaultEvents: false
});
// Note: For some reason wipeUp is actually a wipe down of finger and wipeDown opposite
$("#dayevents").touchwipe({
wipeLeft: function() { dayListSwipe = true; swipeHandler("next"); },
wipeRight: function() { dayListSwipe = true; swipeHandler("prev"); },
wipeUp: function() {
//console.log('wipeUp');
// Only for full list, calendar not visible
if ($("#calendar").is(":visible")) {
return false;
}
// Only if list is scrolled to top
scrollObj = $('#dayevents');
if(scrollObj.scrollTop() == 0) {
//console.log('before');
fullListSwipe = true;
swipeHandler("before");
} else {
return false;
}
},
wipeDown: function() {
//console.log('wipeDown');
// Only for full list, calendar not visible
if ($("#calendar").is(":visible")) {
return false;
}
// Only if list is scrolled to bottom
scrollObj = $('#dayevents');
if(scrollObj.scrollTop() + scrollObj.innerHeight() >= scrollObj[0].scrollHeight) {
//console.log('after');
fullListSwipe = true;
swipeHandler("after");
} else {
return false;
}
},
preventDefaultEvents: false
});
}
// -----------------------------------
// BIND EVENTS
// -----------------------------------
// ---------------------------
// Bind Main Navigation Events
// ---------------------------
// Login link
$("#loginlink").bind("click", function(e){
if ($.active) { return false; }
logInOut(e);
});
// logout link
$("#logoutlink").bind("click", function(e){
if ($.active) { return false; }
logInOut(e);
});
// Today link
$(".todaylink").bind("click", function(){
if ($.active) { return false; }
goToToday();
});
// View List icon
$("#viewlist").bind("click", function(){
if ($.active) { return false; }
showCurrentView('list', true);
});
// View calendar icon
$("#viewcalendar").bind("click", function(){
if ($.active) { return false; }
$(".dayeventsA, .dayeventsB, .daydatedisplay, .noeventsonday").remove(); //emptying out the event list (note dayeventsB not being used)
showCurrentView('calendar', true);
});
// Add event icon
$("a.addlink").bind("click", function(e){
if ($.active) { return false; }
addEvent(e);
});
// Refresh icon
$("#refreshbtn").bind("click", function(){
if ($.active) { return false; }
refreshCurrentView();
});
// ------------------------------------------------
// Bind Event List (calendar and list views) Events
// ------------------------------------------------
// Display event detail
$("#eventstemplate").bind("click", function(e){
if ($.active) { return false; }
displayEvent(e);
});
// -----------------------------
// Bind Event Detail View Events
// -----------------------------
// Back link
$("#eventdone").bind("click", function(){
if ($.active) { return false; }
eventDone();
});
// Open email form
$("#openNewEmail").bind("click", function(e){
if ($.active) { return false; }
openNewEmail(e);
});
// Get / open reminder form
$("#getReminder").bind("click", function(e){
if ($.active) { return false; }
getReminder(e);
});
// Download event icon
$("#downloadEvent").bind("click", function(e){
if ($.active) { return false; }
downloadEvent(e);
});
// Edit event link
$("#editlink").bind("click", function(e){
if ($.active) { return false; }
editEvent(e);
});
// -----------------------------
// Bind Email Form Events
// -----------------------------
// Email cancel link
$("#emailform, #sendemailactionrow").find("[cwui=emailbtnback]").bind("click", function(){
if ($.active) { return false; }
cancelEmail();
});
// Email send link
$("#sendemail").bind("click", function(e){
if ($.active) { return false; }
sendEmail(e);
});
// Email body char counter display
$("#emailform").find("[cwui=intronote]").bind("keyup", displayCharsRemaining);
// Email clear body link
$("#emailform").find("[cwui=nointro]").bind("click", clearIntro);
// -----------------------------
// Reminder Form Events
// -----------------------------
// Reminder cancel
$("#setreminderbtnback").bind("click", function(){
if ($.active) { return false; }
gCurrentPlace = "eventdetails";
display_action_row();
jDetailBodyDIV.show();
jRemindBodyDIV.hide();
});
$("#setreminder").bind("click", function(e){
if ($.active) { return false; }
setReminder(e);
});
$("#deletereminder").bind("click", function(e){
if ($.active) { return false; }
deleteReminder(e);
});
// -----------------------------
// Edit Form Events
// -----------------------------
// Bind Editor Events
$("#chooseeditbtnback").bind("click", function(){
$("#chooseedit").hide()
gCurrentPlace = "eventdetails";
display_action_row();
jDetailBodyDIV.show();
});
$("#eventform #category_id").change(checkCatFiltered);
$("#rpt_type").change(docontrols);
// If banners on bind change of date components to doDateSync to sync dates
if (gbanners_on == 1) {
$("#eday, #emonth, #eyear, #end_eday, #end_emonth, #end_eyear").change(doDateSync);
}
// OTHER EDIT EVENT BINDS ARE BELOW ALSO
// -----------------------------
// Category Filter Form
// -----------------------------
// Bind Category Filter open
$("#filterbtn").bind("click", function(){
if ($.active) { return false; }
openFilter();
});
// Bind Category Filter cancel
$("#filterbtnback").bind( "click", function() {
if ($.active) { return false; }
cancelFilter();
});
$("#filterbtnapply").bind( "click", function() {
if ($.active) { return false; }
if (applyCategoryFilter()) {
$("#checkonecatmsg").hide();
$("#category_filter_list").hide();
showCurrentView (currentView, true);
}
});
// ****************************************************************
// End binding
// ****************************************************************
// Create global of event detail container
jDetailBodyDIV = $("#event");
// Conditional setup of html editor
if (usehtmled) {
if (typeof tinyMCE == 'undefined') {
// Send event to Google Anaytics for tracking that tinymce did not load
// This may be that cdn failed or blocked by firewall
//_gaq.push(['_trackEvent', 'editor_mobile_tinymce_notloaded', gCalendar]);
} else {
// // Description editor - NOTE: EITHER SETUP OR ONINIT CAN GO AWAY
tinyMCE.init({
selector: "#description",
theme: "modern",
width: "99%",
height: 175,
relative_urls : false,
remove_script_host : false,
forced_root_block: false,
browser_spellcheck : true,
plugins: 'lists advlist',
branding: false,
menubar: "",
toolbar: "bold italic | alignleft aligncenter alignright | bullist numlist",
autofocus: false,
setup: function(editor) {
editor.on('blur', function(e) {
display_action_row();
});
editor.on('focus', function(e) {
$(".action_row").hide();
});
}
});
}
}
$('#eventform').scroll(function() {
// Remove focus to make sure auto fill closed
$(document.activeElement).blur();
display_action_row();
});
// Click on title sets focus on title
$('#title').bind('click', function(event) {
if (event.target.id == 'title') {
$("#edit").focus();
$("#title").focus();
}
return true;
});
// Related to above commented out code. Keeping here for possible future developments
addeditdisable = 0;
// ----------------------------
// HIDE / SHOW LOCATION DETAILS
// ----------------------------
// So they don't have to keep opening more location details each time when
if ($.cookie("cw_locationdetails") == "visible") {
$("#locationdetails").show();
$("#location_details_show").hide();
$("#location_details_hide").css("display","inline-block");
}
// Event handler for showing/hiding location details
$("#location_details_control").on("click", function(event) {
if ($("#locationdetails").is(':visible')) {
$("#locationdetails").hide();
$("#location_details_hide").hide();
$("#location_details_show").css("display","inline-block");
$.cookie("cw_locationdetails", "hidden");
} else {
$("#locationdetails").show();
$("#location_details_show").hide();
$("#location_details_hide").css("display","inline-block");
$.cookie("cw_locationdetails", "visible");
}
event.stopPropagation();
});
// Event handler for location field focus
$(".locationfield").on("focus", function(event) {
btnclicked = 'none';
});
// Event handler for location field change
$(".locationfield").on("change", function(event) {
locchange();
});
// ----------------------------
// HIDE / SHOW END DATE
// ----------------------------
// So they don't have to keep opening more details each time when
// adding multiple users at a time.
if ($.cookie("cw_end_date_div") == "visible") {
$("#end_date_div").show();
$("#end_date_show").hide();
$("#end_date_hide").css("display","inline-block");
}
// Event handler for showing/hiding end date
$("#end_date_control").on("click", function(event) {
if ($("#end_date_div").is(':visible')) {
$("#end_date_div").hide();
$("#end_date_hide").hide();
$("#end_date_show").css("display","inline-block");
$.cookie("cw_end_date_div", "hidden");
} else {
$("#end_date_div").show();
$("#end_date_show").hide();
$("#end_date_hide").css("display","inline-block");
$.cookie("cw_end_date_div", "visible");
}
event.stopPropagation();
});
// ----------------------------
// HIDE / SHOW PRIVATE NOTE
// ----------------------------
// So they don't have to keep opening more private note each time when
if ($.cookie("cw_privatenote") == "visible") {
$("#privatenote, .privatenoteslabel").show();
$("#privatenote_show").hide();
$("#privatenote_hide").css("display","inline-block");
} else {
$("#privatenote, .privatenoteslabel").hide();
$("#privatenote_hide").hide();
$("#privatenote_show").css("display","inline-block");
}
// Event handler for showing/hiding private note
$("#privatenote_control").on("click", function(event) {
if ($("#privatenote").is(':visible')) {
$("#privatenote, .privatenoteslabel").hide();
$("#privatenote_hide").hide();
$("#privatenote_show").css("display","inline-block");
$.cookie("cw_privatenote", "hidden");
} else {
$("#privatenote, .privatenoteslabel").show();
$("#privatenote_show").hide();
$("#privatenote_hide").css("display","inline-block");
$.cookie("cw_privatenote", "visible");
}
event.stopPropagation();
});
// Refresh link if launched as homescreen app
if (window.matchMedia('(display-mode: standalone)').matches) {
$("#refreshbtn").show();
}
if (window.navigator.standalone === true) {
$("#refreshbtn").show();
}
// ****************************************************************
// Try logging in with cookies
// ****************************************************************
// Get possible user cookies
var usernameold = readCookie("cwUsername"); // Legacy cookie
var usernamenew = readCookie("cw_user"); // Transitional single calendar cookie
var calendar_specific_cookie_name = "cw_auth_" + $("#calendar_id").val();
var usernamemulticalcookie = readCookie(calendar_specific_cookie_name); // New multi-calendar cookie
if ((usernameold) || (usernamenew) || (usernamemulticalcookie)) {
// Cookies present so try login. getViewCategories will init calendar
loginCookies(getViewCategories);
} else {
// No cookies for login so just getViewCategories will init calendar
getViewCategories();
}
} // End of function init
// Handle touch swipes for different pages. For calendar grid this
// goes to next or previous month. For other pages it is same as
// touching on cancel link
function swipeHandler(direction){
if ($.active) { return false; }
if ($("#dayevents").is(":visible")) {
if (currentView == 'calendar') {
// These handle swipes of calendar or list below calendar
if (calendarSwipe) {
calendarSwipe = false;
cal.navMonth(direction); // Go to previous or next month
} else if (dayListSwipe) {
dayListSwipe = false;
navDay(direction); // Go to previous or next day
}
} else if (currentView == 'list') {
if (fullListSwipe) {
fullListSwipe = false;
doListScroll(direction); // Get events before or after
}
}
} else if (($("#event").is(":visible")) && (direction == 'prev')) {
$("#eventdone").click();
} else if (($("#emailform").is(":visible")) && (direction == 'prev')) {
$("#emailbtnback").click();
} else if (($("#reminderform").is(":visible")) && (direction == 'prev')) {
$("#setreminderbtnback").click();
} else if (($("#chooseedit").is(":visible")) && (direction == 'prev')) {
$("#chooseeditbtnback").click();
} else if (($("#eventform").is(":visible")) && (direction == 'prev')) {
$("#editeventbtnback").click();
} else if (($("#loginform").is(":visible")) && (direction == 'prev')) {
$("#logincancel").click();
} else if (($("#category_filter_list").is(":visible")) && (direction == 'prev')) {
$("#filterbtnback").click();
}
}
// Called when full events list is scrolled. If list is at top or bottom
// then gets last (bottom) or first (top) date in the list and then calls
// getListEvents which handles get and display of events before or after list
function doListScroll(direction) {
if ($.active) { return false; }
scrollObj = $('#dayevents');
if (direction == 'before') {
// Get lists before top of full list (i.e. past events)
var getdatestr = $("#dayevents > div[data-date]").first().attr("data-date");
var getyear = getdatestr.substring(0, 4);
var getmonth = getdatestr.substring(4, 6);
var getday = getdatestr.substring(6, 8);
getdate = new Date(parseInt(getyear), parseInt(getmonth)-1, parseInt(getday));
getListEvents(gCalendar, getdate, 'before');
}
if (direction == 'after') {
// Get lists after bottom of full list (i.e. future events)
var getdatestr = $("#dayevents > div[data-date]").last().attr("data-date");
var getyear = getdatestr.substring(0, 4);
var getmonth = getdatestr.substring(4, 6);
var getday = getdatestr.substring(6, 8);
getdate = new Date(parseInt(getyear), parseInt(getmonth)-1, parseInt(getday));
getListEvents(gCalendar, getdate, 'after');
}
// Scroll page body to top to prevent page bounce in iphones
$("body").scrollTop(0);
}
//########################## Filter Helpers #####################################
function getcatlist() {
var catlist = '';
// Categories filter
if (((typeof cidarray != "undefined") && (cidarray.length > 0)) && ($(".catpermits").length != $(".catpermits:checked").length)) {
for (var i = 0; i < cidarray.length; i++) {
catlist += '&cid[]=' + cidarray[i];
}
} else if (gNumCatsCalendar != gNumCatsUser) {
$(".catpermits").each(function(index) {
catlist += '&cid[]=' + this.value;
});
}
return catlist;
}
function getloclist() {
// Locations filter
if ((typeof lidarray != "undefined") && (lidarray.length > 0)) {
var loclist = '';
for (var i = 0; i < lidarray.length; i++) {
loclist += '&lid[]=' + lidarray[i];
}
} else {
var loclist = '';
}
return loclist;
}
//########################## Month Display #####################################
function initMonthlyDisplay(data, result){
if(data){
commonAjaxCallback(data, result);
}
// Create initial month grid display, includes call to get number of events on each day (i.e. view = n)
cal = new cwCal($("#calendar")[0], disabledDates, selectedDate, clearCache, "pleasewait");
if(cal.date == undefined){
cal.date = new Date();
}
// Open to calendar view, shows single day event list below month grid
showCurrentView('calendar', true);
}
// Gets number of events in each doy of month used to build grid with dots
function getMonthEvents(calendar, pDate){
var date = new Date(pDate);
clearCache();
date.setDate(1);
date.setDate(1 - date.getDay());
var month = (date.getMonth() + 1);
var year = date.getFullYear();
var day = date.getDate();
date.setDate(date.getDate() + 42);
var emonth = (date.getMonth() + 1);
var eyear = date.getFullYear();
var eday = date.getDate();
// Categories filter
var catlist = getcatlist();
// Locations filter
var loclist = getloclist();
$.getJSON("/cwapi.php" + gPHPSESSID + catlist + loclist, {fmt:"json", view:"n", bd:day, bm:month, by:year, ed:eday, em:emonth, ey:eyear, crd:calendar}, monthCallback, "json");
}
// Callback from getMonthEvents to save monthEvents object with event counts on each day
function monthCallback(data, result){
if(!commonAjaxCallback(data, result)){
return;
}
// Will contain list of dates with number of events in each day
monthEvents = new Object();
if (data.status.code == 200) {
// Load the dates into the monthEvents object
for(var i = 0; i < data.dateswithevents.length; i++){
var cw_event = data.dateswithevents[i]
if(cw_event.month.length == 1){
cw_event.month = "0" + cw_event.month;
}
if(cw_event.day.length == 1){
cw_event.day = "0" + cw_event.day;
}
monthEvents[parseInt(cw_event.year + cw_event.month + cw_event.day)] = cw_event;
}
setElementHeights();
} else {
// Something wrong
handleResponseError(data);
}
// Render the month grid for given date
cal.render(cal.date, true);
// Display action row here to make sure add (+) shows if applicable
// Was not showing if logged in, no cookie set, and did full refresh
display_action_row();
}
// Clears monthEvents object with number of events in each day
function clearCache(){
monthEvents = undefined;
}
//############# Display Days Events Below Grid #####################
// Get summaries of events on specific day for display in list below
// month grid. Callback displays events in list below calendar month grid.
function getDayEvents(calendar, date){
gCameFromDetail = false;
cal.date = date;
gCameFromMonth = false;
var month = (date.getMonth() + 1);
var year = date.getFullYear();
var day = date.getDate();
// Categories filter
var catlist = getcatlist();
// Locations filter
var loclist = getloclist();
$.getJSON("/cwapi.php" + gPHPSESSID + catlist + loclist, {fmt:"json", view:"s", bd:day, bm:month, by:year, ed:day, em:month, ey:year, crd:calendar}, dayCallback, "json");
}
// Display list of events for given day below month grid
function dayCallback(data, result){
$(".dayeventsA, .dayeventsB, .daydatedisplay, .noeventsonday").remove(); //emptying out the event list (note dayeventsB not being used)
$("#dayevents").height(listcalendarht);
if(!commonAjaxCallback(data, result)){
return;
}
if(data.status.code == 200 || data.status.code == 204){
$(".date").text(cal.date.format("ddd, mmm d, yyyy"));
// Get this selected date year, month, day
var sel_month = (cal.date.getMonth() + 1);
var sel_year = cal.date.getFullYear();
var sel_day = cal.date.getDate();
var sel_monthstr = ("0"+sel_month).slice(-2);
var sel_daystr = ("0"+sel_day).slice(-2);
if(data.status.code == 204){
// No events found so display message using template
var jNoEventsDiv = $("#noeventstemplate").clone(true);
// Remove id attribute from template
jNoEventsDiv.removeAttr("id");
$("#dayevents").prepend(jNoEventsDiv);
var noeventsmsg = 'No events on this day';
gNoEventsBefore = true;
jNoEventsDiv.attr("class", "noeventsonday");
jNoEventsDiv.attr("data-date", sel_year+sel_monthstr+sel_daystr);
jNoEventsDiv.find(".noeventsmsg").html(''+noeventsmsg+'')
.css("text-align","center")
.css("font-size","1.2em")
.css("background-color", "#ffffff")
.css("color","gray")
.css("margin","100px 0");
jNoEventsDiv.show();
} else {
// Loop thru events and create list for selected day
for(var i = 0; i < data.eventsummaries.length; i++){
var cw_event = data.eventsummaries[i];
jEventDiv = getEventSummaryDiv(cw_event);
// Add attribute of this occurrence date string in format accepted by js date function
// This is so we can get the occurrence date when displaying details so we can reset cal.date
jEventDiv.attr("data-date", sel_year+sel_monthstr+sel_daystr);
$("#dayevents").append(jEventDiv);
}
// Add single spacer at bottom of list - needed for full scroll to bottom on iphone
$("#dayevents").append("
");
}
$("#dayevents").scrollTop(0);
} else {
// Something wrong
handleResponseError(data);
}
}
//###################### Full List Display ######################
// Get event summary data for display in full list (i.e. no month grid)
// This is called for initial list display and also for lazy loading
// triggered by scrolling to the top or bottom of the list
function getListEvents(calendar, date, direction){
if (date == 'Invalid Date') { return false; }
// Get lazy load direction. On inital load this is 'beforeandafter'
// and can also be 'before' if scrolled to top or 'after' if scrolled
// to bottom. The flags gNoEventsBefore and gNoEventsAfter are set
// if lazy load cannot find events and flag prevents subsequent server
// hits if nothing to find.
gGetEventsDirection = direction;
// Set api parameters for each type of lazy load. Values for
// gBeforeLimit and gAfterLimit specify the maximum events to
// get from database for each direction.
if (direction == 'before') {
// If not more events before the cancel
if (gNoEventsBefore) {
$("body").scrollTop(0);
return;
}
// Get events before date
var beforelimit = gBeforeLimit;
var afterlimit = 0;
var incbdate = false; // Do not include events on cd:day since already in list
var incadate = false; // No affect since afterlimit zero, but needs a value
} else if (direction == 'after') {
// If not more events after then cancel
if (gNoEventsAfter) {
$("body").scrollTop(0);
return;
}
// Get events after date
var beforelimit = 0;
var afterlimit = gAfterLimit;
var incadate = false; // Do not include events on cd:day since already in list
var incbdate = false; // No affect since before limit zero, but needs a value
} else { // This is for direction = 'beforeandafter'
// Reset no events flags
gNoEventsBefore = false;
gNoEventsAfter = false;
// Get events before and after date (span)
var beforelimit = gBeforeLimit;
var afterlimit = gAfterLimit;
var incadate = false; // Do not include events on cd:day so no duplicates
var incbdate = true; // Include events on cd:day so get one copy only
}
gCameFromDetail = false; // Just in case not already false
// Only set new calendar date if doing full event list before and after date
// The gGetEventsDirection is only set to 'before' or 'after' for scrolling
// the events list to get more events in list on scroll but the calendar date
// for the month should not change. This prevents date getting screwed up.
if (gGetEventsDirection == 'beforeandafter') {
cal.date = date;
// Empty out the event list so nothing left over from month view
// (note dayeventsB not being used)
$(".dayeventsA, .dayeventsB, .daydatedisplay, .noeventsonday").remove();
}
gCameFromMonth = false;
var month = (date.getMonth() + 1);
var year = date.getFullYear();
var day = date.getDate();
// Categories filter
var catlist = getcatlist();
// Locations filter
var loclist = getloclist();
$.getJSON("/cwapi.php" + gPHPSESSID + catlist + loclist, {fmt:"json", view:"s",cd:day, cm:month, cy:year, bl:beforelimit, bi:incbdate, al:afterlimit, ai:incadate, crd:calendar}, listCallback, "json");
}
// Display events summaries in full list (i.e. no month grid)
function listCallback(data, result){
if(!commonAjaxCallback(data, result)){
return;
}
$("#dayevents").height(listcurrentfullht); // Make sure list div correct height
if(data.status.code == 200 || data.status.code == 204){ // Either has events (200) or not (204)
// Prepare for list generation and display
// For full rewrite of list (i.e. 'beforeandafter') we totally clear list
// and will later scroll to a given date. For 'before' and 'after' lazy
// loads we remember last object to which we will scroll after list generated
if (gGetEventsDirection == 'beforeandafter') {
var scrollElement = '';
} else if (gGetEventsDirection == 'before') {
var scrollElement = $("#dayevents div").first(); // First div ok
} else {
var scrollElement = $("#dayevents .dayeventsA").last(); // Must use class .dayeventsA so get full event div not inside divs
}
// Show list container, not updated yet
$("#dayevents").show();
// Selected date vars used below
var selmonth = ('0' + (cal.date.getMonth()+1)).slice(-2);
var selyear = cal.date.getFullYear();
var selday = ('0' + cal.date.getDate()).slice(-2);
var seldatestr = ""+selyear+selmonth+selday;
if (data.status.code == 204) { // No events found
// Get the no events message div
var jNoEventsDiv = $("#noeventstemplate").clone(true);
// Remove id attribute
jNoEventsDiv.removeAttr("id").attr("class", "dayeventsA").css("border-width","0px");
// Set data-date atrribute for scrolling
jNoEventsDiv.attr("data-date", seldatestr);
// Some styling
jNoEventsDiv.find(".noeventsmsg")
.css("text-align","center")
.css("font-size","1.2em")
.css("background-color", "#ffffff")
.css("color","gray")
.css("margin","100px 0");
var noefmsg = ''; // Init message
if ((gGetEventsDirection == 'before') && (!gNoEventsBefore)) {
// If lazy load was for past events (i.e. 'before') and
// 'before' no events found flag not already set
$("#dayevents").prepend(jNoEventsDiv);
noefmsg = 'No past events found';
if ((typeof cidarray != "undefined") && (cidarray.length > 0)) {
// Only if category filter set
noefmsg += 'in filtered ';
noefmsg += '';
noefmsg += ' categories';
}
jNoEventsDiv.find(".noeventsmsg").html(noefmsg);
gNoEventsBefore = true; // Set flag so will not duplicate later
} else if (!gNoEventsAfter) {
// If lazy load was for future events (i.e. 'after') and
// 'after' no events found flag not already set
$("#dayevents").append(jNoEventsDiv);
if (gGetEventsDirection == 'after') {
noefmsg = 'No future events found';
} else {
noefmsg = 'No events found';
}
if ((typeof cidarray != "undefined") && (cidarray.length > 0)) {
// Only if category filter set
noefmsg += 'in filtered ';
noefmsg += '';
noefmsg += ' categories';
}
noefmsg += '';
jNoEventsDiv.find(".noeventsmsg").html(noefmsg);
gNoEventsAfter = true; // Set flag so will not duplicate later
}
jNoEventsDiv.show(); // Show the no events found message div
// Scroll list to this div
var scrollTopval = $(jNoEventsDiv).parent().scrollTop() + $(jNoEventsDiv).offset().top - $(jNoEventsDiv).parent().offset().top;
$('#dayevents').animate({
scrollTop: scrollTopval
}, {
duration: 0,
specialEasing: {
width: 'linear',
height: 'easeOutBounce'
},
complete: function (e) {
}
});
} else {
// Found events so create list of event summary divs
var numberOfEvents = data.eventsummaries.length; // Total number of events found to be added to list
// Used to detect change in date for events list display
prior_occ_event_day = '';
prior_occ_event_month = '';
prior_occ_event_year = '';
for(var i = 0; i < data.eventsummaries.length; i++) { // loop thru each event
var cw_event = data.eventsummaries[i]; // cw_event now holds this on event's data
// Format event occurrence month and day as always two digits
cw_event.occ_event_month = ("0"+cw_event.occ_event_month).slice(-2);
cw_event.occ_event_day = ("0"+cw_event.occ_event_day).slice(-2);
// Create date strings so are in alpha sortable format
var occdatestralpha = ""+cw_event.occ_event_year+cw_event.occ_event_month+cw_event.occ_event_day;
var priordatestralpha = ""+prior_occ_event_year+prior_occ_event_month+prior_occ_event_day;
// Save date sring of first event
if (i == 0) { var firstoccdatestralpha = occdatestralpha; }
// If selected date is today then check if between prior and this events date.
// This means that today has no events and need to set gselectedDateClass = 'disabled'
// so we get a date div and no events on this day div for empty today date
if ((todaydatestr > priordatestralpha) && (todaydatestr < occdatestralpha)) {
gselectedDateClass = 'disabled';
}
if (gGetEventsDirection == 'before') { // This was lazy load for past events only (not 'after' and not 'beforeandafter')
// Check for change in event occurrence day from prior event
if ((cw_event.occ_event_day != prior_occ_event_day) || (cw_event.occ_event_month != prior_occ_event_month) || (cw_event.occ_event_year != prior_occ_event_year)) {
// Date changed so if this is not first event then prepend prevously created date div
// This is because we need to put old date before events for that date. Remember we are
// going back into past (prepending list)
if (i > 0) {
$("#dayevents").prepend(jDateDiv); // This is previously created data div because this is prepend (i.e. gGetEventsDirection == 'before')
jDateDiv.length = 0;
}
// Create date div for this date change, will prepend in at later date change
jDateDiv = getDateDiv(cw_event.occ_event_year, cw_event.occ_event_month, cw_event.occ_event_day);
// Save month, day and year as prior for checking date transition
prior_occ_event_month = cw_event.occ_event_month;
prior_occ_event_day = cw_event.occ_event_day;
prior_occ_event_year = cw_event.occ_event_year;
}
// If getting events before need to prepend events
jEventDiv = getEventSummaryDiv(cw_event);
// Add attribute of this occurrence date string in format accepted by js date function
// This is so we can get the occurrence date when displaying details so we can reset cal.date
jEventDiv.attr("data-date",cw_event.occ_event_year+cw_event.occ_event_month+cw_event.occ_event_day);
$("#dayevents").prepend(jEventDiv);
} else { // This is for initial load 'beforeandafter' or scroll to get 'after' for future events
// Check for change in event occurrence day from prior event
if ((cw_event.occ_event_day != prior_occ_event_day) || (cw_event.occ_event_month != prior_occ_event_month) || (cw_event.occ_event_year != prior_occ_event_year)) {
// Check if need to insert target date div with no events message
// This should only be for 'beforeandafter' full list load because
// it comes with a specific date (i.e. from month view grid)
if (gselectedDateClass.indexOf('disabled') !== -1) {
// Create date strings so are in alpha sortable format
var occdatestralpha = ""+cw_event.occ_event_year+cw_event.occ_event_month+cw_event.occ_event_day;
var priordatestralpha = ""+prior_occ_event_year+prior_occ_event_month+prior_occ_event_day;
// Does this event have different date from prior and current event (i.e. selected date
// is between two events. If so then there is no event on this selected date so create msg div to indicate
if ((gGetEventsDirection == 'beforeandafter') && (((seldatestr > priordatestralpha) && (seldatestr < occdatestralpha)) ||
((seldatestr < occdatestralpha) && (i == 0)))) {
// Create and append no events on this day div
jDateDiv = getDateDiv(selyear, selmonth, selday);
$("#dayevents").append(jDateDiv);
jDateDiv.length = 0;
var noeventonthisdayhtml = '
';
noeventonthisdayhtml += '
';
noeventonthisdayhtml += 'No events on this day
';
$("#dayevents").append(noeventonthisdayhtml);
}
// Save month, day and year as prior for checking date transition
prior_occ_event_month = cw_event.occ_event_month;
prior_occ_event_day = cw_event.occ_event_day;
prior_occ_event_year = cw_event.occ_event_year;
}
// Here we append the date change spacer
jDateDiv = getDateDiv(cw_event.occ_event_year, cw_event.occ_event_month, cw_event.occ_event_day);
$("#dayevents").append(jDateDiv);
jDateDiv.length = 0;
}
jEventDiv = getEventSummaryDiv(cw_event);
// Add attribute of this occurrence date string in format accepted by js date function
// This is so we can get the occurrence date when displaying details so we can reset cal.date
jEventDiv.attr("data-date",cw_event.occ_event_year+cw_event.occ_event_month+cw_event.occ_event_day);
$("#dayevents").append(jEventDiv);
}
} // End of loop thru all found events
if (jDateDiv.length > 0) {
if ((gGetEventsDirection == 'before')) {
$("#dayevents").prepend(jDateDiv); // This is previously created data div because this is prepend (i.e. gGetEventsDirection == 'before')
jDateDiv.length = 0;
} else {
$("#dayevents").append(jDateDiv);
jDateDiv.length = 0;
}
}
// If was not date change in list then all events on only one day
// so need to prepend date spacer above
// Get date sring of first event
var lastoccdatestralpha = occdatestralpha;
// If selected date was not shown because events did not span selected date
// then need to show selected date with no events message
if ((gGetEventsDirection == 'beforeandafter') && ((seldatestr < firstoccdatestralpha) || (seldatestr > lastoccdatestralpha))) {
// Create and append no events on this day div
jDateDiv = getDateDiv(selyear, selmonth, selday);
var noeventonthisdayhtml = '
';
noeventonthisdayhtml += '
';
noeventonthisdayhtml += 'No events on this day
';
if (seldatestr > lastoccdatestralpha) {
$("#dayevents").append(jDateDiv);
jDateDiv.length = 0;
$("#dayevents").append(noeventonthisdayhtml);
}
}
if (g_cw_id > 0) {
var scrollElement = $("#dayevents div[cw_id='"+g_cw_id+"']"); // Get div for event just viewed or edited for scroll to
if (scrollElement.length > 0) { // If found the event
// Get the scroll top value for this events div
var scrollTopval = $(scrollElement).parent().scrollTop() + $(scrollElement).offset().top - $(scrollElement).parent().offset().top;
scrollTopval = scrollTopval + 30; // Adding height of date div so shows date dive abobe event if just above event
$('#dayevents').animate({
scrollTop: scrollTopval
}, {
duration: 0,
specialEasing: {
width: 'linear',
height: 'easeOutBounce'
},
complete: function (e) {
g_cw_id = 0; // Clear the event id
}
});
} else {
// Could not find event to scroll to
}
} else if (gGetEventsDirection == 'beforeandafter') {
// This is camplete rewrite of list so scroll to current display date
scrollToSelectedDate();
} else if ((gGetEventsDirection == 'before') || (gGetEventsDirection == 'after')) {
// This was either lazy load for events before (past) or after (future)
// Use scroll element from beginning of this function
var scrollTopval = $(scrollElement).parent().scrollTop() + $(scrollElement).offset().top - $(scrollElement).parent().offset().top;
$('#dayevents').animate({
scrollTop: scrollTopval
}, {
duration: 0,
specialEasing: {
width: 'linear',
height: 'easeOutBounce'
},
complete: function (e) {
}
});
}
}
} else {
// Something wrong
handleResponseError(data);
}
}
// Create event summary div for display in lists either under calendar or full list views
// Called by listCallback for each event being displayed in list
function getEventSummaryDiv(cw_event) {
jEventDiv = $("#eventstemplate").clone(true); // Get template for event summary div
// Remove id from this event div
jEventDiv.removeAttr("id");
jEventDiv.attr("cw_id", cw_event.event_id); // Add id of this event to attribute cw_id
var dayClassName = "dayeventsA"; // For styles, dayeventsA used, dayeventsB not used
jEventDiv.attr("class", dayClassName);
jEventDiv.attr("repeat_id", cw_event.repeat_id); // Add repeat id as attribute
jEventDiv.show();
// Event title
var titletxt = cw_event.title;
if (cw_event.draft) {
titletxt = titletxt + '
draft
';
}
jEventDiv.find(".title").html(titletxt);
// Category color as vertical line
if (cw_event.category_color == "" || cw_event.category_color == "#fff" || cw_event.category_color == "#FFFFFF") {
var color = "#888";
var border = "1px solid #aaa";
} else {
var color = "#fff";
var border = "none";
}
jEventDiv.find(".category").css("background", cw_event.category_color);
// Event location
if(cw_event.location_name){
jEventDiv.find(".location").html("" + " " + cw_event.location_name);
}
else{
jEventDiv.find(".location").text("");
}
// Event date(s)
if (cw_event.isbanner == 1) {
// For banner events only
cal.banner_beg_date = new Date(parseInt(cw_event["event_year"]), parseInt(cw_event["event_month"])-1, parseInt(cw_event["event_day"])); // end date obj
cal.banner_end_date = new Date(parseInt(cw_event["end_event_year"]), parseInt(cw_event["end_event_month"])-1, parseInt(cw_event["end_event_day"])); // end date obj
if (cal.date.getFullYear() == cal.banner_beg_date.getFullYear()) {
var bandatetimestr = cal.banner_beg_date.format("ddd, mmm d");
} else {
var bandatetimestr = cal.banner_beg_date.format("ddd, mmm d, yyyy");
}
if (gIsMilitary == '1') {
var timeformatstr = "UTC:HH:MM";
} else {
var timeformatstr = "UTC:h:MMtt";
}
if (!cw_event["allday"]) {
bandatetimestr += " " + dateFormat(tsToDate(cw_event, "start_timestamp_gmt", "start_tzoffset"), timeformatstr);
}
if (cal.date.getFullYear() == cal.banner_end_date.getFullYear()) {
bandatetimestr += ' - ' + cal.banner_end_date.format("ddd, mmm d");
} else {
bandatetimestr += ' - ' + cal.banner_end_date.format("ddd, mmm d, yyyy");
}
if ((!cw_event["noend"]) && (!cw_event["allday"])) {
bandatetimestr += " " + dateFormat(tsToDate(cw_event, "end_timestamp_gmt", "end_tzoffset"), timeformatstr);
}
jEventDiv.find(".startendtime").html("
Multi
Day
");
jEventDiv.find(".bannerdate").text(bandatetimestr);
} else {
// Non banner event (single or repeating occurrences)
jEventDiv.find(".startendtime").html(getStartEndTime(cw_event, false));
}
return jEventDiv;
}
// Creates date display div for list views with dow, month, day and if not current year then also year
function getDateDiv(divyear, divmonth, divday) {
// Create day spacer with date
jDateDiv = $("#daydatetemplate").clone(true);
// Remove id attribute
jDateDiv.removeAttr("id");
displaydate = new Date(parseInt(divyear), parseInt(divmonth)-1, parseInt(divday));
if (divyear == todayyear) {
// Do not display year if current year
displaydatestr = displaydate.format("ddd, mmm d");
} else {
// Display year if not current year
displaydatestr = displaydate.format("ddd, mmm d, yyyy");
}
jDateDiv.find(".daydate").html(displaydatestr);
datadatestr = displaydate.format("yyyymmdd");
jDateDiv.attr("data-date", datadatestr);
jDateDiv.attr("class", "daydatedisplay");
// Highlight if today
if (datadatestr == todaydatestr) {
jDateDiv.find("td").css("color","#d24e4e");
}
jDateDiv.show();
// Save this dates info for use in date change detection in other scripts
prior_occ_event_day = divday; // Reset prior day
prior_occ_event_month = divmonth; // Reset prior day
prior_occ_event_year = divyear; // Reset prior day
return jDateDiv;
}
function scrollToSelectedDate (scrolldate) {
// Scroll to currently selected date or next nearest date
// starting with current display date and incrementing by one day
var foundscrolldate = false;
// Try going forward in time
for(var i = 0; i < 365; i++) {
scrolldate = new Date(cal.date);
scrolldate.setDate(cal.date.getDate() + (i*1));
datadatestr = scrolldate.format("yyyymmdd");
scrollElement = $("div[data-date='"+datadatestr+"']" );
if (scrollElement.length != 0) {
foundscrolldate = true;
break;
}
}
// Try going backwards in time
if (!foundscrolldate) {
for(var i = 0; i < 365; i++) {
scrolldate = new Date(cal.date);
scrolldate.setDate(cal.date.getDate() - (i*1));
datadatestr = scrolldate.format("yyyymmdd");
scrollElement = $("div[data-date='"+datadatestr+"']" );
if (scrollElement.length != 0) {
foundscrolldate = true;
break;
}
}
}
if (foundscrolldate) {
// Found date div so get value to set scroll top
var scrollTopval = $(scrollElement).parent().scrollTop() + $(scrollElement).offset().top - $(scrollElement).parent().offset().top
} else {
var scrollTopval = 1; // Did not find so scroll to top of event list
}
// Do the scroll
$('#dayevents').animate({
scrollTop: scrollTopval
}, {
duration: 0,
specialEasing: {
width: 'linear',
height: 'easeOutBounce'
},
complete: function (e) {
}
});
}
//######################### Event Display ###############################
// Display event detail view, e should contain object from touch on event in summary list
// or can contain the actual id of event to be displayed
function displayEvent(e){
gCameFromDetail = true;
if(typeof(e) == "object"){
// Came from touch on event div in list
if ($.active) { return false; } // Do not respond to touch if ajax activly refreshing list
// Event summary div was clicked to view details so get
// event id and display event details
// Check to make sure that an event was clicked on
var cw_id = $(e.target).closest("[cw_id]").attr("cw_id");
if(cw_id){
g_cw_id = cw_id;
} else {
return;
}
// Get the repeat id (will be zero if not repeating)
g_rpt_master_id = $(e.target).closest("[repeat_id]").attr("repeat_id");
// Set current date to that of selected event occurrence
// This is so return from event goes to date of event
// Important for list view so event still in view on return
var occ_datestr = $(e.target).closest("[cw_id]").attr("data-date")
cal.date = new Date(occ_datestr.substring(0, 4)+'/'+occ_datestr.substring(4, 6)+'/'+occ_datestr.substring(6, 8));
$("#dayevents").hide();
$("#calendar").hide();
gCurrentPlace = "eventdetails";
display_action_row();
} else {
// Came from call with event id as parameter
if ((typeof e == 'number') && (e > 0)) {
// Return from editor for adding event
g_cw_id = e;
g_rpt_master_id = 0;
} else {
// Return from editor of existing event if edit was single event or single occurrence of repeat and not delete
g_cw_id = $("#edit #event_id").val();
g_rpt_master_id = $("#edit #repeat_id").val();
}
}
// Go get event information for display
getEvent(gCalendar, g_cw_id, g_rpt_master_id, eventCallback, "");
}
//######################## Common Event Display and Edit Code ###################################
// Makes call to server to retrieve individual event details
function getEvent(calendar, id, repeat_id, callback, edittype){
if(callback == editEventCallback){
var op = "u"; //under new cwapi this has the same meaning of update
}
else{
op = "d"; //for a single event detail is now "d"
}
$.getJSON("/cwapi.php" + gPHPSESSID, {fmt:"json", view:op, "eid[]":id, edittype:edittype, "repeat_id":repeat_id, crd:calendar}, callback, "json");
}
// Populate and display event detail
function eventCallback(data, result){
commonAjaxCallback(data, result);
if (data.status.code == 200) { // Found event
var jEventDiv = $("#event");
// Get all event details into cw_event object
var cw_event = data.eventdetails[0];
// Do not show email or download buttons if show busy set and not logged in
if ((gCalShowBusy == 1) && (!cw_event.canedit)) {
$("#openNewEmail").css({"display":"none"});
$("#downloadEvent").css({"display":"none"});
} else {
$("#openNewEmail").css({"display":"inline"});
$("#downloadEvent").css({"display":"inline"});
}
// Do not show remind button if show busy set and not logged in and event not is past
if (((gCalShowBusy == 1) && (!cw_event.canedit)) || (data.eventdetails[0].inpast)) {
$("#getReminder").css({"display":"none"});
} else {
$("#getReminder").css({"display":"inline"});
}
// Do not show edit link if not logged in or cannot edit
if (!cw_event.canedit) {
$("#editlink").css({"display":"none"});
} else {
$("#editlink").css({"display":"inline"});
}
// --------------------------
// Begin event detail display
// --------------------------
// Event date(s)
if (cw_event.isbanner) {
// Banner events only
cal.banner_beg_date = new Date(parseInt(cw_event.event_year), parseInt(cw_event.event_month)-1, parseInt(cw_event.event_day)); // end date obj
cal.banner_end_date = new Date(parseInt(cw_event.end_event_year), parseInt(cw_event.end_event_month)-1, parseInt(cw_event.end_event_day)); // end date obj
var bandatetimestr = cal.banner_beg_date.format("ddd, mmm d, yyyy");
if (data.settings.military == '1') {
var timeformatstr = "UTC:HH:MM";
} else {
var timeformatstr = "UTC:h:MMtt";
}
if (!cw_event.allday) {
bandatetimestr += " " + dateFormat(tsToDate(cw_event, "start_timestamp_gmt", "start_tzoffset"), timeformatstr);
}
bandatetimestr += ' - ' + cal.banner_end_date.format("ddd, mmm d, yyyy");
if ((!cw_event.noend) && (!cw_event.allday)) {
bandatetimestr += " " + dateFormat(tsToDate(cw_event, "end_timestamp_gmt", "end_tzoffset"), timeformatstr);
}
} else {
// Non banner events (single or repeating occurrence)
cal.event_beg_date = new Date(parseInt(cw_event["event_year"]), parseInt(cw_event["event_month"])-1, parseInt(cw_event["event_day"])); // end date obj
}
// Put this events id and repeat id into detail event display navbar for other actions usage
$("#eventdetailactionrow").attr("cw_id", cw_event.event_id);
$("#eventdetailactionrow").attr("repeat_id", cw_event.repeat_id);
// -----------------------------------
// Put event values into place holders
// -----------------------------------
// Clear all values from past event display
$("#event .detailvalue").html('');
// Hide all labels for now, show later if has values
$("#event .label").hide();
// Event title
var titletxt = cw_event.title;
if (cw_event.draft) {
titletxt = titletxt + '
draft
';
}
$("#value_title").html(titletxt);
// Date and Time
if (cw_event.isbanner) {
$("#value_date").html(bandatetimestr);
} else {
$("#value_date").html(dateFormat(cal.event_beg_date, "ddd, mmm d, yyyy")+''+getStartEndTime(cw_event, true))+'';
}
// Location name
var locationstr = cw_event.location_name;
// Location map link
var mapqscore = 0; // Will only show map link if map quality score is greater than or equal to 3
var mapurl = '';
if (cw_event.location_street) { mapurl += cw_event.location_street; mapqscore++;}
if (cw_event.location_city) { mapurl += '+'+cw_event.location_city; mapqscore++;}
if (cw_event.location_stateprovince) { mapurl += '+'+cw_event.location_stateprovince; mapqscore++;}
if (cw_event.location_zippostal) { mapurl += '+'+cw_event.location_zippostal;}
if (cw_event.location_country) { mapurl += '+'+cw_event.location_country; }
if ((mapurl) && (mapqscore >= 3)) {
// Encode map url components
mapurl = encodeURIComponent(mapurl);
var deviceOS = getMobileOperatingSystem();
if (deviceOS == 'iOS') {
mapurl = 'http://maps.apple.com/maps?address='+mapurl;
} else {
mapurl = 'https://maps.google.com/?q='+mapurl;
}
var mapstr = "";
}
// Location weather link
var weatherqscore = 0; // Will only show weather link if weather quality score is greater than or equal to 3
var weatherurl = '';
// if (cw_event.location_city) { weatherurl += cw_event.location_city; weatherqscore++;}
// if ((cw_event.location_stateprovince) && (cw_event.location_stateprovince.length == 2)) {
// weatherurl += '+'+cw_event.location_stateprovince;
// weatherqscore++;
// }
// if (cw_event.location_zippostal) { weatherurl += '+'+cw_event.location_zippostal; weatherqscore++;}
// if (cw_event.location_country) { weatherurl += '+'+cw_event.location_country; }
var weather_country = cw_event.location_country.toLowerCase();
var country_haystack = "us usa united states of america";
if (country_haystack.includes(weather_country)) { weather_country = 'us'; }
//console.log(weather_country);
if (weather_country) { weatherurl += '/'+ weather_country; weatherqscore++;}
if ((cw_event.location_stateprovince) && (cw_event.location_stateprovince.length == 2)) {
weatherurl += '/'+cw_event.location_stateprovince.toLowerCase();
weatherqscore++;
}
var weather_city = cw_event.location_city.replace(/\s+/g,' ').trim();
weather_city = weather_city.replace(" ", "-");
if (weather_city) { weatherurl += '/'+ weather_city.toLowerCase(); weatherqscore++;}
if ((weatherurl) && (weatherqscore >= 3) ) {
// Encode map url components
//weatherurl = encodeURIComponent(weatherurl);
//weatherurl = 'http://www.wunderground.com/cgi-bin/findweather/getForecast?bannertypeclick=htmlSticker&query='+weatherurl;
weatherurl = 'https://www.wunderground.com/weather'+weatherurl;
//console.log(weatherurl);
var weatherstr = "";
}
if (locationstr) {
$("#label_location").show();
$("#value_location_name").html(locationstr);
}
// Clear any previous button HTML
// then add the map link
$("#value_map").html("");
if (typeof mapstr != "undefined") {
$("#value_map").html(mapstr);
}
// Clear any previous button HTML
// then add the weather link
$("#value_weather").html("");
if (typeof weatherstr != "undefined") {
$("#value_weather").html(weatherstr);
}
// Location address
if (mapurl) { // If anything in map url then must be something in address fields
if (cw_event.location_street) { $("#value_location_street").text(cw_event.location_street); }
if ((cw_event.location_city) && (cw_event.location_stateprovince)) {
$("#value_location_city").text(cw_event.location_city+',');
} else if (cw_event.location_city) {
$("#value_location_city").text(cw_event.location_city);
}
if (cw_event.location_stateprovince) { $("#value_location_stateprovince").text(cw_event.location_stateprovince); }
if (cw_event.location_zippostal) { $("#value_location_zippostal").text(cw_event.location_zippostal); }
if (cw_event.location_country) { $("#value_location_country").text(cw_event.location_country); }
$("#container_citystatezip").show();
} else {
$("#container_citystatezip").hide();
}
// Category
if (cw_event.category_color == "" || cw_event.category_color == "#fff" || cw_event.category_color == "#FFFFFF") {
border_if_white = "border:1px solid #d3d3d3;";
} else {
border_if_white = "border:none;";
}
// Legend colored dot
var legendhtml = '';
$("#label_category_name").show();
$("#value_category_name").html(legendhtml+cw_event.category_name);
// Description
// Check if description only html editor comment
if (cw_event.description == ""){
cw_event.description = ''; // really no description text
}
if (cw_event.description) {
if (cw_event.description.search("") != -1) {
$("#value_description").html(cw_event.description.replace(/\n/g, " ")); // Not html so convert line ends to brs
} else {
$("#value_description").html(cw_event.description);
}
$("#label_description").show();
}
// Event Contact
if ((cw_event.contact) || (cw_event.email) || (cw_event.phone)) {
if (cw_event.contact) { $("#value_contact").text(cw_event.contact); }
if (cw_event.email) {
if (checkemailaddress(cw_event.email)) {
$("#value_email").html('' + cw_event.email + '');
} else {
$("#value_email").text(cw_event.email);
}
}
if (cw_event.phone) {
$("#value_phone").html('' + cw_event.phone + '');
}
$("#label_contact").show();
}
// More info link
var url = cw_event.url;
var display_url = url;
if(url){
if(!url.match(/^https?/)){
url = "http://" + url;
}
$("#value_url").html('' + cw_event.url + '');
$("#label_url").show();
}
// Location Details
if ((cw_event.location_contact) || (cw_event.location_phone) || (cw_event.location_directions) || (cw_event.location_guidelines)) {
if (cw_event.location_contact) {
$("#value_location_contact").html(cw_event.location_contact);
$("#label_location_contact").show();
}
if (cw_event.location_phone) {
$("#value_location_phone").html('' + cw_event.location_phone + '');
}
if (cw_event.location_directions) {
$("#value_location_directions").html(nl2br(cw_event.location_directions, false));
$("#label_location_directions").show();
}
if (cw_event.location_guidelines) {
$("#value_location_guidelines").html(nl2br(cw_event.location_guidelines, false));
$("#label_location_guidelines").show();
}
}
// Private Note
if (cw_event.privatenote) {
$("#value_privatenote").html(nl2br(cw_event.privatenote, false));
$("#label_privatenote").show();
}
$("#eventdetailactionrow").attr("cw_id");
$("#eventdetailactionrow").attr("repeat_id");
jEventDiv.show();
gCurrentPlace = "eventdetails";
display_action_row();
// Adjust height of event detail view div and set scroll to top
$("#event").height(listcurrentfullht - 30).scrollTop(0); //
// Hightlight reminder icon (bell) if reminder set for this event
checkReminder()
} else if (data.status.code == 204) {
// Event not found. Might be caused by change of single event to repeating event
// because the event is completely replaced and old event id not longer valid
// Could also be caused by another user deleting the event in another session
$.alert('Event not found. May have been changed by other user. Refreshing view now.','')
// Skip event detail display and return to current view
gCurrentPlace = "events_cal_or_list";
showCurrentView(currentView, true);
} else {
// Something wrong
handleResponseError(data);
}
}
// Handle back button from event detail view
function eventDone(){
$("#event").hide();
$("#edit").hide();
gCurrentPlace = "events_cal_or_list";
display_action_row();
if (gRefreshAfterEdit) {
// Refresh lists after event edit
showCurrentView(currentView, true);
gRefreshAfterEdit = false; // Reset to not refresh
} else {
showCurrentView(currentView, false);
}
}
// Cancel event add / edit
function eventEditDone(){
$("#edit").hide();
if (gCurrentPlace == "eventadd") {
showCurrentView(currentView, false);
} else {
gCurrentPlace = "eventdetails";
$("#event").show();
display_action_row();
}
}
//######################### Add New Event ###############################
function checkCalendarStatus() {
// Check calendar type status
var alertstr = '';
if (calendar_type == "Expired") {
alertstr = "Sorry, your calendar trial has expired. \n\nAs a courtesy, your calendar is available for viewing. \nYou may restore the ability to add and edit events by purchasing a subscription.";
} else if (calendar_type == "Billing-C") {
alertstr = "Sorry, our records show a problem billing for your account. \n\nAs a courtesy, your calendar is available for viewing. \nYou may restore the ability to add and edit events by updating your billing information.";
} else if (calendar_type == "Renewal-C") {
alertstr = "Sorry, our records show your subscription renewal is past due. \n\nAs a courtesy, your calendar is available for viewing. \nYou may restore the ability to add and edit events by renewing your subscription";
}
if (alertstr != '') {
alertstr += "\n\nFor details, login to your calendar using a desktop or tablet and go to Account Details. \n\nOr, contact support@calendarwiz.com"
$.alert(alertstr, "Calendar Status");
return false;
} else {
return true;
}
}
function addEvent(e, skipCategories){
if (!checkCalendarStatus()) { return false; }
endDateIsDirty = false; // For date picker control
if(skipCategories == undefined){
// Need to populate the category drop down
editEventEvent = undefined;
getEditLocations(gCalendar, populateLocationDropDown);
getEditCategories(gCalendar, populateCategoryDropDown);
// Note: getEditCategories will do ajax call to populate category dropdown in editor
// and then call addEvent again but with skipCategories defined so next time will skip
return;
}
// Need to check here to see if we're logged in with
// permission to add an event
if(gLoggedIn){
if (gCanAdd) {
$(cal.grid).hide();
$("#navTodayAddLogInOut").hide();
$("#dayevents").hide();
getNewEvent(gCalendar, cal.date);
} else {
$.alert('Sorry, this login not allowed to add events','');
}
} else {
if($("#loginform").is(":visible"))
return;
if($.confirm("Login required to add an event.\nDo you want to login?","")){
gDestinationScreen = "#edit";
$(cal.grid).hide();
$("#navTodayAddLogInOut").hide();
$("#dayevents").hide();
logincancelviz();
$("#loginform").show();
gCurrentPlace = "loginscreen";
display_action_row();
$("#navTodayAddLogInOut").hide();
$("#username")[0].focus();
}
}
}
// Get default event values
function getNewEvent(calendar, date){
var month = (date.getMonth() + 1);
var year = date.getFullYear();
var day = date.getDate();
// Get default event values "view = a"
$.getJSON("/cwapi.php" + gPHPSESSID, {fmt:"json", view:"a", aday:day, amonth:month, ayear:year, crd:calendar}, editEventCallback, "json");
}
//############################ Display Event for Adding or Editing ###############################
var editEventEvent;
function editEvent(e, skipCategories){
if (!checkCalendarStatus()) { return false; }
if(skipCategories == undefined){
// Populate the category drop down
editEventEvent = e;
getEditLocations(gCalendar, populateLocationDropDown);
getEditCategories(gCalendar, populateCategoryDropDown);
return false;
}
$("#chooseedit").hide();
$("#chooseeditactionrow").hide();
$("#dayevents").hide();
$("#event").hide();
var edittype = "";
if (typeof(e) == "string") {
edittype = e;
// Send in the repeat_id in here
getEvent(gCalendar, g_cw_id, g_rpt_master_id, editEventCallback, edittype);
return false;
} else {
g_cw_id = $("#eventdetailactionrow").attr("cw_id");
g_rpt_master_id = $("#eventdetailactionrow").attr("repeat_id");
}
if (g_rpt_master_id > 0) {
// Pop up a dialog to ask the user whether they want to do an
// edit on a single event or all occurences of a repeating event
$(".action_row").hide();
gCurrentPlace = "chooseedit";
display_action_row();
$("#chooseedit").show();
return false;
} else {
getEvent(gCalendar, g_cw_id, g_rpt_master_id, editEventCallback, edittype);
return false;
}
}
function editEventCallback(data, result){
commonAjaxCallback(data, result);
if(data.status.code == 200){
var jEventDiv = $("#edit");
$("#eventform").height(listcurrentfullht - 30).scrollTop(0);
jEventDiv.show();
if(data.status.view == "a"){
var cw_event = data.eventdefaults;
}
else if(data.status.view == "u"){
var cw_event = data.eventupdatedetails[0];
}
else{
cw_event = data.eventdetails[0];
}
// Display action text
var actionheadertxt = '';
gCurrentPlace = "eventedit"; // may change to eventadd in next conditional
if (data.status.view == "a") {
actionheadertxt = 'New Event';
gCurrentPlace = "eventadd";
gCameFromDetail = false; // just in case
} else if (data.status.view == "u") {
if (cw_event.edittype == "single") {
actionheadertxt = 'Edit Single Repeat';
} else if (cw_event.edittype == "repeats") {
actionheadertxt = 'Edit All Repeats';
} else {
actionheadertxt = 'Edit Single Event';
}
}
$("#addeditheader").text(actionheadertxt);
if(cal.date == undefined){
cal.date = new Date();
}
$(".date").text(cal.date.format("dddd, mmmm d, yyyy"));
// Extract the day, month, year from the seconds
// need to heed if this is military time
var mil = data.settings.military == '1';
var startDate = tsToDate(cw_event, "start_timestamp_gmt", "start_tzoffset");
cw_event.emonth = dateFormat(startDate, "UTC:m");
cw_event.eyear = dateFormat(startDate, "UTC:yyyy");
cw_event.eday = dateFormat(startDate, "UTC:d");
var endDate = tsToDate(cw_event, "end_timestamp_gmt", "end_tzoffset");
var rptendDate = tsToDate(cw_event, "rpt_end_timestamp_gmt", "rpt_end_tzoffset");
// Begin for banner events
// End for banner events
if(mil){
$("#startampm").hide();
$("#endampm").hide();
$("#startampmlabel").hide();
$("#endampmlabel").hide();
cw_event.starthour = dateFormat(startDate, "UTC:H");
cw_event.endhour = dateFormat(endDate, "UTC:H");
}
else{
$("#startampm").show();
$("#endampm").show();
$("#startampmlabel").show();
$("#endampmlabel").show();
cw_event.starthour = dateFormat(startDate, "UTC:h");
cw_event.endhour = dateFormat(endDate, "UTC:h");
}
setHoursDropDown($("#starthour")[0], mil);
setHoursDropDown($("#endhour")[0], mil);
cw_event.startminute = dateFormat(startDate, "UTC:MM");
if(cw_event.startminute.match(/^[1-5][0,5]$/)){
cw_event.startminute = "+" + cw_event.startminute;
}
cw_event.startampm = dateFormat(startDate, "UTC:tt");
cw_event.endminute = dateFormat(endDate, "UTC:MM");
if(cw_event.endminute.match(/^[1-5][0,5]$/)){
cw_event.endminute = "+" + cw_event.endminute;
}
cw_event.endampm = dateFormat(endDate, "UTC:tt");
cw_event.draft = "0";
if(cw_event.login_email == ""){
$("#remindme").hide();
}
else{
$("#remindme").show();
}
// When a single event comes through here there are no rpt fields
// so the rpt fields default to the last state they were in
// so we need to come up with something to initialize their state
// what we can do is just insert default values into cw_event right here
cw_event.rpt_end_day = dateFormat(rptendDate, "UTC:d");
cw_event.rpt_end_month = dateFormat(rptendDate, "UTC:m");
cw_event.rpt_end_year = dateFormat(rptendDate, "UTC:yyyy");
if(cw_event.rpt_type == undefined){
cw_event.rpt_sun = false;
cw_event.rpt_mon = false;
cw_event.rpt_tue = false;
cw_event.rpt_wed = false;
cw_event.rpt_thu = false;
cw_event.rpt_fri = false;
cw_event.rpt_sat = false;
cw_event.rpt_type = "none";
cw_event.rpt_weeks = "1";
cw_event.rpt_days = "2";
cw_event.rpt_month_place = "first";
cw_event.rpt_noenddate= false;
}
initControls(); //only for whether they are enabled or disabled.
for(var p in fieldsToPopulate){
if ((p == 'privatenote') && (!data.status.canviewprivatenote)) {
continue;
}
var thisInputControl = $("#"+p);
if(fieldsToPopulate[p].match(/span/)){
thisInputControl.text(cw_event[p]);
} else if(fieldsToPopulate[p].match(/checkbox/)){
if(cw_event[p]){
thisInputControl.attr("checked", true);
} else {
thisInputControl.attr("checked", false);
}
} else {
thisInputControl.val(cw_event[p]);
}
if(cw_event.edittype == "single") { //in single event of a repeating series mode
if(p.match(/^rpt_/) && !p.match(/^rpt_last_flag/)){
thisInputControl.attr("disabled", true);
}
} else {
thisInputControl.attr("disabled", false);
}
}
// If adding event then set category dropdown to first checked category in filter
if(data.status.view == "a"){
// Set category dropdown to first category selected in filter
if (cidarray.length > 0) {
$("#eventform #category_id").val(cidarray[0]);
}
}
// Store location_id in global DOM variable from location select list
last_saved_location_id = $("#location_id").val();
// Set Saved Location zero option text if editing event already has Saved Location
if (last_saved_location_id != 0) {
$("#location_id option[value='0']").text('De-Select Saved Location');
}
// If using html editor (i.e. tinymce) update the tinymce editor so has
// content of the description text area and not left over from previous edit
if ((usehtmled) && (typeof tinyMCE != 'undefined')) {
// Get editor object
var editor = tinyMCE.get('description');
// Get content of description text area
var descriptionval = $("#description").val();
descriptionval = descriptionval.replace(//g, "");
editor.execCommand('mceSetContent', false, descriptionval);
editor.startContent = tinymce.trim(editor.getContent({ format: 'raw' }));
editor.isNotDirty = true;
editor.nodeChanged();
}
setNumDays('e');
setNumDays('rpt_end_');
timeOptions();
docontrols();
gInitSerialize = $("[name=eventform]").serialize();
display_action_row();
checkCatFiltered();
// Scroll form to top
$("#eventform").scrollTop(0); // minus 30 for padding
// Remove focus from active element just in case so
// keyboard not opened on inital form display
$(document.activeElement).blur();
} else {
// Something wrong
handleResponseError(data);
}
}
//################################ Saving an Event #######################################
function SaveEvent(mode){
if (mode != 'delete') {
var valid = validate();
if(!valid){
return false;
}
}
// If using html editor (i.e. tinymce), need to get description into actual textarea for posting
if ((usehtmled) && (typeof tinyMCE != 'undefined')) {
var tinymce_description = tinymce.get('description').getContent();
if (tinymce_description != '') {
$("#description").val('' + tinymce_description); // Only if description not blank
} else {
$("#description").val('');
}
} else {
// Remove html editor comment tag in case was edited by html editor and then html editor turned off in options
// Note that other html tags are not removed at this time
$("#description").val($("#description").val().replace(//g, ""));
}
var serialize = $("[name=eventform]").serialize();
var edittype = $("#edittype").val();
var event_id = $("#event_id").val();
var repeat_id = $("#repeat_id").val();
if(mode == "delete") {
$.confirm("Are you sure you want to delete this event?", "This operation cannot be undone", function () {
serialize += "&btndelete=Delete";
$("#edit").hide();
$.post("/calendars/editevent_savemobile.php" + gPHPSESSID + "&crd=" + gCalendar + "&view=u", serialize, eventSaveCallback, "json")
.done(function() {
})
.fail(function() {
});
return;
});
}
if(mode == "draft"){
serialize = serialize.replace(/draft=0/, "draft=1");
}
if(edittype == "single" || edittype == ""){
serialize = serialize.replace(/occ_changed=0/, "occ_changed=1");
}
var dirty = gInitSerialize != serialize;
if(dirty){
if(!validate()){
return;
}
if($("#old_occ_changed").val() == "1" && (edittype == 'repeats')){
if(!confirm("CAUTION: You are editing all occurrences of this repeating event. Saving will overwrite changes previously made to individual occurrences of this event.")){
return;
}
}
}
// CLIENT: Flag set when editing occurrence of a repeating series if any data was changed. This needs to be set by client and in POST
serialize = serialize.replace(/minute=%2B/g, "minute=");
// Get primary category name and put into serialize variable
var selcatoption = $("#category_id option:selected");
var cat_name = selcatoption.text();
serialize += "&cat_name=" + cat_name;
// Get category noconflict property value for the selected category
var catnoconflicts = selcatoption.prop("noconflicts");
// If saved location selected, Get selected location noconflict property
if ($("#location_id").val() != 0) {
var sellocoption = $("#location_id option:selected");
var loc_name = sellocoption.text();
var locnoconflicts = sellocoption.prop("noconflicts");
} else {
var locnoconflicts = '0';
var loc_name = '';
}
if ((mode == "save") || (mode == 'draft')) {
if ((mode != 'draft') && ((catnoconflicts == "1") || (locnoconflicts == "1"))) {
// Check for category conflicts
$.post("/calendars/editevent_conflictcheck.php"+gPHPSESSID+"&crd="+gCalendar+"&catnoconflicts="+catnoconflicts+"&locnoconflicts="+locnoconflicts, serialize, function(data) {
if (data.status.code == 700) {
var catconcount = parseInt(data.conflicts.catconcount);
if (catconcount == 1) { var catcontxt = ""; } else { var catcontxt = "s"; }
var locconcount = parseInt(data.conflicts.locconcount);
if (locconcount <= 1) { var loccontxt = ""; } else { var loccontxt = "s"; }
var numconflicts = parseInt(data.conflicts.eventconcount);
if (numconflicts == 1) { var numcontxt = ""; } else { var numcontxt = "s"; }
var conflictmsg = 'Sorry, this event conflicts with ' + numconflicts + ' event' + numcontxt + ' in the calendar. ' + "\n\n";
if (catconcount > 0) {
conflictmsg += ' - ' + catconcount +' conflict' + catcontxt + ' in category ' + cat_name + "\n";
}
if (locconcount > 0) {
conflictmsg += ' - ' + locconcount +' conflict' + loccontxt + ' at location ' + loc_name + "\n";
}
conflictmsg += "\n";
conflictmsg += 'Please Choose different time';
if (catconcount > 0) {
if (locconcount > 0) {
conflictmsg += ', category or location.';
} else {
conflictmsg += ' or category.';
}
} else {
conflictmsg += ' or location.';
}
conflictmsg += "\n\n";
$.alert(conflictmsg, 'Conflict Found');
return false;
} else {
// No conflicts found so save
postSave(serialize);
}
}, "json");
} else {
// No conflict check, just save
postSave(serialize);
}
}
}
function postSave(serialize) {
// Go ahead and save event - update or add, deletes done above
var edited_event_id = $("#edit #event_id").val() // May be blank if this was add event
if (edited_event_id == '') {
var postview = 'a';
} else {
var postview = 'u';
}
$("#edit").hide();
$.post("/calendars/editevent_savemobile.php" + gPHPSESSID + "&crd=" + gCalendar + "&view=" + postview, serialize, eventSaveCallback, "json");
}
function eventSaveCallback(data, result){
commonAjaxCallback(data, result);
if (data.status.code == 200) {
gRefreshAfterEdit = true; // flag to force refresh of lists on return from event detail
// See if this was delete
if (data.status.msg == 'Successful Delete Operation') {
// Event was deleted
var wasdelete = true;
} else {
// Not a delete
var wasdelete = false;
// Get id of first occurrence returned from save event
if (typeof data.eventdetails.first_occ_event_id != 'undefined') {
var first_occ_event_id = data.eventdetails.first_occ_event_id;
} else {
var first_occ_event_id = 0;
}
}
var edittype = $("#edit #edittype").val();
var edited_event_id = $("#edit #event_id").val() // May be blank if this was add event
var edited_repeat_id = $("#edit #repeat_id").val() // May be zero if this was edit of single event
gCameFromUpdate = true;
// ------------------------------------------------------
// Handle view return for different types of event saves
// ------------------------------------------------------
// For new add event and deletes retun to current view
if ((wasdelete) || ((edited_event_id == '') && (edittype == ''))) {
g_cw_id = 0;
showCurrentView (currentView, true);
} else if (gCameFromDetail) {
gCameFromDetail = false;
gCurrentPlace = "eventdetails";
display_action_row();
if (first_occ_event_id > 0) {
displayEvent(first_occ_event_id);
} else {
displayEvent(edited_event_id);
}
} else {
g_cw_id = 0;
showCurrentView (currentView, true);
}
} else {
if (data.status.code == '855') {
showCurrentView (currentView, true);
$.alert('Sorry, you have exceeded allowed daily events. Contact support@calendarwiz.com', 'Limit Reached');
g_cw_id = 0;
showCurrentView (currentView, true);
} else {
// Something wrong
handleResponseError(data);
}
}
}
// Clean up event display list date seperators
function cleanuplistdates() {
var prevdatadate = '';
$("#dayevents").children().each(function(index) {
var attr = $(this).attr('data-date');
if (typeof attr != 'undefined') {
if (prevdatadate != '') {
$("#dayevents div[data-date='"+prevdatadate+"']").remove();
}
prevdatadate = attr;
} else {
prevdatadate = '';
}
});
}
//##################################### Category Filter Functions #################################
// Open category filter setting display
function openFilter() {
$("#navTodayAddLogInOut").hide();
$("#calendar").hide();
$("#dayevents").hide();
$("#category_filter_list").height(listcurrentfullht - 28).show().scrollTop(0); // minus 28 for padding
gCurrentPlace = "categoryfilter";
display_action_row();
}
// Cancel category filter setting and return to previous checked
function cancelFilter() {
// Set category filter checkboxes to previous values
$('.catpermits').each(function() {
if (jQuery.inArray($(this).val(), cidarray) !== -1) {
$(this).attr("checked", true);
} else {
$(this).attr("checked", false);
}
});
// If all categories now checked then set all checkbox checked
if ($(".catpermits").length == $(".catpermits:checked").length) {
$("#toggleallcats").attr("checked", true);
} else {
$("#toggleallcats").attr("checked", false);
}
$("#checkonecatmsg").hide();
$("#category_filter_list").hide();
showCurrentView (currentView, false);
}
// // Get any filter categories saved in cookie
function getCategoryFilterCookie() {
// Init or clear array of cookie category ids
cidcookiearray = [];
// Get categories for filter from cookie if present
cidcookie = readCookie('cw_cidarray_'+gCalendarId);
if (cidcookie != "" && cidcookie != null) {
cidcookiearray = cidcookie.split(',');
}
}
// Get categories for filter list, respects private categories via login
function getViewCategories(){
$.getJSON("/cwapi.php" + gPHPSESSID, {fmt:"json", view:"c", crd:gCalendar}, getViewCategoriesCallback, "json");
}
// Loads categories into search select. Checks categories that are active
function getViewCategoriesCallback(data, result){
if(!commonAjaxCallback(data, result)){
return;
}
if (data.categories != undefined) {
// Clear any elements that might be in category list
$("#category_filter_list .content *").remove();
// Insert 'All' checkbox
if (gSkip_all == 0) {
categoryhtml = ' ';
$("#category_filter_list .content").append(categoryhtml);
// Bind click to 'All' checkbox, Note remove above should
// also remove any previous binds to this element
$("#toggleallcats").bind("click", toggleAllCategories);
}
// Clear the array of checked categories
cidarray = [];
// Populate cidarray with category filter ids if cookie present.
// This function will NOT override any cid[]'s in URL
getCategoryFilterCookie();
// If no categories in cidarray from cookie (or URL) and
// skip all option not set then make allcatschecked = true
// flag to put all categories into array later in script
if ((cidurlarray.length == 0) && (cidcookiearray.length == 0) && (gSkip_all == 0)) {
var allcatschecked = true;
$("#toggleallcats").attr("checked", true);
} else {
var allcatschecked = false;
}
var categories = data.categories;
var categoryhtml = '';
var category_index = 0; // init
// For each category in this calendar returned
for(var i = 0; i < categories.length; i++){
// Create html for category filter checkbox list
categoryhtml = ' ';
$("#category_filter_list .content").append(categoryhtml);
// If this category id is already in cidarray get it's index otherwise this gives -1
// NOTE: This inArray is strict (i.e. type sensitive - cidarray is string not numeric)
if (cidurlarray.length > 0) {
category_index = jQuery.inArray(""+categories[i].category_id, cidurlarray) // Will be -1 if not in array
} else if (cidcookiearray.length > 0) {
category_index = jQuery.inArray(""+categories[i].category_id, cidcookiearray) // Will be -1 if not in array
} else {
category_index = -1;
}
if ((category_index !== -1) || (allcatschecked)) {
$("#"+categories[i].category_id).attr("checked", true); // Check this category checkbox
// Only put into cidarray if in url or cookie
if (category_index !== -1) {
cidarray.push(categories[i].category_id); // Add this category id to cidarray
}
}
}
if ($(".catpermits").length == $(".catpermits:checked").length) {
// If number of categories in filter array same as total number of categories
// then set all check box checked and set filter icon color to normal
$("#toggleallcats").attr("checked", true);
cidarray = []; // Clear the array of checked categories
$(".fa-filter").css("color","#fff"); // All checked so filter off = normal icon color white
} else {
$(".fa-filter").css("color","#cdffba"); // Not all checked so filter on = green
}
// Set up on click event for category filter checkboxes for setting all checkbox with all checked
$(".catpermits").bind("click", setAllCatsCheckbox);
// Hide category filter if only one category
if ($(".catpermits").length == 1) {
$("#filterbtn").hide();
} else {
$("#filterbtn").show();
}
// If multi categories not allowed and All is checked then uncheck other radio buttons
// and set filter icon to filter off color (white)
if (($("#toggleallcats").is(':checked')) && (gSkip_all == 0) && (gMulticat_on == 0)) {
$(".catpermits").attr("checked",false);
$(".fa-filter").css("color","#fff"); // filter off = normal icon color white
}
// Display view
if (typeof cal == 'object') {
// If calendar already init then just show current view with refresh
showCurrentView(currentView, true);
} else {
// First time load, need to init calendar and show month grid with list below
initMonthlyDisplay();
}
}
}
// Function to check or uncheck All checkbox in category filter list on click to any checkbox in list
function setAllCatsCheckbox() {
var numnotchecked = $(".catpermits:not(:checked)").length; // Number of categories checked
$("#checkonecatmsg").hide();
if (numnotchecked == 0) {
$("#toggleallcats").attr("checked", true);
} else {
$("#toggleallcats").attr("checked", false);
}
display_action_row();
}
function toggleAllCategories() {
if ((this.checked) && (gMulticat_on == 1)) {
$('.catpermits').each(function() {
this.checked = true;
});
} else {
$('.catpermits').each(function() {
this.checked = false;
});
}
display_action_row();
}
// Function to create category filter array, set cookie, set icon color
// Category filter checkboxes (catpermits[]) will always have at least
// one checked, maybe all checked.
function applyCategoryFilter() {
// See if at least one categories checked, if not show message
if ($(".categoryfilter").find('input[name="catpermits[]"]:checked, input[name="toggleallcats"]:checked').length == 0) {
$("#checkonecatmsg").show(); // Show message if none are checked, at least one must be checked
return false;
} else {
$("#checkonecatmsg").hide(); // In case was showing
}
// If All categories checked or if All radio checked when multi category selection is off
if (($(".catpermits").length == $(".catpermits:checked").length) || ($("#toggleallcats:checked").length > 0)) {
cidarray = []; // Clear the array of filter categories
eraseCookie('cw_cidarray_'+gCalendarId); // Erase cookie for future session
$(".fa-filter").css("color","#fff"); // filter off = normal icon color white
return true;
}
// ---------------------------------------------------------
// At least one but not all checked so apply category filter
// ---------------------------------------------------------
// Change to filter overrides url cid[] arguments
cidurlarray = [];
// Init categories filter array
if (typeof cidarray != "undefined") {
cidarray.length = 0;
} else {
cidarray = new Array();
}
var catcookiestr = ''; // String to hold category ids separated by commas
// Loog thru alls checked categories and create DOM array and cookie string
// both containing category ids of checked categories
$(".catpermits").each(function(index) {
if (this.checked === true) {
// Put category id into DOM array
cidarray.push(this.value);
// Put category id into cookie string
catcookiestr += this.value + ',';
}
});
// Create cookie of category ids in filter
createCookie('cw_cidarray_'+gCalendarId,catcookiestr.slice(0, -1),50);
// Set filter icon to active color
$(".fa-filter").css("color", "#cdffba"); // filter on = green
return true;
}
// Check if selected in category is hidden in filter, show notice
function checkCatFiltered() {
if ((cidarray.length > 0) && (jQuery.inArray($("#eventform #category_id").val(), cidarray) < 0)) { // Will be -1 if not in array
// Category selected in editor is hidden by filter so display notice
$("#categoryhiddenmsg").show();
} else {
$("#categoryhiddenmsg").hide();
}
}
//##################################### Email Functions #################################
function openNewEmail(e){
var jDetailDIV = $(e.target).closest("[cwui='detaildiv']");
$("#eventdetailactionrow").hide();
$("#event").hide();
var jEmailBodyDIV = $("#emailform");
jEmailBodyDIV.show();
jEmailBodyDIV.height(listcurrentfullht - 30); // minus 30 for padding
$("#emailform").height(listcurrentfullht - 30);
jEmailBodyDIV.find("[cwui=emailfrom]").val(gData.status.email);
var prefillemailaddress = gData.status.email; // Just email address, no display name
jEmailBodyDIV.find("[cwui=emailfrom]").val(prefillemailaddress);
jEmailBodyDIV.find("[cwui=emailto]").val(prefillemailaddress);
jEmailBodyDIV.find("[cwui=subject]").val("Calendar Note: " + $("#event #value_title").text());
jEmailBodyDIV.find("[cwui=intronote]").val("Check out this event.").trigger('keyup');
jEmailBodyDIV.show();
jEmailBodyDIV.scrollTop(0);
gCurrentPlace = "sendemail";
display_action_row();
return false;
}
function clearIntro() {
$("#emailform").find("[cwui=intronote]").val('');
displayCharsRemaining();
}
function displayCharsRemaining() {
var jIntroNote = $("#emailform").find("[cwui=intronote]");
var len = jIntroNote.val().length;
if(len > introNoteMaxLength){
jIntroNote.val(jIntroNote.val().substr(0,introNoteMaxLength));
return false;
}
$("#emailform").find("[cwui=charleft]").text("Max " + introNoteMaxLength + " characters, " +(introNoteMaxLength - len) + " remaining");
return true;
}
function cancelEmail() {
$("#sendemailactionrow").hide();
$("#emailform").hide();
gCurrentPlace = "eventdetails";
display_action_row();
jDetailBodyDIV.show();
}
function sendEmail(e){
var jDetailDIV = $(e.target).closest("div");
var jEmailBodyDIV = $("#emailform");
var emailto = jEmailBodyDIV.find("[cwui=emailto]").val();
var emailfrom = jEmailBodyDIV.find("[cwui=emailfrom]").val();
if(!checkemailaddress(emailto)){
jEmailBodyDIV.find("[cwui=emailto]").focus();
$.alert("Please enter a valid to email address", "");
jDetailDIV.scrollTop(0);
return;
}
if(!checkemailaddress(emailfrom)){
jEmailBodyDIV.find("[cwui=emailfrom]").focus();
$.alert("Please enter a valid from address", "");
jDetailDIV.scrollTop(0);
return;
}
var subject = jEmailBodyDIV.find("[cwui=subject]").val();
var note = jEmailBodyDIV.find("[cwui=intronote]").val();
if(subject == ""){
jEmailBodyDIV.find("[cwui=subject]").focus();
$.alert("Please enter an email subject", "");
jDetailDIV.scrollTop(0);
return;
}
if(note.length > introNoteMaxLength){
jEmailBodyDIV.find("[cwui=intronote]").focus();
$.alert("Please shorten your introduction to " + introNoteMaxLength + " characters.", "");
jDetailDIV.scrollTop(0);
return;
}
$.post("/cwapi.php" + gPHPSESSID + "&callback=?", {subject:subject, intronote:note, fromemail:emailfrom, toemail:emailto, event_id:g_cw_id, repeat_id:g_rpt_master_id, fmt:"json", view:"sendevent", crd:gCalendar}, emailCallback, "json");
}
function emailCallback(data, result){
commonAjaxCallback(data, result);
if(data.status.code == "200"){
//here we show the status msg
$.alert(data.status.msg, "Success");
var jEmailBodyDIV = $("#emailform");
jDetailBodyDIV.show();
jEmailBodyDIV.hide();
$("#emailform").hide();
gCurrentPlace = "eventdetails";
display_action_row();
}else{
// Something wrong
handleResponseError(data);
}
}
//##################################### Remind Functions #################################
// Check if a reminder set for event currently displayed in detail view
function checkReminder() {
$.getJSON("/cwapi.php" + gPHPSESSID + "&callback=?", {event_id:g_cw_id, repeat_id:g_rpt_master_id, fmt:"json", view:"getremind", crd:gCalendar}, checkreminderCallback);
}
// Callback to set reminder icon color to active color if reminder set for event in detail view
function checkreminderCallback(data, result) {
if (data.remindersettings.reminder_id > 0) {
$(".fa-bell").css("color","#cdffba"); // Not all checked so filter on = green
} else {
$(".fa-bell").css("color","#fff"); // All checked so filter off = normal icon color white
}
}
// Get reminder for event on click on reminder icon (bell)
function getReminder(e) {
var jDetailDIV = $(e.target).closest("div");
$.getJSON("/cwapi.php" + gPHPSESSID + "&callback=?", {event_id:g_cw_id, repeat_id:g_rpt_master_id, fmt:"json", view:"getremind", crd:gCalendar}, reminderCallback);
return false;
}
// Set reminder from form displayed by click on reminder icon (bell)
function setReminder(e) {
var jDetailDIV = $(e.target).closest("div");
var priortime = jRemindBodyDIV.find("[cwui=priortime]").val();
var email = jRemindBodyDIV.find("[cwui=email]").val();
if(!jRemindBodyDIV.reminder_id){
jRemindBodyDIV.reminder_id = 0;
}
if(checkemailaddress(email)){
if (g_rpt_master_id > 0) { g_repeat_mode = 'all'; } else { g_repeat_mode = 'single'; }
$.getJSON("/cwapi.php" + gPHPSESSID + "&callback=?", {reminder_id:jRemindBodyDIV.reminder_id, email:email, priortime:priortime, repeat_mode:g_repeat_mode, event_id:g_cw_id, repeat_id:g_rpt_master_id, fmt:"json", view:"setremind", crd:gCalendar}, reminderCallback);
}else{
$.alert("Please enter a valid email address", "");
}
}
// Delete reminder for event from form displayed by click on reminder icon (bell)
function deleteReminder(e) {
var jDetailDIV = $(e.target).closest("div");
if (g_rpt_master_id > 0) { g_repeat_mode = 'all'; } else { g_repeat_mode = 'single'; }
$.getJSON("/cwapi.php" + gPHPSESSID + "&callback=?", {reminder_id:jRemindBodyDIV.reminder_id, repeat_mode:g_repeat_mode, event_id:g_cw_id, repeat_id:g_rpt_master_id, fmt:"json", view:"delremind", crd:gCalendar}, reminderCallback);
}
// Confirm after reminder action
function reminderCallback(data, result) {
commonAjaxCallback(data, result);
if(data.status.code == 200 || data.status.code == 201){
if(data.status.view == "getremind"){
//here we need to display the reminder form and hide the event display
jDetailBodyDIV.hide();
jRemindBodyDIV = $("#reminderform");
jRemindBodyDIV.show();
display_action_row();
jRemindBodyDIV.find("[cwui=priortime]").val(data.remindersettings.priortime);
jRemindBodyDIV.find("[cwui=email]").val(data.remindersettings.email);
$("#reminderform").height(listcurrentfullht);
if (data.status.code == 200) {
jRemindBodyDIV.find("[cwui=email]").val(data.remindersettings.email);
} else {
// Get best default email, with display name if possible
var prefillemailaddress = gData.status.email; // Just email address, no display name
jRemindBodyDIV.find("[cwui=email]").val(prefillemailaddress);
}
jRemindBodyDIV.reminder_id = data.remindersettings.reminder_id;
if(data.remindersettings.reminder_id){
$("#deletereminder").show();
$("setreminder").val("Update Reminder");
$("#setreminderheader").text('Editing Reminder');
}else{
$("setreminder").val("Set Reminder");
$("#deletereminder").hide();
$("#setreminderheader").text('Setting Reminder');
}
gCurrentPlace = "setreminder";
display_action_row();
}else{
// Here we show the status msg - reminder set
if (data.status.view == 'delremind') {
$(".fa-bell").css("color","#fff"); // Change remonder icon (bell) to not set color
$.alert('Reminder removed', "");
} else if (data.status.view == 'setremind') {
$(".fa-bell").css("color","#cdffba"); // Change reminder icon (bell) to set color
$.alert('Reminder saved', "");
} else {
$.alert(data.status.msg, "");
}
jDetailBodyDIV.show();
jRemindBodyDIV.hide();
gCurrentPlace = "eventdetails";
display_action_row();
}
} else {
// Something wrong
handleResponseError(data);
}
}
//##################################### Download Event #################################
// Downloads event ical by click on download icon in event detail view
function downloadEvent(e) {
location.href = "/calendars/popcal.php" + gPHPSESSID + "&eid=" + g_cw_id;
return false;
}
//##################################### Edit Form Functions #################################
function setNumDays(prefix) {
//we need to make the eday dropdown have the right number of days in it.
//also have to change the selected day if it is greater than the number of days in the month
var feb = 28;
if((parseInt($("#"+prefix+"year").val()) % 4) == 0){
feb = 29;
}
var days = dateFormat.daysInMonth[parseInt($("#"+prefix+"month").val())];
if(days == 28){
days = feb;
}
var dayDD = $("#"+prefix+"day")[0];
var len = dayDD.options.length;
if(len == days)return;
if(len < days){
for(var i = len; i < days; i++){
dayDD[i] = new Option(i + 1, i + 1);
}
}
else{
var selectedIndex = dayDD.selectedIndex;
dayDD.options.length = days;
if(selectedIndex > (days - 1)){
dayDD.selectedIndex = days - 1;
}
}
}
function setHoursDropDown(dd, military){
if(military){
if(dd.options.length == 24){
return;
} else {
dd.options.length = 0;
for(var i = 0; i < 24; i++){
dd.options[i] = new Option(pad(i), i);
}
}
}
else{
// Handles scenario where
if(dd.options.length == 12){
return;
} else {
dd.options.length = 0;
for(var i = 0; i < 12; i++){
dd.options[i] = new Option(pad(i + 1), pad(i + 1));
}
}
}
}
function timeOptions(type){
var allday = $("#allday")[0].checked;
var noend = $("#noend")[0].checked;
if(type == "allday" && allday){
$("#noend")[0].checked = false;
noend = false;
}
else if(type == "noend" && noend){
$("#allday")[0].checked = false;
allday = false;
}
if(!allday && !noend){
$("#starthour").removeAttr("disabled");
$("#startminute").removeAttr("disabled");
$("#startampm").removeAttr("disabled");
$("#starttime").show();
$("#endhour").removeAttr("disabled");
$("#endminute").removeAttr("disabled");
$("#endampm").removeAttr("disabled");
$("#endtime").show();
}
else if(noend){
$("#starthour").removeAttr("disabled");
$("#startminute").removeAttr("disabled");
$("#startampm").removeAttr("disabled");
$("#starttime").show();
$("#endhour").attr("disabled", true);
$("#endminute").attr("disabled", true);
$("#endampm").attr("disabled", true);
$("#endtime").hide();
}
else{
$("#starthour").attr("disabled", true);
$("#startminute").attr("disabled", true);
$("#startampm").attr("disabled", true);
$("#starttime").hide();
$("#endhour").attr("disabled", true);
$("#endminute").attr("disabled", true);
$("#endampm").attr("disabled", true);
$("#endtime").hide();
}
}
// Following function now used to get list of categories for add / edit
// Note the subview parameter to be used in the get category list api
// function to flag filtering of only categories allowed to edit for this
// logged in user - introduced 120406 by Dennis Mulryan
function getEditCategories(calendar, callback){
$.getJSON("/cwapi.php" + gPHPSESSID, {fmt:"json", view:"c", subview:"addedit", crd:calendar}, callback, "json");
}
// Get locations for add / edit form
function getEditLocations(calendar, callback){
$.getJSON("/cwapi.php" + gPHPSESSID, {fmt:"json", view:"l", subview:"addedit", crd:calendar}, callback, "json");
}
// Loads categories into editor select
function populateCategoryDropDown(data, result){
if (data.categories != undefined) {
var categories = data.categories;
var jEventDiv = $("#edit");
var catDD = jEventDiv.find(":input[name='category_id']")[0];
catDD.options.length = 0;
for(var i = 0; i < categories.length; i++){
// This put in because API sends any category name with special characters as null
// Need to fix this in API since affects all category name from API (e.g. search widgets)
if(!categories[i].category_name) { categories[i].category_name = 'unknown'; }
if(categories[i].category_name.length > 45){
categories[i].category_name = categories[i].category_name.substr(0, 45) + "...";
}
catDD.options[catDD.options.length] = new Option(categories[i].category_name, categories[i].category_id);
// Add noconflicts attribute to this category option, note need to use length-1
// because we just created option so length changed and need to backup one index
catDD.options[catDD.options.length-1].noconflicts = categories[i].noconflicts;
}
}
if(editEventEvent == undefined){
addEvent(editEventEvent, true);
}
else{
editEvent(editEventEvent, true);
}
}
// Loads locations into select
function populateLocationDropDown(data, result){
if (data.locations != undefined) {
// Clear any remnants of prior add or edit
last_saved_location_id = 0;
$("#locmsg").hide();
// Put location data into var
var locations = data.locations;
var jEventDiv = $("#edit");
var locDD = jEventDiv.find(":input[name='location_id']")[0];
locDD.options.length = 0;
if (data.locations.length > 0) {
// First option is no saved location
locDD.options[0] = new Option("Choose Saved Location", "0");
for(var i = 0; i < locations.length; i++){
// This put in because API sends any location name with special characters as null
// Need to fix this in API since affects all location name from API (e.g. search widgets)
if(!locations[i].location_name) { locations[i].location_name = 'unknown'; }
if(locations[i].location_name.length > 45){
locations[i].location_name = locations[i].location_name.substr(0, 45) + "...";
}
locDD.options[locDD.options.length] = new Option(locations[i].location_name, locations[i].location_id);
// Add noconflicts attribute to this category option, note need to use length-1
// because we just created option so length changed and need to backup one index
locDD.options[locDD.options.length-1].noconflicts = locations[i].location_no_conflicts;
}
} else {
// No current saved locations
locDD.options[0] = new Option("No Saved Locations Yet", "0");
}
}
}
//######################### Refresh ###############################
var gTimeoutHandle;
var timeoutMilliseconds = 60000 * 115; // 115 minutes
//var timeoutMilliseconds = 10000 * 1; // 10 seconds for testing
gTimeoutHandle = window.setTimeout(refresh, timeoutMilliseconds);
// This only refreshes the session on server
function refresh() {
// Clear any existing timeout
if(gTimeoutHandle){
window.clearTimeout(gTimeoutHandle);
gTimeoutHandle = undefined;
gTimeoutHandle = window.setTimeout(refresh, timeoutMilliseconds);
}
if ($.active) { return false; }
// Do post to refresh session on server
$.post("/cwapi.php" + gPHPSESSID, {view:"refresh", fmt:"json", crd:gCalendar}, refreshCallback, "json");
}
function refreshCallback(data, result){
commonAjaxCallback(data, result);
}
// This refreshes current view to get up-to-date info
function refreshCurrentView() {
showCurrentView(currentView, true);
}
//######################### Authorization ###############################
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}
function login(username, password, callback){
// Hide wrong case message
$("#passwordmsg").hide();
var rememberme = $("#rememberme:checked").val();
var parameters = {scr:username, psw:password, crd:gCalendar, remlogin:rememberme};
$.post("/cwapi.php" + gPHPSESSID + "&auth=login&fmt=json", parameters, callback, "json");
}
function loginFromForm(){
var username = $.trim($("#username").val());
if(username == ""){
$.alert("Please enter Username or Email", "");
return;
}
var password = $.trim($("#password").val());
if(password == ""){
$.alert("Please enter a Password", "");
return;
}
var parameters = {login:username, password:password, setpassword:setpassword};
login(username, password, loginCallback);
}
function loginCookies(callback){
var rememberme = true;
$.post("/cwapi.php" + gPHPSESSID + "&auth=cookies&view=auth&fmt=json", {fmt:"json", crd:gCalendar, rememberme:true}, callback, "json");
}
function showLoginScreen(){
var username = readCookie("cw_user");
if(username){
$("#username").val(unescape(username));
$("#password").val("");
$("#rememberme").attr("checked", true);
} else{
$("#username").val("");
$("#password").val("");
}
$("#navTodayAddLogInOut").hide();
$(cal.grid).hide();
logincancelviz();
$("#loginform").show();
if(username){
$("#password")[0].focus();
} else{
$("#username")[0].focus();
}
gCurrentPlace = "loginscreen";
display_action_row();
}
function logincancelviz() {
if (gNoPublicCats) {
$("#logincancel").hide();
} else {
$("#logincancel").show();
}
}
function logInOut(e){
// Ignore login / out touch if ajax still actively
// retrieving else may show list with login screen
// also open below
if ($.active > 0) {
return false;
}
if (e.target.id == "loginlink") {
$("#dayevents").hide();
showLoginScreen();
gDestinationScreen = undefined;
} else {
// Call the cwapi.php to logout session.
$.post("/cwapi.php" + gPHPSESSID + "&auth=logout&fmt=json", {crd:gCalendar}, logoutCallback, "json");
}
}
function logoutCallback(data, result) {
if ((commonAjaxCallback(data, result)) && (data.status["code"] == 200)) {
// Need to get categories for filter to remove any private
getViewCategories();
}
}
function loginCallback(data, result) {
commonAjaxCallback(data, result);
if(data.status.code == 200){
$("#loginform").hide();
$("#loginlink").hide();
// Need to get categories for filter in case all were private
// which triggered login before this could be done
//getViewCategories();
// Do not show add button if show busy set and not logged in
if ((gCalShowBusy == 1) && (!gLoggedIn)) {
$("#navTodayAddLogInOut #a1").hide();
} else {
$("#navTodayAddLogInOut #a1").show();
}
gCanAdd = data.status.canadd; // Can this use add events true or false
gIsAdmin = data.status.isadmin; // Is this user an admin
gIsManager = data.status.ismanager; // Is this user a manager
// Reset to initial value so will check case again
setpassword = false;
// Need to get categories for filter in case all were private
// which triggered login before this could be done
// Note this will show current view after getting categories
getViewCategories();
} else if (data.status.code == 400){
$.alert(data.status.msg, "");
return false;
} else {
// Something wrong
handleResponseError(data);
}
}
function loginformcancel() {
$("#loginform").hide();
showCurrentView (currentView, false);
}
function setCalendar(){
var calendar = $("#cal_id").val();
if(calendar == ""){
$("#cal_id")[0].focus();
$.alert("Please enter a calendar name", "");
return;
}
window.location.href = "/mobile.html?crd=" + calendar;
return;
}
//#################################### Format Functions ##############################
function getStartEndTime(cw_event, stringonly){
if(cw_event.allday){
if (stringonly) {
return "All Day";
} else {
return "
"+"All"+"
"+"
"+"Day"+"
";
}
}
else{
var mil = gIsMilitary == '1';
if(mil){
var endText = "";
if(!cw_event.noend){
endText = " " + dateFormat(tsToDate(cw_event, "end_timestamp_gmt", "end_tzoffset"), "UTC:HH:MM");
}
var startText = dateFormat(tsToDate(cw_event, "start_timestamp_gmt", "start_tzoffset"), "UTC:HH:MM");
if (stringonly) {
return startText+" - "+endText;
} else {
return '
";
}
}
}
}
function tsToDate(cw_event, tsPropertyName, timeZoneOffsetPropertyName){
return new Date((parseInt(cw_event[tsPropertyName]) + 3600*parseFloat(cw_event[timeZoneOffsetPropertyName])) * 1000)
}
//################################################## Navigation Functions ##########################################
function goToToday(){
cal.date = new Date();
g_cw_id = 0; // Clear the event id so does not scroll to event
if (currentView == 'calendar') {
// Display calendar and today's events below
if ($(".today").hasClass("disabled")) {
gselectedDateClass = 'disabled';
} else {
gselectedDateClass = 'enabled';
}
showCurrentView (currentView, true);
} else { // List view
showCurrentView (currentView, true);
}
}
//################################################## View Functions ##########################################
function showCurrentView (currentView, refresh) {
if(cal.date == undefined){
cal.date = new Date();
}
if ((gOrientation == 'landscape') && ($("#dayevents .daydatedisplay").length == 0)) {
// If orientation is landscape but no date spacers then show current view
// with refresh because may have been rotated to landscape after opening
// event detail view and need to make sure full list view is shown
refresh = true;
}
if ((currentView == 'list') || (gOrientation == 'landscape')) {
viewList(refresh);
} else {
viewCalendar(refresh);
}
gCurrentPlace = "events_cal_or_list";
display_action_row();
}
function viewList(refresh){
$("#filterdiv").hide();
$("#viewlist").hide();
if (gOrientation == 'landscape') {
$("#viewcalendar").hide();
} else {
currentView = 'list';
$("#viewcalendar").show();
}
$("#calendar").hide();
$("#dayevents").height(listcurrentfullht).show();
if (refresh) {
getListEvents(gCalendar, cal.date, 'beforeandafter');
}
// Note: Do not set current view to list because
// may just be in landscape orientation which forces
// full list view but want to keep currentView if it
// is calendar so re-orientation to portrait shows calendar
}
function viewCalendar(refresh){
$("#filterdiv").hide();
$("#viewlist").show();
$("#viewcalendar").hide();
$("#calendar").show();
$("#dayevents").css("height",listcalendarht).show();
if (refresh) {
getMonthEvents(gCalendar, cal.date);
getDayEvents(gCalendar, cal.date);
}
// Always set currentView to calendar here
currentView = 'calendar';
}
//###################### AJAX Functions ######################
function commonAjaxCallback(data, result){
if(data == undefined){
return false;
}
if(data.status.PHPSESSID){
gPHPSESSID = "?PHPSESSID=" + data.status.PHPSESSID;
gData = data;
}
cw_jsonStatus = data.status;
calendar_type = data.status.calendar_type;
gNoPublicCats = data.status.nopubliccats;
gLoggedIn = data.status.loggedin;
gCanAdd = data.status.canadd;
gIsAdmin = data.status.isadmin;
gIsManager = data.status.ismanager;
gNumCatsCalendar = data.status.numcatscalendar;
gNumCatsUser = data.status.numcatsuser;
//showPopup(); // Commented out so does not show popup message after login
if(data.status.code == 404){
$("#cal_id").val(gCalendar);
if (cal) {
$(cal.grid).hide();
}
$("#promptCalendar").show();
$("#navTodayAddLogInOut").hide();
return false;
} else if ((data.status.code == 401) || ((gNoPublicCats) && (!gLoggedIn))) {
$("#cal_id").val(gCalendar);
if (cal) {
$(cal.grid).hide();
}
$("#dayevents").hide();
$("#navTodayAddLogInOut").hide();
logincancelviz();
$("#loginform").show();
$("#username")[0].focus();
gDestinationScreen = undefined;
return false;
}
if (typeof data.settings != 'undefined') { // Not present if coming back from edit
gIsMilitary = data.settings.military;
gLocationPrefix = data.settings.location_prefix;
gTimeZone = data.settings.tz_env_name;
gTimeZoneOffset = data.settings.tzoffsetnodst;
gEventTitleGoMore = data.settings.etitle_gomore;
gEventTitleGoAction = data.settings.gomore_action;
gCalShowBusy = data.settings.calshowbusy;
gCalShowBusyText = data.settings.calshowbusytxt;
gBannersOn = data.settings.banners_on;
locations_only_admins = data.settings.locations_only_admins;
allowed_locations = data.settings.allowed_locations;
}
gLoggedIn = data.status.loggedin;
gCanAdd = data.status.canadd;
gIsAdmin = data.status.isadmin;
gIsManager = data.status.ismanager;
if (nolognavbar != 1) {
if(gLoggedIn){
$("#loginlink").hide();
$("#logoutlink").show();
}
else{
$("#loginlink").show();
$("#logoutlink").hide();
}
} else {
$("#loginlink").hide();
$("#logoutlink").hide();
}
return true;
}
//------------------
// ERROR HANDLING
// -----------------
function handleAjaxError(XMLHttpRequest, textStatus, errorThrown) {
$.hideIndicator(); // Juset in case still showing
if (gDebug_On) {
// Display error for diagnostic
document.write(textStatus + " " + errorThrown);
document.write("response: " + XMLHttpRequest.response + " ");
document.write("responseType: " + XMLHttpRequest.responseType + " ");
document.write("responseText: " + XMLHttpRequest.responseText + " ");
document.write("status: " + XMLHttpRequest.status + " ");
document.write("statusText: " + XMLHttpRequest.statusText + " ");
} else {
if (XMLHttpRequest.readyState == 4) {
// HTTP error (can be checked by XMLHttpRequest.status and XMLHttpRequest.statusText)
$.alert('Touch OK to continue','Processing Problem');
} else if (XMLHttpRequest.readyState == 0) {
// Network error (i.e. connection refused, access denied due to CORS, etc.)
$.alert('Check internet connection','Connection Problem');
} else {
// Unknown error
$.alert('Try again in a moment','Error Encountered');
}
}
}
function handleResponseError (data) {
$.hideIndicator(); // Just in case still showing
// Down for maintenance
if (data.status.code == 999) {
// Show alert
$.alert('Try again after maintenance completed','Offline For Maintenance');
// Show down for maintenance div
$('#downformaintenance').show();
} else if (data.status.code == 400) {
// Argument error, rate limit error, others?
$.alert(data.status.msg, 'Problem Found');
} else if (data.status.code == 401) {
// Hit rate limit, slow down
$.alert('Private calendar requires login.','');
} else {
// Display error alert
$.alert("Wait a minute and try again", "Server Error: "+data.status.code);
// Attempt to show current view (i.e. month grid with list or full list)
showCurrentView(currentView, false);
}
}
//------------------
// GLOBAL AJAX SETUP
// -----------------
// Checks time since last server access to determing if session
// is 'stale' (i.e. if session most likely ended on server). This
// is to prevent Android home screen app error when opened but not
// full refresh (i.e. warm or hot open on Android). Uses same var
// timeoutMilliseconds as conditional to determine if session stale
// This is called beforeSend for each ajax call by ajaxSetup
function sessionStale(){
var gNowTime = new Date().getTime(); // Get now data/time in milliseconds
// Check if over timeoutMilliseconds have passed since last ajax call
// and if so then do complete refresh of the mobile app. Uses the same
// var timeoutMilliseconds plus one minute (60000) as test since refresh
// should occur every timeoutMilliseconds
if ((gNowTime - gTimeLastAjax) > (timeoutMilliseconds + 60000)) {
location.reload(true);
return true;
} else {
gTimeLastAjax = new Date().getTime(); // move to commonajaxcallback?
return false;
}
}
$.ajaxSetup({
cache : false,
beforeSend: function (jqXHR) {
// Check if session older than session time out on server
// and if 'stale' then
if (sessionStale()) {
return false;
}
},
error:function(XMLHttpRequest, textStatus, errorThrown) {
handleAjaxError(XMLHttpRequest, textStatus, errorThrown);
}
});
// For diagnostics
// $( document ).ajaxSend(function( event, request, settings ) {
// alert(settings.url);
// });
// Show please wait image on ajaxt start
$(document).ajaxStart(function() {
$.showIndicator();
});
// Hide please wait image on ajaxt end
$( document ).ajaxStop(function() {
$.hideIndicator();
});
// ----------------------------------------------------------------------------------------------
// Function to get and set selected location
// ----------------------------------------------------------------------------------------------
function getlocation() {
var selectedValue = $("#location_id").val();
// DOM Global, remember last selected saved location id
last_saved_location_id = selectedValue;
if (selectedValue == 0) {
// If location id is zero then blank all location fields
$("#location_name").val('');
$("#location_city").val('');
$("#location_country").val('');
$("#location_stateprovince").val('');
$("#location_street").val('');
$("#location_zippostal").val('');
$("#location_no_conflicts").val(0);
// Change text in Saved Locations zero option to un-select
$("#location_id option[value='0']").text('Choose Saved Location');
} else {
// Event has saved location so get details from server
var parameters = {crd:gCalendar};
var theurl = "/calendars/editevent_getlocation.php" + gPHPSESSID + "&location_id=" + selectedValue;
$.getJSON(theurl, parameters,"json")
.done(function(data) {
statusObj = data.status;
$("#location_name").val(data.location.location_name);
$("#location_city").val(data.location.location_city);
$("#location_country").val(data.location.location_country);
$("#location_stateprovince").val(data.location.location_stateprovince);
$("#location_street").val(data.location.location_street);
$("#location_zippostal").val(data.location.location_zippostal);
// Clear location message span just in case warning was present from previous
// change to saved event information
$("#locmsg").hide();
// Note: Only setting flag for location not allowing conflicts, this is single location
// at this time but future needs to support multiple locations so if any location does
// not allow conflicts then flag is set.
$("#location_no_conflicts").val(data.location.location_no_conflicts);
// Change text in Saved Locations zero option to un-select
$("#location_id option[value='0']").text('De-Select Saved Location');
});
}
}
// If location text changes then set id to zero so no longer saved location
// Also display message offering to save new location as reusable Saved Location
function locchange(event) {
var locmsgdivid = 'locmsg'; // Default, location change message will show below location details
// See change event was triggered by click on button.
// The value of btnclicked is set onmousedown for each button
// because the onchange event will trigger before the click event
// on a button and we want to know which button is clicked here so
// we can adjust the behavior of the save location message depending
// on the button that was clicked. For example the cancel button just
// closes the editor, the save button just shows the Save Location
// message and does not save until touch Save again
if (btnclicked == 'cancelbtn') {
eventDone();
return true;
} else if (btnclicked == 'deletebtn') {
SaveEvent('delete');
return true;
}
// Reset
btnclicked = 'none';
var btntxt_add = '';
btntxt_add_1 = btntxt_add + 'Add as new Saved Location';
btntxt_add_2 = btntxt_add + 'Add ' + $("#location_name").val() + ' as new Saved Location';
var celltxt = '';
if (last_saved_location_id != 0) {
var btntxt_update = '';
btntxt_update += 'Update this Saved Location';
// This is a saved location that has been changed
$("#location_id").val(0); // Set location id to zeror, no longer saved location
$("#location_no_conflicts").val(0); // Only saved locations can have no conflicts set
celltxt = "Changes to saved location, " + $("#location_id option[value='" + last_saved_location_id + "']").text() + ", will only apply to this event.";
if ((gIsAdmin == '1') || (locations_only_admins == 0)) { // Aways allow admins to add / update locations or any who edit if only admins option not set
celltxt += ' You may: ' + btntxt_add_1 + ' ' + btntxt_update; // append add and/or update links to saved locations message
// Show location message and save as new location button
$("#"+locmsgdivid).css("display","block").html(celltxt);
return;
} else {
// Just show location message with no save as new location button
$("#"+locmsgdivid).css({"display":"block","background-color":"#fcf8e3","color":"#c09853"}).html(celltxt);
}
} else {
// Not a saved location changed, just location entered for event
// Show suggestion to add to Saved Locations
if ((gIsAdmin == '1') || ((locations_only_admins == 0))) { // If allowed to manage locations
// This is new location not derived from Saved Location
celltxt = "Suggestion: ";
celltxt += btntxt_add_2; // append add link to saved locations message
// Show location message and save as new location button
$("#"+locmsgdivid).css("display","inline-block").html(celltxt);
}
}
}
// Add location to reusable Save Location list
function addlocation(update_location_id) {
var current_locations = $("#location_id option").length;
if (current_locations >= allowed_locations) {
var alertstr = "You've exceeded your subscripiton limit of " + allowed_locations + " locations. ";
alertstr += 'Email support@calendarwiz.com for more locations.';
$.alert(alertstr, "Location Limit Exceeded");
return false;
}
var location_name = escape($("#location_name").val());
if (location_name == '') {
$.alert("Please enter a Location Name", "");
return false;
}
var location_street = escape($("#location_street").val());
var location_city = escape($("#location_city").val());
var location_stateprovince = escape($("#location_stateprovince").val());
var location_zippostal = escape($("#location_zippostal").val());
var location_country = escape($("#location_country").val());
var theurl = "calendars/ajax/save_location_from_editor.php?crd=" + gCalendar;
theurl += "&location_id=" + update_location_id;
theurl += "&location_name=" + location_name;
theurl += "&location_street=" + location_street;
theurl += "&location_city=" + location_city;
theurl += "&location_stateprovince=" + location_stateprovince;
theurl += "&location_zippostal=" + location_zippostal;
theurl += "&location_country=" + location_country;
theurl += "&PHPSESSID=" + gPHPSESSID;
// Do post to save the location
$.getJSON(theurl, function(data){
if (data.status.code == 200) {
// Get info for new location
var location_id = data.locationdetails.location_id;
var location_name = unescape(data.locationdetails.location_name);
if (update_location_id != 0) {
// ----------------------------------------
// UPDATED: Update location name in select
// ----------------------------------------
$('#location_id').find('option[value="' + location_id + '"]').text(location_name);
$('#location_id').val(location_id);
} else {
// -----------------------------------------------
// ADDED: Insert new location into location select
// -----------------------------------------------
// Insert new location into locations select options in alpha order
// Note value != 0 is for first 'Select From...' dummy option
var numsavedlocoptions = $('#location_id option').length;
if (numsavedlocoptions == 1) {
// This is first saved location so just append
$('#location_id').append('');
$('#location_id').val(location_id);
} else {
// Already one or more saved locations so insert in alphabetic order or to end
var locoptioncount = 0;
$('#location_id option').each(function() {
locoptioncount++;
if (locoptioncount == numsavedlocoptions){
// Just append to end
$('#location_id').append('');
$('#location_id').val(location_id);
return false;
} else if ((this.text.toLowerCase() > location_name.toLowerCase()) && (this.value != 0)) {
// Insert in alpha order
$(this).before('');
$('#location_id').val(location_id);
return false;
}
});
}
}
// Show brief success message - this div is defined in header.inc.php
if (data.locationdetails.result == 'added') {
// Added saved location
if($("#locmsg").is(":visible")){
$("#locmsg").html('Location Added Successfully').show().delay(4000).fadeOut();
}
} else {
// Updated saved location
if($("#locmsg").is(":visible")){
$("#locmsg").html('Location Updated Successfully').show().delay(4000).fadeOut();
}
}
numsavedlocoptions++;
// KEEP THIS CODE FOR FUTURE USE
//-----------------------------
// Update mirror locations list
//-----------------------------
/*
// Build item html
var mirrorlochtml = '
';
mirrorlochtml += '
';
// Insert item into mirror locations list
var nummirrorlocoptions = $('#mirrorloc_dialog .mirrorlocitem').length;
if (nummirrorlocoptions == 1) {
// This is first saved location so just append
$('#mirrorloc_dialog').append(mirrorlochtml);
} else {
// Already one or more saved locations so insert in alphabetic order or to end
var mirrorlocoptioncount = 0;
$('#mirrorloc_dialog .mirrorlocitem').each(function() {
mirrorlocoptioncount++;
if (mirrorlocoptioncount == nummirrorlocoptions){
// Just append to end
$('#mirrorloc_dialog').append(mirrorlochtml);
return false;
} else if ($(this).find(".mirrorlocname").text().toLowerCase().trim() > location_name.toLowerCase()) {
// Insert in alpha order
$(this).before(mirrorlochtml);
return false;
}
});
}
$("#mlocid_" + location_id).uniform();
$.uniform.update("#mlocid_" + location_id);
hidemirrorloc();
// Show mirror locations select button if more than two saved locations
if (numsavedlocoptions > 2) {
$("#mirrorlocdiv").show();
$("#mirrorloctoofew").hide();
}
*/
} else {
// Something went wrong
$.alert(data.status.msg, "");
}
});
}
//####################### Monthly Calendar ##################################
// Returns if date in calendar grid is disabled (i.e. has no events)
// Will call getMonthEvents to get data from server if no already stored
// in monthEvents object is not yet defined
function disabledDates(date){
//here we need to see if the date is already here. If not we need to get it
// if ($.active > 0) {
// return cwCal.FETCHING;
// }
if (monthEvents == undefined) {
getMonthEvents(gCalendar, cal.date);
return cwCal.FETCHING;
} else {
if(monthEvents[cal.dateToInt(date)] == undefined){
return cwCal.DISABLED;
} else {
return cwCal.ENABLED;
}
}
}
// Handle touch on calendar grid date. Highlights selected date
// and calls getDayEvents which gets and displays events on that day
function selectedDate(e){
var jCell = $(e.target);
// Set all day numbers to white background
$(".day_number").css("background","#fff")
.css("box-shadow","none")
.css("font-weight","normal");
if (typeof(jCell[0].date) == 'undefined') {
var jCell = $(e.currentTarget);
if (typeof(jCell[0].date) == 'undefined') {
var jCell = $(e.target).closest("td");
if (typeof(jCell[0].date) == 'undefined') {
return false;
}
}
}
// Set the selected day number to gray
jCell.find(".day_number").css("background","#fff")
.css("font-weight","bold")
.css("box-shadow","#dcdcdc 0 6px 9px 2px inset");
// Set his global variable for later
gselectedDateClass = jCell.find(".day_number").parent().attr('class');
// Go get events on this day and display in list below calendar grid
getDayEvents(gCalendar, jCell[0].date);
}
// Navigate to previous or next day based on swipe on list under
// calendar grid. Go to previous month if needed
function navDay(direction) {
if (direction == 'prev') {
cal.date.setDate(cal.date.getDate() - 1);
} else if (direction == 'next') {
cal.date.setDate(cal.date.getDate() + 1);
}
showCurrentView(currentView, true);
return;
}
// Begin Calendar Grid Functions
var cwCal = function(grid, disabledDates, selectedDate, clearCache, pleaseWait){
this.grid = grid;
this.date = null;
this.disabledDates = disabledDates;
this.selectedDate = selectedDate;
this.clearCache = clearCache;
var that = this;
this.navMonth = function(e){
if(typeof(e) == "string"){
var direction = e;
}else{
var direction = $(e.target).closest(".nav").attr("id");
}
// Get date object for middle of currently displayed month
var middate = new Date(that.date.getFullYear(),that.date.getMonth(),15,0,0,0,0);
if(direction.match(/next/i)){
// Add one month
middate.setMonth(middate.getMonth() + 1);
} else {
// Subtract one month
middate.setMonth(middate.getMonth() - 1);
}
// Reset currently displayed month to first of month
that.date = new Date(middate.getFullYear(),middate.getMonth(),1,0,0,0,0);
// Get calendar grid number of events on each day for new month
getMonthEvents(gCalendar, that.date);
// If new month is current month then go to today
var todayDate = new Date();
var todayMonth = todayDate.getMonth() - 1;
var thatMonth = that.date.getMonth() - 1;
var todayYear = todayDate.getFullYear();
var thatYear = that.date.getFullYear();
if ((todayMonth == thatMonth) && (todayYear == thatYear)) {
that.date = todayDate;
}
// Get events on current display date
getDayEvents(gCalendar, that.date);
}
// Bind previous and next month event to grid arrows
$(".nav").bind("click", this.navMonth);
this.dateToInt = function(date){
var month = date.getMonth() + 1;
if(month < 10){
month = "0" + month;
}
else{
month = month.toString();
}
var day = date.getDate();
if(day < 10){
day = "0" + day;
}
else{
day = day.toString();
}
return parseInt(date.getFullYear().toString() + month + day);
}
};
cwCal.months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
//cwCal.days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
cwCal.days = ["S", "M", "T", "W", "T", "F", "S"];
cwCal.ENABLED = 1;
cwCal.DISABLED = 0;
cwCal.FETCHING = -1;
// CALENDAR GRID RENDERING
cwCal.prototype.render = function(date, preserveCache){
gCameFromMonth = true;
if(!preserveCache){
clearCache();
}
if(date == undefined){
this.date = new Date();
}
else{
this.date = date;
}
// Set todays date vars
today = new Date();
todaydom = today.getDate();
todaymonth = today.getMonth();
todayyear = today.getFullYear();
todaydatestr = ""+todayyear+('0' + (todaymonth+1)).slice(-2)+('0' + (todaydom)).slice(-2);
// Set currently viewed months date vars
var initmonth = this.date.getMonth();
$("#month").text(dateFormat.i18n.monthNames[initmonth + 12] + " " + this.date.getFullYear());
// Note that start is incremented for each day being displayed in grid
var start = new Date(this.date);
var startmonth = start.getMonth();
start.setDate(1);
var dow = start.getDay();
start.setDate(start.getDate() - dow);
for(var i = 2; i < 8; i++){
for(var j = 0; j < 7; j++){
var dom = start.getDate();
var moy = start.getMonth();
var cell = this.grid.rows[i].cells[j];
var jCell = $(cell);
// Unbind click event, otherwise multiple bind occur
// on the jCell causing mutiple request to the server
jCell.unbind("click");
var cellspanhtml = '' + dom + '';
cell.innerHTML = cellspanhtml;
cell.className = "";
if((dom == todaydom) && (todaymonth == moy) && (todayyear == start.getFullYear())){
cell.className = "today ";
}
else if(moy != startmonth){
cell.className = "offmonth ";
}
if(j == 0 || j == 6){
cell.className += "weekend ";
}
if((dom == 1) && (moy == this.date.getMonth())) {
cell.className += "firstofmonth ";
}
var dateStatus = this.disabledDates(start);
// Get date for this cell, note that start is incremented at end of this loop
cell.date = new Date(start);
jCell.bind("click", this.selectedDate);
// Handle display if cell has events (ENABLED) or no events (DISABLED)
if(dateStatus == cwCal.DISABLED){
cell.className += "disabled";
cell.innerHTML += "
";
if (this.date.getTime() == start.getTime()) { gselectedDateClass = 'disabled'; }
}else if(dateStatus == cwCal.ENABLED){
cell.className += "enabled";
// Add html for small dot in cell indicating there are events on that cells day
cell.innerHTML +="
";
if (this.date.getTime() == start.getTime()) { gselectedDateClass = 'enabled'; }
}
else{
break;
}
start.setDate(dom + 1); // Increment start date object for next cell
}
if(dateStatus == cwCal.FETCHING)break;
}
for(j = 0; j < 7; j++){
this.grid.rows[1].cells[j].innerHTML = cwCal.days[j];
}
if(preserveCache){
$(this.grid).show();
}
// Calculate center of month grid
var offset = $(cal.grid).offset();
var width = $(cal.grid).width();
var height = $(cal.grid).height();
gGridCenterX = (offset.left + width / 2) - 15;
gGridCenterY = offset.top + height / 2;
}
// ------------------------------------
// EDIT EVENT FORM CONTROLS FUNCTIONS
// ------------------------------------
// Master function to set controls states depending on repeat
// type selected on page load or user selection
function initControls(){
enablerptchoices();
enableweekdays();
enablerptdate();
enablerptfields();
if(cw_jsonStatus.view == "a"){
$("#deleteButton").hide();
$("#deleteButtonTop").hide();
}
else{
$("#deleteButton").show();
$("#deleteButtonTop").show();
}
}
function docontrols() {
// Single occurrence of repeating event
if (document.eventform.edittype.value == "single") {
disablerptchoices();
disablerptdate();
disablerptfields();
disableweekdays();
donoenddate();
return;
}
// No repeating event
if (document.eventform.rpt_type.selectedIndex == 0) { // No repeats
disablerptdate();
disablerptfields();
disableweekdays();
clearweekdays();
donoenddate();
disablerptdate();
}
// Repeat options below
if (document.eventform.rpt_type.selectedIndex == 1) { // Daily
disablerptfields();
disableweekdays();
clearweekdays();
donoenddate();
}
if (document.eventform.rpt_type.selectedIndex == 2){ // Once Every # Days
disableweekdays();
clearweekdays();
disablerptfields();
document.eventform.rpt_days.disabled = false;
$("#rpt_days").show();
donoenddate();
$(document.eventform.rpt_type.options[2]).text("Every...");
}
else{
$(document.eventform.rpt_type.options[2]).text("Every # days");
}
if (document.eventform.rpt_type.selectedIndex == 3){ // Every # weeks by day
disablerptfields();
enableweekdays();
document.eventform.rpt_weeks.disabled = false;
$("#rpt_weeks").show();
donoenddate();
$(document.eventform.rpt_type.options[3]).text("Every...");
}
else{
$(document.eventform.rpt_type.options[3]).text("Every # weeks");
}
if (document.eventform.rpt_type.selectedIndex == 4) { // Monthly by day
disablerptfields();
enableweekdays();
document.eventform.rpt_month_place.disabled = false;
$("#rpt_month_place").show();
donoenddate();
}
if (document.eventform.rpt_type.selectedIndex == 5) { // Monthly by date
disablerptfields();
disableweekdays();
clearweekdays();
donoenddate();
}
if (document.eventform.rpt_type.selectedIndex == 6) { // Yearly by date
disablerptfields();
disableweekdays();
clearweekdays();
donoenddate();
}
return;
}
// Additional Controls Context Function Called By docontrols
// Disable repeat fields
function disablerptfields() {
document.eventform.rpt_weeks.disabled=true;
$("#rpt_weeks").hide();
document.eventform.rpt_days.disabled=true;
$("#rpt_days").hide();
document.eventform.rpt_month_place.disabled=true;
$("#rpt_month_place").hide();
}
// Enable repeat fields - for posting
function enablerptfields() {
document.eventform.rpt_weeks.disabled=false;
$("#rpt_weeks").show();
document.eventform.rpt_days.disabled=false;
$("#rpt_days").show();
document.eventform.rpt_month_place.disabled=false;
$("#rpt_month_place").show();
}
// Disable repeat date
function disablerptdate() {
document.eventform.rpt_end_day.disabled=true;
document.eventform.rpt_end_month.disabled=true;
document.eventform.rpt_end_year.disabled=true;
$("#enddate").hide();
}
// Enable repeat date
function enablerptdate() {
document.eventform.rpt_end_day.disabled=false;
document.eventform.rpt_end_month.disabled=false;
document.eventform.rpt_end_year.disabled=false;
$("#enddate").show();
}
//Disable weekdays
function disableweekdays() {
// Disable check boxes
document.eventform.rpt_sun.disabled=true;
document.eventform.rpt_mon.disabled=true;
document.eventform.rpt_tue.disabled=true;
document.eventform.rpt_wed.disabled=true;
document.eventform.rpt_thu.disabled=true;
document.eventform.rpt_fri.disabled=true;
document.eventform.rpt_sat.disabled=true;
$("#selectbydays").hide();
}
// Enable weekdays
function enableweekdays() {
document.eventform.rpt_sun.disabled=false;
document.eventform.rpt_mon.disabled=false;
document.eventform.rpt_tue.disabled=false;
document.eventform.rpt_wed.disabled=false;
document.eventform.rpt_thu.disabled=false;
document.eventform.rpt_fri.disabled=false;
document.eventform.rpt_sat.disabled=false;
$("#selectbydays").show();
}
// Clear weekdays
function clearweekdays () {
document.eventform.rpt_sun.checked=false;
document.eventform.rpt_mon.checked=false;
document.eventform.rpt_tue.checked=false;
document.eventform.rpt_wed.checked=false;
document.eventform.rpt_thu.checked=false;
document.eventform.rpt_fri.checked=false;
document.eventform.rpt_sat.checked=false;
}
// Disable all repeat choices
function disablerptchoices() {
$("#repeat_disabled_note").show();
$("#rpt_type").removeClass("dropdown-small").addClass("dropdown-disabled").attr("disabled", true);
}
// Enable all repeat choices
function enablerptchoices() {
$("#repeat_disabled_note").hide();
$("#rpt_type").removeClass("dropdown-disabled").addClass("dropdown-small").attr("disabled", false);
}
// Process click on allday checkbox
function doallday() {
if (document.eventform.allday.checked) {
document.eventform.noend.checked = false;
disabletimes();
return;
} else if (!document.eventform.noend.checked) {
enabletimes();
return;
}
}
// Process click on noend (time) checkbox
function donoend() {
if (document.eventform.noend.checked) {
document.eventform.allday.checked = false;
document.eventform.endhour.disabled = true;
document.eventform.endminute.disabled = true;
if (document.eventform.endampm) { document.eventform.endampm.disabled = true; }
document.eventform.starthour.disabled = false;
document.eventform.startminute.disabled = false;
if (document.eventform.startampm) { document.eventform.startampm.disabled = false; }
return;
} else if (!document.eventform.allday.checked) {
enabletimes();
return;
}
}
// Process change to no end date checkbox
function donoenddate() {
// If editing single occurrence of repeating event
if (document.eventform.edittype.value == "single") {
document.eventform.rpt_noenddate.disabled = true;
$("#noenddate").hide();
return;
}
// If yearly repeats not checked then disable
if (document.eventform.rpt_type.selectedIndex != 6) {
document.eventform.rpt_noenddate.checked = false;
document.eventform.rpt_noenddate.disabled = true;
$("#noenddate").hide();
}
else {
//yearly repeats is checked
document.eventform.rpt_noenddate.disabled = false;
$("#noenddate").show();
}
if (document.eventform.rpt_noenddate.checked) {
disablerptdate();
} else {
enablerptdate();
}
}
function disabletimes() {
document.eventform.starthour.disabled = true;
document.eventform.startminute.disabled = true;
if (document.eventform.startampm) { document.eventform.startampm.disabled = true; }
document.eventform.endhour.disabled = true;
document.eventform.endminute.disabled = true;
if (document.eventform.endampm) { document.eventform.endampm.disabled = true; }
}
function enabletimes() {
document.eventform.starthour.disabled = false;
document.eventform.startminute.disabled = false;
if (document.eventform.startampm) { document.eventform.startampm.disabled = false; }
document.eventform.endhour.disabled = false;
document.eventform.endminute.disabled = false;
if (document.eventform.endampm) { document.eventform.endampm.disabled = false; }
}
endDateIsDirty = false; // Init, used by doDateSync function
// Function to sync start and end dates when banners on
function doDateSync(e) {
var targetId = e.target.id;
// Make sure start date is before End Date (single event or banner end date)
var startday = Number(document.eventform.eday.value);
var startmonth = Number(document.eventform.emonth.value);
var startyear = Number(document.eventform.eyear.value);
var starthour = Number(document.eventform.starthour.value);
var startminute = Number(document.eventform.startminute.value);
var endday = Number(document.eventform.end_eday.value);
var endmonth = Number(document.eventform.end_emonth.value);
var endyear = Number(document.eventform.end_eyear.value);
var endhour = Number(document.eventform.endhour.value);
var endminute = Number(document.eventform.endminute.value);
var starttimestamp = new Date(startyear,startmonth,startday);
var endtimestamp = new Date(endyear,endmonth,endday);
if (starttimestamp != endtimestamp) {
if ((targetId == 'eday') || (targetId == 'emonth') || (targetId == 'eyear')) {
var startDateChanged = true;
setNumDays('e');
} else if ((targetId == 'end_eday') || (targetId == 'end_emonth') || (targetId == 'end_eyear')) {
var endDateChanged = true;
endDateIsDirty = true;
setNumDays('end_e');
}
if ((startDateChanged) && (!endDateIsDirty)) {
$("#end_eday").val(startday);
$("#end_emonth").val(startmonth);
$("#end_eyear").val(startyear);
setNumDays('end_e');
return;
}
}
}
function weeksDaysUpdate(){
var weeks = $("#rpt_weeks").val();
if(weeks == "1"){
weeks = "week";
}
else{
weeks = weeks + " weeks";
}
$("#rpt_type option:eq(3)").text("Every " + weeks + " on these days");
var days = $("#rpt_days").val();
if(days == "2"){
days = "other day";
}
else{
days = days + " days";
}
$("#rpt_type option:eq(2)").text("Every " + days);
}
// END OF EDIT EVENT FORM CONTROLS //
// BEGIN EDIT EVENT FORM VALIDATION FUNCTIONS
// Utility function to display validation message
function cwscroll (positionselector) {
var offset = $(positionselector).offset();
$('html,body').animate({ scrollTop: offset.top - 100 }, { duration: 'slow', easing: 'swing'});
}
// Validate edit event fields before saving
function validate() {
// --------------------------------------------------------
// Dates and Times Validation
// --------------------------------------------------------
// Check text fields
var value = $("#title").val();
if (value == "") {
document.eventform.title.focus();
$.alert("Please enter an event Title", "");
return false;
}
// Check that category was selected
var value = document.eventform.category_id.value;
if (value == "0") {
document.eventform.category_id.focus();
$.alert("Please select a Category", "");
return false;
}
var isMilitaryTime = !$("#startampm").is(":visible"); // If startam field visible then NOT military
if (gbanners_on == '1') {
// -----------
// BANNERS ON
// -----------
// Get date and time vars
var startday = Number(document.eventform.eday.value);
var startmonth = Number(document.eventform.emonth.value);
var startyear = Number(document.eventform.eyear.value);
var starthour = Number(document.eventform.starthour.value);
var startminute = Number(document.eventform.startminute.value);
var endday = Number(document.eventform.end_eday.value);
var endmonth = Number(document.eventform.end_emonth.value);
var endyear = Number(document.eventform.end_eyear.value);
var endhour = Number(document.eventform.endhour.value);
var endminute = Number(document.eventform.endminute.value);
if (!isMilitaryTime) { // If not military or 24 hour format
// Need to convert to 24 hour format
var startampm = document.eventform.startampm.value;
var endampm = document.eventform.endampm.value;
// Convert start hour to military time
if ((startampm == "am") && (starthour == 12)) {
starthour = starthour - 12;
} else if ((startampm == "pm") && (starthour != 12)) {
starthour = starthour + 12;
}
// Convert end hour to military time
if ((endampm == "am") && (endhour == 12)) {
endhour = endhour - 12;
} else if ((endampm == "pm") && (endhour != 12)) {
endhour = endhour + 12;
}
} else {
var startampm = "";
var endampm = "";
}
// Get just date timestamps not times
var startdaytimestamp = new Date(startyear,startmonth,startday);
var enddaytimestamp = new Date(endyear,endmonth,endday);
// If overnight shift then make end on next day
if ((starthour >= 12) && (endhour < 12)) {
var tendstamp = enddaytimestamp.getTime() + (1 * 24 * 60 * 60 * 1000);
enddaytimestamp.setTime(tendstamp);
}
// Make sure end date not before start date
if (enddaytimestamp < startdaytimestamp) {
cwscroll ("#eday");
$.alert("Start Date is after End Date","");
return false;
}
// Get number of days spanned
var days_span = ((enddaytimestamp - startdaytimestamp) / 86400000) + 1; // Get days spanned by event
if (days_span == 1) { // if not banner event
if ((document.eventform.noend.checked == false) && (document.eventform.allday.checked == false)) {
// Not banner and not allday or not end so need to check event times
// Get start and end timestamps including times
var starttimetimestamp = new Date(startyear,startmonth,startday,starthour,startminute);
var endtimetimestamp = new Date(endyear,endmonth,endday,endhour,endminute);
// Make sure single day / non banner event start time not equal or after end time
if (endtimetimestamp <= starttimetimestamp) {
cwscroll ("#starthour");
$.alert("Start Time is same or after End Time","");
return false;
}
}
} else { // If banner event
// Is banner event so check not exceeding maximum days span
// Check the date range, 86400000 is the number of milliseconds in one day
var ban_days_back = "";
if (days_span > ban_days_back) {
cwscroll ("#eyear");
$.alert("Individual events cannot span more than " + ban_days_back + " days","");
return false;
}
}
} else {
// BANNERS OFF
// Check event start time is before end time on a day
// Modified to skip pm to am event times to allow
// events starting at say 11:00pm and ending at 2:00am
// the next day.
veday = Number(document.eventform.eday.value);
vemonth = Number(document.eventform.emonth.value) - 1; // Months are 0-11 in javascript
veyear = Number(document.eventform.eyear.value);
vstarthour = Number(document.eventform.starthour.value);
vstartminute = Number(document.eventform.startminute.value);
vendhour = Number(document.eventform.endhour.value);
vendminute = Number(document.eventform.endminute.value);
if (!isMilitaryTime) {
vstartampm = document.eventform.startampm.value;
vendampm = document.eventform.endampm.value;
// Convert start hour to military time
if ((vstartampm == "am") && (vstarthour == 12)) {
vstarthour = vstarthour - 12;
} else if ((vstartampm == "pm") && (vstarthour != 12)) {
vstarthour = vstarthour + 12;
}
// Convert end hour to military time
if ((vendampm == "am") && (vendhour == 12)) {
vendhour = vendhour - 12;
} else if ((vendampm == "pm") && (vendhour != 12)) {
vendhour = vendhour + 12;
}
}
vstartstamp = new Date(veyear,vemonth,veday,vstarthour,vstartminute,0);
vendstamp = new Date(veyear,vemonth,veday,vendhour,vendminute,0);
// If overnight shift then make end on next day
if ((vstarthour >= 12) && (vendhour < 12)) {
var tendstamp = vendstamp.getTime();
tendstamp = tendstamp + (1 * 24 * 60 * 60 * 1000);
vendstamp.setTime(tendstamp);
}
if ((document.eventform.noend.checked == false) && (document.eventform.allday.checked == false)) { // Skip if no end time or allday
if (vstartstamp.getTime() >= vendstamp.getTime()) {
$.alert("Start Time is equal to or after End Time","");
return false;
}
}
}
// If repeating and not editing single occurrence, check start date is before end date
if ((!document.eventform.rpt_type.selectedIndex == 0) && (document.eventform.edittype.value != "single") && (!document.eventform.rpt_noenddate.checked)) {
var startday = Number(document.eventform.eday.value);
var startmonth = Number(document.eventform.emonth.value) - 1;
var startyear = Number(document.eventform.eyear.value);
var starthour = Number(document.eventform.starthour.value);
var startminute = Number(document.eventform.startminute.value);
var rptendday = Number(document.eventform.rpt_end_day.value);
var rptendmonth = Number(document.eventform.rpt_end_month.value) - 1;
var rptendyear = Number(document.eventform.rpt_end_year.value);
var endhour = Number(document.eventform.endhour.value);
var endminute = Number(document.eventform.endminute.value);
var starttimestamp = new Date(startyear,startmonth,startday);
var rptendtimestamp = new Date(rptendyear,rptendmonth,rptendday);
if (starttimestamp >= rptendtimestamp) {
$.alert("Start Date is equal to or later than Repeat End Date","");
return false;
}
}
// For applicable options, maker sure day of week(s) are checked
if ((document.eventform.rpt_type.selectedIndex == 3) || (document.eventform.rpt_type.selectedIndex == 4)) {
var nd = 0;
if (document.eventform.rpt_sun.checked) { nd++; }
if (document.eventform.rpt_mon.checked) { nd++; }
if (document.eventform.rpt_tue.checked) { nd++; }
if (document.eventform.rpt_wed.checked) { nd++; }
if (document.eventform.rpt_thu.checked) { nd++; }
if (document.eventform.rpt_fri.checked) { nd++; }
if (document.eventform.rpt_sat.checked) { nd++; }
if (nd == 0) {
$.alert("Please select days of week to complete repeating event","");
return false;
}
}
return true;
}
// END OF EDIT EVENT FORM VALIDATION
/**
* Determine the mobile operating system.
* This function returns one of 'iOS', 'Android', 'Windows Phone', or 'unknown'.
*
* @returns {String}
*/
function getMobileOperatingSystem() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
// Windows Phone must come first because its UA also contains "Android"
if (/windows phone/i.test(userAgent)) {
return "Windows Phone";
}
if (/android/i.test(userAgent)) {
return "Android";
}
// iOS detection from: http://stackoverflow.com/a/9039885/177710
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
return "iOS";
}
return "unknown";
}
// Ulility function to change newlines to
function nl2br (str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? ' ' : ' ';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
}
// A call to this function is in commonAjaxCallback() NOW COMMENTED OUT OF COMMONAJAXCALLBACK
// just after gLoggedIn is assigned.
function showPopup() {
if (gLoggedIn && readCookie("cw_mobile_pop_dismissed") != "true") {
$("#popup_message").fadeIn("slow");
}
}
function dismissPopup() {
$("#popup_message").fadeOut();
createCookie("cw_mobile_pop_dismissed", "true",1000);
}
// As last resort puts debug text into div at bottom of page
function debug(txt){
$("#debug").text($("#debug").text() + " " + txt);
}
/*
* Date Format 1.2.3
* (c) 2007-2009 Steven Levithan
* MIT license
*
* Includes enhancements by Scott Trenda
* and Kris Kowal
*
* Accepts a date, a mask, or a date and a mask.
* Returns a formatted version of the given date.
* The date defaults to the current date/time.
* The mask defaults to dateFormat.masks.default.
*/
function pad(val, len) {
val = String(val);
len = len || 2;
while (val.length < len) val = "0" + val;
return val;
};
var dateFormat = function () {
var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
timezoneClip = /[^-+\dA-Z]/g;
// Regexes and supporting functions are cached through closure
return function (date, mask, utc) {
var dF = dateFormat;
// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
mask = date;
date = undefined;
}
// Passing date through Date applies Date.parse, if necessary
date = date ? new Date(date) : new Date;
if (isNaN(date)) throw SyntaxError("invalid date");
mask = String(dF.masks[mask] || mask || dF.masks["default"]);
// Allow setting the utc argument via the mask
if (mask.slice(0, 4) == "UTC:") {
mask = mask.slice(4);
utc = true;
}
var _ = utc ? "getUTC" : "get",
d = date[_ + "Date"](),
D = date[_ + "Day"](),
m = date[_ + "Month"](),
y = date[_ + "FullYear"](),
H = date[_ + "Hours"](),
M = date[_ + "Minutes"](),
s = date[_ + "Seconds"](),
L = date[_ + "Milliseconds"](),
o = utc ? 0 : date.getTimezoneOffset(),
flags = {
d: d,
dd: pad(d),
ddd: dF.i18n.dayNames[D],
dddd: dF.i18n.dayNames[D + 7],
m: m + 1,
mm: pad(m + 1),
mmm: dF.i18n.monthNames[m],
mmmm: dF.i18n.monthNames[m + 12],
yy: String(y).slice(2),
yyyy: y,
h: H % 12 || 12,
hh: pad(H % 12 || 12),
H: H,
HH: pad(H),
M: M,
MM: pad(M),
s: s,
ss: pad(s),
l: pad(L, 3),
L: pad(L > 99 ? Math.round(L / 10) : L),
t: H < 12 ? "a" : "p",
tt: H < 12 ? "am" : "pm",
T: H < 12 ? "A" : "P",
TT: H < 12 ? "AM" : "PM",
Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
};
return mask.replace(token, function ($0) {
return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
});
};
}();
// Some common format strings
dateFormat.masks = {
"default": "ddd mmm dd yyyy HH:MM:ss",
shortDate: "m/d/yy",
mediumDate: "mmm d, yyyy",
longDate: "mmmm d, yyyy",
fullDate: "dddd, mmmm d, yyyy",
shortTime: "h:MM TT",
mediumTime: "h:MM:ss TT",
longTime: "h:MM:ss TT Z",
isoDate: "yyyy-mm-dd",
isoTime: "HH:MM:ss",
isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};
// Internationalization strings
dateFormat.i18n = {
dayNames: [
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
],
monthNames: [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
]
};
dateFormat.daysInMonth = [0,31,28,31,30,31,30,31,31,30,31,30,31];
// For convenience...
Date.prototype.format = function (mask, utc) {
return dateFormat(this, mask, utc);
};
// END OF Date Format 1.2.3
/**
* jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!)
* Common usage: wipe images (left and right to show the previous or next image)
*
* @author Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
* @version 1.1.1 (9th December 2010) - fix bug (older IE's had problems)
* @version 1.1 (1st September 2010) - support wipe up and wipe down
* @version 1.0 (15th July 2010)
* THIS MODIFIED BY DWM 161031 TO FIX INCORRECT SWIPE RIGHT WHEN DAY VIEW WITH
* LONG LIST OF EVENTS WHEN SLIGHT DIAGONAL IN SWIPE DOWN CAUSING SWIPE ON X AXIS
*/
(function($) {
$.fn.touchwipe = function(settings) {
var config = {
min_move_x: 75,
min_move_y: 75,
wipeLeft: function() { },
wipeRight: function() { },
wipeUp: function() { },
wipeDown: function() { },
preventDefaultEvents: true
};
if (settings) $.extend(config, settings);
this.each(function() {
var startX;
var startY;
var isMoving = false;
var wasWipe = false;
var target;
function cancelTouch() {
this.removeEventListener('touchmove', onTouchMove);
isMoving = false;
wasWipe = true;
}
function wipeX(dx) {
wasWipe = true;
cancelTouch();
if(dx > 0) {
config.wipeLeft();
}
else {
config.wipeRight();
}
}
function wipeY(dy) {
wasWipe = true;
cancelTouch();
if(dy > 0) {
config.wipeDown();
}
else {
config.wipeUp();
}
}
function onTouchMove(e) {
if(config.preventDefaultEvents) {
e.preventDefault();
}
if(isMoving) {
var x = e.touches[0].pageX;
var y = e.touches[0].clientY;
var dx = startX - x;
var dy = startY - y;
var dxabs = Math.abs(dx);
var dyabs = Math.abs(dy);
if (dxabs >= config.min_move_x) { var dxmax = true; } else { var dxmax = false; }
if (dyabs >= config.min_move_y) { var dymax = true; } else { var dymax = false; }
if ((dxmax) && (dymax)) {
if (dxabs > dyabs) {
wipeX(dx);
} else {
wipeY(dy);
}
} else if (dxmax) {
wipeX(dx);
} else if (dymax) {
wipeY(dy);
}
}
}
function onTouchEnd(e){
if(!wasWipe){
config.click({target:target});
}
this.removeEventListener('touchend', onTouchEnd);
}
function onTouchStart(e)
{
if (e.touches.length == 1) {
startX = e.touches[0].pageX;
startY = e.touches[0].clientY;
target = e.touches[0].target;
isMoving = true;
wasWipe = false;
this.addEventListener('touchmove', onTouchMove, false);
if(config.click){
this.addEventListener('touchend', onTouchEnd, false);
}
}
}
if ('ontouchstart' in document.documentElement) {
this.addEventListener('touchstart', onTouchStart, false);
}
});
// END OF jQuery Plugin to obtain touch gestures
// Detects focus and blur on form fields and corrects visibility
// issues with navigtion bars - maybe only needed for iOS
if(/ipad|iphone|ipod/i.test(navigator.userAgent.toLowerCase())) {
$(document).on('focus', 'input, textarea, select', function() {
$(".action_row").hide();
});
$(document).on('blur', 'input, textarea, select', function() {
if ($("#loginform").is(':hidden')) { // login form does not have action_row
display_action_row();
}
});
}
return this;
};
})(jQuery);
// Include email validaton script
// email-addresses.js - RFC 5322 email address parser
// v 3.0.1
// From: https://github.com/jackbearheart/email-addresses
//
// http://tools.ietf.org/html/rfc5322
//
// This library does not validate email addresses.
// emailAddresses attempts to parse addresses using the (fairly liberal)
// grammar specified in RFC 5322.
//
// email-addresses returns {
// ast: ,
// addresses: [{
// node: ,
// name: ,
// address: ,
// local: ,
// domain:
// }, ...]
// }
//
// emailAddresses.parseOneAddress and emailAddresses.parseAddressList
// work as you might expect. Try it out.
//
// Many thanks to Dominic Sayers and his documentation on the is_email function,
// http://code.google.com/p/isemail/ , which helped greatly in writing this parser.
function checkemailaddress(emailstr) {
if (emailstr == '') { return false; }
// Get parts of email address
emailobj = emailAddresses.parseOneAddress(emailstr);
//emailobj = MyCwCal.emailAddresses.parseOneAddress(emailstr)
//emailobj = this.emailAddresses.parseOneAddress(emailstr);
if (emailobj && emailobj !== 'null' && emailobj !== 'undefined') {
nodisplaynameemailstr = emailobj.address;
} else {
nodisplaynameemailstr = 'invalid';
}
// Check for correct email address format
var filter = /^([a-zA-Z0-9_+'\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
if (!filter.test(nodisplaynameemailstr)) {
return false;
} else {
return true;
}
}
(function (global) {
"use strict";
function parse5322(opts) {
// tokenizing functions
function inStr() { return pos < len; }
function curTok() { return parseString[pos]; }
function getPos() { return pos; }
function setPos(i) { pos = i; }
function nextTok() { pos += 1; }
function initialize() {
pos = 0;
len = parseString.length;
}
// parser helper functions
function o(name, value) {
return {
name: name,
tokens: value || "",
semantic: value || "",
children: []
};
}
function wrap(name, ast) {
var n;
if (ast === null) { return null; }
n = o(name);
n.tokens = ast.tokens;
n.semantic = ast.semantic;
n.children.push(ast);
return n;
}
function add(parent, child) {
if (child !== null) {
parent.tokens += child.tokens;
parent.semantic += child.semantic;
}
parent.children.push(child);
return parent;
}
function compareToken(fxnCompare) {
var tok;
if (!inStr()) { return null; }
tok = curTok();
if (fxnCompare(tok)) {
nextTok();
return o('token', tok);
}
return null;
}
function literal(lit) {
return function literalFunc() {
return wrap('literal', compareToken(function (tok) {
return tok === lit;
}));
};
}
function and() {
var args = arguments;
return function andFunc() {
var i, s, result, start;
start = getPos();
s = o('and');
for (i = 0; i < args.length; i += 1) {
result = args[i]();
if (result === null) {
setPos(start);
return null;
}
add(s, result);
}
return s;
};
}
function or() {
var args = arguments;
return function orFunc() {
var i, result, start;
start = getPos();
for (i = 0; i < args.length; i += 1) {
result = args[i]();
if (result !== null) {
return result;
}
setPos(start);
}
return null;
};
}
function opt(prod) {
return function optFunc() {
var result, start;
start = getPos();
result = prod();
if (result !== null) {
return result;
}
else {
setPos(start);
return o('opt');
}
};
}
function invis(prod) {
return function invisFunc() {
var result = prod();
if (result !== null) {
result.semantic = "";
}
return result;
};
}
function colwsp(prod) {
return function collapseSemanticWhitespace() {
var result = prod();
if (result !== null && result.semantic.length > 0) {
result.semantic = " ";
}
return result;
};
}
function star(prod, minimum) {
return function starFunc() {
var s, result, count, start, min;
start = getPos();
s = o('star');
count = 0;
min = minimum === undefined ? 0 : minimum;
while ((result = prod()) !== null) {
count = count + 1;
add(s, result);
}
if (count >= min) {
return s;
}
else {
setPos(start);
return null;
}
};
}
// One expects names to get normalized like this:
// " First Last " -> "First Last"
// "First Last" -> "First Last"
// "First Last" -> "First Last"
function collapseWhitespace(s) {
return s.replace(/([ \t]|\r\n)+/g, ' ').replace(/^\s*/, '').replace(/\s*$/, '');
}
// UTF-8 pseudo-production (RFC 6532)
// RFC 6532 extends RFC 5322 productions to include UTF-8
// using the following productions:
// UTF8-non-ascii = UTF8-2 / UTF8-3 / UTF8-4
// UTF8-2 =
// UTF8-3 =
// UTF8-4 =
//
// For reference, the extended RFC 5322 productions are:
// VCHAR =/ UTF8-non-ascii
// ctext =/ UTF8-non-ascii
// atext =/ UTF8-non-ascii
// qtext =/ UTF8-non-ascii
// dtext =/ UTF8-non-ascii
function isUTF8NonAscii(tok) {
// In JavaScript, we just deal directly with Unicode code points,
// so we aren't checking individual bytes for UTF-8 encoding.
// Just check that the character is non-ascii.
return tok.charCodeAt(0) >= 128;
}
// common productions (RFC 5234)
// http://tools.ietf.org/html/rfc5234
// B.1. Core Rules
// CR = %x0D
// ; carriage return
function cr() { return wrap('cr', literal('\r')()); }
// CRLF = CR LF
// ; Internet standard newline
function crlf() { return wrap('crlf', and(cr, lf)()); }
// DQUOTE = %x22
// ; " (Double Quote)
function dquote() { return wrap('dquote', literal('"')()); }
// HTAB = %x09
// ; horizontal tab
function htab() { return wrap('htab', literal('\t')()); }
// LF = %x0A
// ; linefeed
function lf() { return wrap('lf', literal('\n')()); }
// SP = %x20
function sp() { return wrap('sp', literal(' ')()); }
// VCHAR = %x21-7E
// ; visible (printing) characters
function vchar() {
return wrap('vchar', compareToken(function vcharFunc(tok) {
var code = tok.charCodeAt(0);
var accept = (0x21 <= code && code <= 0x7E);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
}));
}
// WSP = SP / HTAB
// ; white space
function wsp() { return wrap('wsp', or(sp, htab)()); }
// email productions (RFC 5322)
// http://tools.ietf.org/html/rfc5322
// 3.2.1. Quoted characters
// quoted-pair = ("\" (VCHAR / WSP)) / obs-qp
function quotedPair() {
var qp = wrap('quoted-pair',
or(
and(literal('\\'), or(vchar, wsp)),
obsQP
)());
if (qp === null) { return null; }
// a quoted pair will be two characters, and the "\" character
// should be semantically "invisible" (RFC 5322 3.2.1)
qp.semantic = qp.semantic[1];
return qp;
}
// 3.2.2. Folding White Space and Comments
// FWS = ([*WSP CRLF] 1*WSP) / obs-FWS
function fws() {
return wrap('fws', or(
obsFws,
and(
opt(and(
star(wsp),
invis(crlf)
)),
star(wsp, 1)
)
)());
}
// ctext = %d33-39 / ; Printable US-ASCII
// %d42-91 / ; characters not including
// %d93-126 / ; "(", ")", or "\"
// obs-ctext
function ctext() {
return wrap('ctext', or(
function ctextFunc1() {
return compareToken(function ctextFunc2(tok) {
var code = tok.charCodeAt(0);
var accept =
(33 <= code && code <= 39) ||
(42 <= code && code <= 91) ||
(93 <= code && code <= 126);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
});
},
obsCtext
)());
}
// ccontent = ctext / quoted-pair / comment
function ccontent() {
return wrap('ccontent', or(ctext, quotedPair, comment)());
}
// comment = "(" *([FWS] ccontent) [FWS] ")"
function comment() {
return wrap('comment', and(
literal('('),
star(and(opt(fws), ccontent)),
opt(fws),
literal(')')
)());
}
// CFWS = (1*([FWS] comment) [FWS]) / FWS
function cfws() {
return wrap('cfws', or(
and(
star(
and(opt(fws), comment),
1
),
opt(fws)
),
fws
)());
}
// 3.2.3. Atom
//atext = ALPHA / DIGIT / ; Printable US-ASCII
// "!" / "#" / ; characters not including
// "$" / "%" / ; specials. Used for atoms.
// "&" / "'" /
// "*" / "+" /
// "-" / "/" /
// "=" / "?" /
// "^" / "_" /
// "`" / "{" /
// "|" / "}" /
// "~"
function atext() {
return wrap('atext', compareToken(function atextFunc(tok) {
var accept =
('a' <= tok && tok <= 'z') ||
('A' <= tok && tok <= 'Z') ||
('0' <= tok && tok <= '9') ||
(['!', '#', '$', '%', '&', '\'', '*', '+', '-', '/',
'=', '?', '^', '_', '`', '{', '|', '}', '~'].indexOf(tok) >= 0);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
}));
}
// atom = [CFWS] 1*atext [CFWS]
function atom() {
return wrap('atom', and(colwsp(opt(cfws)), star(atext, 1), colwsp(opt(cfws)))());
}
// dot-atom-text = 1*atext *("." 1*atext)
function dotAtomText() {
var s, maybeText;
s = wrap('dot-atom-text', star(atext, 1)());
if (s === null) { return s; }
maybeText = star(and(literal('.'), star(atext, 1)))();
if (maybeText !== null) {
add(s, maybeText);
}
return s;
}
// dot-atom = [CFWS] dot-atom-text [CFWS]
function dotAtom() {
return wrap('dot-atom', and(invis(opt(cfws)), dotAtomText, invis(opt(cfws)))());
}
// 3.2.4. Quoted Strings
// qtext = %d33 / ; Printable US-ASCII
// %d35-91 / ; characters not including
// %d93-126 / ; "\" or the quote character
// obs-qtext
function qtext() {
return wrap('qtext', or(
function qtextFunc1() {
return compareToken(function qtextFunc2(tok) {
var code = tok.charCodeAt(0);
var accept =
(33 === code) ||
(35 <= code && code <= 91) ||
(93 <= code && code <= 126);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
});
},
obsQtext
)());
}
// qcontent = qtext / quoted-pair
function qcontent() {
return wrap('qcontent', or(qtext, quotedPair)());
}
// quoted-string = [CFWS]
// DQUOTE *([FWS] qcontent) [FWS] DQUOTE
// [CFWS]
function quotedString() {
return wrap('quoted-string', and(
invis(opt(cfws)),
invis(dquote), star(and(opt(colwsp(fws)), qcontent)), opt(invis(fws)), invis(dquote),
invis(opt(cfws))
)());
}
// 3.2.5 Miscellaneous Tokens
// word = atom / quoted-string
function word() {
return wrap('word', or(atom, quotedString)());
}
// phrase = 1*word / obs-phrase
function phrase() {
return wrap('phrase', or(obsPhrase, star(word, 1))());
}
// 3.4. Address Specification
// address = mailbox / group
function address() {
return wrap('address', or(mailbox, group)());
}
// mailbox = name-addr / addr-spec
function mailbox() {
return wrap('mailbox', or(nameAddr, addrSpec)());
}
// name-addr = [display-name] angle-addr
function nameAddr() {
return wrap('name-addr', and(opt(displayName), angleAddr)());
}
// angle-addr = [CFWS] "<" addr-spec ">" [CFWS] /
// obs-angle-addr
function angleAddr() {
return wrap('angle-addr', or(
and(
invis(opt(cfws)),
literal('<'),
addrSpec,
literal('>'),
invis(opt(cfws))
),
obsAngleAddr
)());
}
// group = display-name ":" [group-list] ";" [CFWS]
function group() {
return wrap('group', and(
displayName,
literal(':'),
opt(groupList),
literal(';'),
invis(opt(cfws))
)());
}
// display-name = phrase
function displayName() {
return wrap('display-name', function phraseFixedSemantic() {
var result = phrase();
if (result !== null) {
result.semantic = collapseWhitespace(result.semantic);
}
return result;
}());
}
// mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
function mailboxList() {
return wrap('mailbox-list', or(
and(
mailbox,
star(and(literal(','), mailbox))
),
obsMboxList
)());
}
// address-list = (address *("," address)) / obs-addr-list
function addressList() {
return wrap('address-list', or(
and(
address,
star(and(literal(','), address))
),
obsAddrList
)());
}
// group-list = mailbox-list / CFWS / obs-group-list
function groupList() {
return wrap('group-list', or(
mailboxList,
invis(cfws),
obsGroupList
)());
}
// 3.4.1 Addr-Spec Specification
// local-part = dot-atom / quoted-string / obs-local-part
function localPart() {
// note: quoted-string, dotAtom are proper subsets of obs-local-part
// so we really just have to look for obsLocalPart, if we don't care about the exact parse tree
return wrap('local-part', or(obsLocalPart, dotAtom, quotedString)());
}
// dtext = %d33-90 / ; Printable US-ASCII
// %d94-126 / ; characters not including
// obs-dtext ; "[", "]", or "\"
function dtext() {
return wrap('dtext', or(
function dtextFunc1() {
return compareToken(function dtextFunc2(tok) {
var code = tok.charCodeAt(0);
var accept =
(33 <= code && code <= 90) ||
(94 <= code && code <= 126);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
});
},
obsDtext
)()
);
}
// domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
function domainLiteral() {
return wrap('domain-literal', and(
invis(opt(cfws)),
literal('['),
star(and(opt(fws), dtext)),
opt(fws),
literal(']'),
invis(opt(cfws))
)());
}
// domain = dot-atom / domain-literal / obs-domain
function domain() {
return wrap('domain', function domainCheckTLD() {
var result = or(obsDomain, dotAtom, domainLiteral)();
if (opts.rejectTLD) {
if (result && result.semantic && result.semantic.indexOf('.') < 0) {
return null;
}
}
// strip all whitespace from domains
if (result) {
result.semantic = result.semantic.replace(/\s+/g, '');
}
return result;
}());
}
// addr-spec = local-part "@" domain
function addrSpec() {
return wrap('addr-spec', and(
localPart, literal('@'), domain
)());
}
// 3.6.2 Originator Fields
// Below we only parse the field body, not the name of the field
// like "From:", "Sender:", or "Reply-To:". Other libraries that
// parse email headers can parse those and defer to these productions
// for the "RFC 5322" part.
// RFC 6854 2.1. Replacement of RFC 5322, Section 3.6.2. Originator Fields
// from = "From:" (mailbox-list / address-list) CRLF
function fromSpec() {
return wrap('from', or(
mailboxList,
addressList
)());
}
// RFC 6854 2.1. Replacement of RFC 5322, Section 3.6.2. Originator Fields
// sender = "Sender:" (mailbox / address) CRLF
function senderSpec() {
return wrap('sender', or(
mailbox,
address
)());
}
// RFC 6854 2.1. Replacement of RFC 5322, Section 3.6.2. Originator Fields
// reply-to = "Reply-To:" address-list CRLF
function replyToSpec() {
return wrap('reply-to', addressList());
}
// 4.1. Miscellaneous Obsolete Tokens
// obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
// %d11 / ; characters that do not
// %d12 / ; include the carriage
// %d14-31 / ; return, line feed, and
// %d127 ; white space characters
function obsNoWsCtl() {
return opts.strict ? null : wrap('obs-NO-WS-CTL', compareToken(function (tok) {
var code = tok.charCodeAt(0);
return ((1 <= code && code <= 8) ||
(11 === code || 12 === code) ||
(14 <= code && code <= 31) ||
(127 === code));
}));
}
// obs-ctext = obs-NO-WS-CTL
function obsCtext() { return opts.strict ? null : wrap('obs-ctext', obsNoWsCtl()); }
// obs-qtext = obs-NO-WS-CTL
function obsQtext() { return opts.strict ? null : wrap('obs-qtext', obsNoWsCtl()); }
// obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR)
function obsQP() {
return opts.strict ? null : wrap('obs-qp', and(
literal('\\'),
or(literal('\0'), obsNoWsCtl, lf, cr)
)());
}
// obs-phrase = word *(word / "." / CFWS)
function obsPhrase() {
return opts.strict ? null : wrap('obs-phrase', and(
word,
star(or(word, literal('.'), colwsp(cfws)))
)());
}
// 4.2. Obsolete Folding White Space
// NOTE: read the errata http://www.rfc-editor.org/errata_search.php?rfc=5322&eid=1908
// obs-FWS = 1*([CRLF] WSP)
function obsFws() {
return opts.strict ? null : wrap('obs-FWS', star(
and(invis(opt(crlf)), wsp),
1
)());
}
// 4.4. Obsolete Addressing
// obs-angle-addr = [CFWS] "<" obs-route addr-spec ">" [CFWS]
function obsAngleAddr() {
return opts.strict ? null : wrap('obs-angle-addr', and(
invis(opt(cfws)),
literal('<'),
obsRoute,
addrSpec,
literal('>'),
invis(opt(cfws))
)());
}
// obs-route = obs-domain-list ":"
function obsRoute() {
return opts.strict ? null : wrap('obs-route', and(
obsDomainList,
literal(':')
)());
}
// obs-domain-list = *(CFWS / ",") "@" domain
// *("," [CFWS] ["@" domain])
function obsDomainList() {
return opts.strict ? null : wrap('obs-domain-list', and(
star(or(invis(cfws), literal(','))),
literal('@'),
domain,
star(and(
literal(','),
invis(opt(cfws)),
opt(and(literal('@'), domain))
))
)());
}
// obs-mbox-list = *([CFWS] ",") mailbox *("," [mailbox / CFWS])
function obsMboxList() {
return opts.strict ? null : wrap('obs-mbox-list', and(
star(and(
invis(opt(cfws)),
literal(',')
)),
mailbox,
star(and(
literal(','),
opt(and(
mailbox,
invis(cfws)
))
))
)());
}
// obs-addr-list = *([CFWS] ",") address *("," [address / CFWS])
function obsAddrList() {
return opts.strict ? null : wrap('obs-addr-list', and(
star(and(
invis(opt(cfws)),
literal(',')
)),
address,
star(and(
literal(','),
opt(and(
address,
invis(cfws)
))
))
)());
}
// obs-group-list = 1*([CFWS] ",") [CFWS]
function obsGroupList() {
return opts.strict ? null : wrap('obs-group-list', and(
star(and(
invis(opt(cfws)),
literal(',')
), 1),
invis(opt(cfws))
)());
}
// obs-local-part = word *("." word)
function obsLocalPart() {
return opts.strict ? null : wrap('obs-local-part', and(word, star(and(literal('.'), word)))());
}
// obs-domain = atom *("." atom)
function obsDomain() {
return opts.strict ? null : wrap('obs-domain', and(atom, star(and(literal('.'), atom)))());
}
// obs-dtext = obs-NO-WS-CTL / quoted-pair
function obsDtext() {
return opts.strict ? null : wrap('obs-dtext', or(obsNoWsCtl, quotedPair)());
}
/////////////////////////////////////////////////////
// ast analysis
function findNode(name, root) {
var i, stack, node;
if (root === null || root === undefined) { return null; }
stack = [root];
while (stack.length > 0) {
node = stack.pop();
if (node.name === name) {
return node;
}
for (i = node.children.length - 1; i >= 0; i -= 1) {
stack.push(node.children[i]);
}
}
return null;
}
function findAllNodes(name, root) {
var i, stack, node, result;
if (root === null || root === undefined) { return null; }
stack = [root];
result = [];
while (stack.length > 0) {
node = stack.pop();
if (node.name === name) {
result.push(node);
}
for (i = node.children.length - 1; i >= 0; i -= 1) {
stack.push(node.children[i]);
}
}
return result;
}
function findAllNodesNoChildren(names, root) {
var i, stack, node, result, namesLookup;
if (root === null || root === undefined) { return null; }
stack = [root];
result = [];
namesLookup = {};
for (i = 0; i < names.length; i += 1) {
namesLookup[names[i]] = true;
}
while (stack.length > 0) {
node = stack.pop();
if (node.name in namesLookup) {
result.push(node);
// don't look at children (hence findAllNodesNoChildren)
} else {
for (i = node.children.length - 1; i >= 0; i -= 1) {
stack.push(node.children[i]);
}
}
}
return result;
}
function giveResult(ast) {
var addresses, groupsAndMailboxes, i, groupOrMailbox, result;
if (ast === null) {
return null;
}
addresses = [];
// An address is a 'group' (i.e. a list of mailboxes) or a 'mailbox'.
groupsAndMailboxes = findAllNodesNoChildren(['group', 'mailbox'], ast);
for (i = 0; i < groupsAndMailboxes.length; i += 1) {
groupOrMailbox = groupsAndMailboxes[i];
if (groupOrMailbox.name === 'group') {
addresses.push(giveResultGroup(groupOrMailbox));
} else if (groupOrMailbox.name === 'mailbox') {
addresses.push(giveResultMailbox(groupOrMailbox));
}
}
result = {
ast: ast,
addresses: addresses,
};
if (opts.simple) {
result = simplifyResult(result);
}
if (opts.oneResult) {
return oneResult(result);
}
if (opts.simple) {
return result && result.addresses;
} else {
return result;
}
}
function giveResultGroup(group) {
var i;
var groupName = findNode('display-name', group);
var groupResultMailboxes = [];
var mailboxes = findAllNodesNoChildren(['mailbox'], group);
for (i = 0; i < mailboxes.length; i += 1) {
groupResultMailboxes.push(giveResultMailbox(mailboxes[i]));
}
return {
node: group,
parts: {
name: groupName,
},
type: group.name, // 'group'
name: grabSemantic(groupName),
addresses: groupResultMailboxes,
};
}
function giveResultMailbox(mailbox) {
var name = findNode('display-name', mailbox);
var aspec = findNode('addr-spec', mailbox);
var comments = findAllNodes('cfws', mailbox);
var local = findNode('local-part', aspec);
var domain = findNode('domain', aspec);
return {
node: mailbox,
parts: {
name: name,
address: aspec,
local: local,
domain: domain,
comments: comments
},
type: mailbox.name, // 'mailbox'
name: grabSemantic(name),
address: grabSemantic(aspec),
local: grabSemantic(local),
domain: grabSemantic(domain),
groupName: grabSemantic(mailbox.groupName),
};
}
function grabSemantic(n) {
return n !== null && n !== undefined ? n.semantic : null;
}
function simplifyResult(result) {
var i;
if (result && result.addresses) {
for (i = 0; i < result.addresses.length; i += 1) {
delete result.addresses[i].node;
}
}
return result;
}
function oneResult(result) {
if (!result) { return null; }
if (!opts.partial && result.addresses.length > 1) { return null; }
return result.addresses && result.addresses[0];
}
/////////////////////////////////////////////////////
var parseString, pos, len, parsed, startProduction;
opts = handleOpts(opts, {});
if (opts === null) { return null; }
parseString = opts.input;
startProduction = {
'address': address,
'address-list': addressList,
'angle-addr': angleAddr,
'from': fromSpec,
'group': group,
'mailbox': mailbox,
'mailbox-list': mailboxList,
'reply-to': replyToSpec,
'sender': senderSpec,
}[opts.startAt] || addressList;
if (!opts.strict) {
initialize();
opts.strict = true;
parsed = startProduction(parseString);
if (opts.partial || !inStr()) {
return giveResult(parsed);
}
opts.strict = false;
}
initialize();
parsed = startProduction(parseString);
if (!opts.partial && inStr()) { return null; }
return giveResult(parsed);
}
/*
function checkemailaddress(emailstr) {
if (emailstr == '') { return false; }
// Get parts of email address
emailobj = emailAddresses.parseOneAddress(emailstr);
//emailobj = MyCwCal.emailAddresses.parseOneAddress(emailstr)
//emailobj = this.emailAddresses.parseOneAddress(emailstr);
if (emailobj && emailobj !== 'null' && emailobj !== 'undefined') {
nodisplaynameemailstr = emailobj.address;
} else {
nodisplaynameemailstr = 'invalid';
}
// Check for correct rsvp email address format
var filter = /^([a-zA-Z0-9_+'\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
if (!filter.test(nodisplaynameemailstr)) {
return false;
} else {
return true;
}
}
*/
function parseOneAddressSimple(opts) {
return parse5322(handleOpts(opts, {
oneResult: true,
rfc6532: true,
simple: true,
startAt: 'address-list',
}));
}
function parseAddressListSimple(opts) {
return parse5322(handleOpts(opts, {
rfc6532: true,
simple: true,
startAt: 'address-list',
}));
}
function parseFromSimple(opts) {
return parse5322(handleOpts(opts, {
rfc6532: true,
simple: true,
startAt: 'from',
}));
}
function parseSenderSimple(opts) {
return parse5322(handleOpts(opts, {
oneResult: true,
rfc6532: true,
simple: true,
startAt: 'sender',
}));
}
function parseReplyToSimple(opts) {
return parse5322(handleOpts(opts, {
rfc6532: true,
simple: true,
startAt: 'reply-to',
}));
}
function handleOpts(opts, defs) {
function isString(str) {
return Object.prototype.toString.call(str) === '[object String]';
}
function isObject(o) {
return o === Object(o);
}
function isNullUndef(o) {
return o === null || o === undefined;
}
var defaults, o;
if (isString(opts)) {
opts = { input: opts };
} else if (!isObject(opts)) {
return null;
}
if (!isString(opts.input)) { return null; }
if (!defs) { return null; }
defaults = {
oneResult: false,
partial: false,
rejectTLD: false,
rfc6532: false,
simple: false,
startAt: 'address-list',
strict: false,
};
for (o in defaults) {
if (isNullUndef(opts[o])) {
opts[o] = !isNullUndef(defs[o]) ? defs[o] : defaults[o];
}
}
return opts;
}
parse5322.parseOneAddress = parseOneAddressSimple;
parse5322.parseAddressList = parseAddressListSimple;
parse5322.parseFrom = parseFromSimple;
parse5322.parseSender = parseSenderSimple;
parse5322.parseReplyTo = parseReplyToSimple;
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = parse5322;
} else {
global.emailAddresses = parse5322;
}
}(this));
$(init);