",
initialize: function () {
this.setup();
},
/**
* Setup method
*
* @returns {void}
*/
setup: function () {
this._alignEvents();
},
_alignEvents: function () {
// Lets start by getting all active days in the current calendar
var days = this.scope.find('.cCalendar_date');
var currentPositions = [];
var self = this;
_.each( days, function (day) {
var day = $( day );
var dayNumber = day.find('.cCalendar_dayNumber').text();
var weekStart = false;
if( day.closest('td').is('tr > td:first-child') ){
weekStart = true;
}
// Get events for this day
var events = day.find('.cEvents_ranged [data-eventID]');
// If there's no events, we can skip this day
if( !events.length ){
currentPositions = [];
return;
}
// Build a wrapper into which we'll move our events
var wrapper = $("").addClass('cEvents');
var spaces = 0;
// Now we loop over currentPositions (from the previous day), and try and arrange today's events
// in the same order
if( currentPositions.length ){
var doneEvent = false;
for( var i = 0; i < currentPositions.length; i++ ){
if( events.filter('[data-eventID="' + currentPositions[i] + '"]').length ){
wrapper.append( events.filter('[data-eventID="' + currentPositions[i] + '"]') );
doneEvent = true;
} else {
// If this is the first day of the week, we won't bother adding spacers unless we've already done an event today
// This prevents lots of unnecessary spacers carrying over from the previous row
if( !weekStart || doneEvent ){
wrapper.append( self._emptyEvent );
spaces++;
}
}
}
}
var remainingEvents = day.find('.cEvents_ranged [data-eventID]');
// If we have any remaining events, and there's gaps available, we can move those events into the gaps rather than
// just putting them at the end
if( spaces && remainingEvents.length ){
var availableSpaces = wrapper.find('[data-eventID="0"]');
// If we have a space and an event, move the event into that space
for( var i = 0; i <= spaces; i++ ){
if( remainingEvents[ i ] && availableSpaces[ i ] ){
$( availableSpaces[ i ] ).replaceWith( $( remainingEvents[ i ] ) );
}
}
// Update remaining events again
remainingEvents = day.find('.cEvents_ranged [data-eventID]');
}
// Add in remaining events
wrapper.append( remainingEvents );
// Replace the existing wrapper with the new, correctly-ordered one
day.find('.cEvents_ranged > .cEvents').replaceWith( wrapper );
// Now we need to build a new currentPositions array for this day, so that the following
// day can use it to do its thing
currentPositions = [];
_.each( day.find('.cEvents_ranged [data-eventID]'), function (event) {
var eventID = parseInt( $( event ).attr('data-eventID') );
if( eventID === 0 ){
currentPositions.push('-');
} else if( _.isNumber( eventID ) && !_.isNaN( eventID ) ){
currentPositions.push( eventID );
}
});
});
// Now get all rows and cells to recalculate height
_.each( this.scope.find('tr'), function (row) {
// Don't bother if there's no birthdays in this row
if( !$( row ).find('.cCalendar_birthdays').length ){
return;
}
var cells = $( row ).find('td.cCalendar_date');
var maxHeightCell = _.max( cells, function (cell) {
return parseInt( $( cell ).height() );
});
if( !_.isElement( maxHeightCell ) ){
return;
}
// Set the div height
cells.find('> div').css({
height: $( maxHeightCell ).height() + 20 + 'px'
});
});
}
});
}(jQuery, _));]]>
1 ) {
$('#elSelect_event_repeats').find('option[value="daily"]').prop( 'disabled', true );
$('#elRepeatOn').hide();
$('#elRepeatOn').attr( 'id', 'elRepeatOn_back' );
if( !currentlySelected || currentlySelected == 'daily' )
{
$('#elSelect_event_repeats').find('option[value="daily"]').prop( 'selected', false );
$('#elSelect_event_repeats').find('option[value="weekly"]').prop( 'selected', true );
currentlySelected = 'weekly';
}
} else {
$('#elSelect_event_repeats').find('option[value="daily"]').prop( 'disabled', false );
if( $('#elRepeatOn_back').length )
{
$('#elRepeatOn_back').attr( 'id', 'elRepeatOn' );
}
if( $('#elSelect_event_repeats').val() == 'weekly' )
{
$('#elRepeatOn').show();
}
}
// If the event spans more than one week, remove "daily" and "weekly" as recurrence options
if( dayDifference > 7 ) {
$('#elSelect_event_repeats').find('option[value="weekly"]').prop( 'disabled', true );
if( !currentlySelected || currentlySelected == 'weekly' )
{
$('#elSelect_event_repeats').find('option[value="weekly"]').prop( 'selected', false );
$('#elSelect_event_repeats').find('option[value="monthly"]').prop( 'selected', true );
currentlySelected = 'monthly';
}
} else {
$('#elSelect_event_repeats').find('option[value="weekly"]').prop( 'disabled', false );
}
// If the event spans more than one month, remove "daily", "weekly" and "monthly" recurrence options
if(
// If there's more than one month between the dates, then we block
( endDate.getMonth() - startDate.getMonth() + ( 12 * ( endDate.getFullYear() - startDate.getFullYear() ) ) > 1 )
||
// Else if there is one month between the dates, then we check the end date is greater than the start date
(
endDate.getMonth() - startDate.getMonth() + ( 12 * ( endDate.getFullYear() - startDate.getFullYear() ) ) == 1 && endDate.getDate() > startDate.getDate()
)
) {
$('#elSelect_event_repeats').find('option[value="monthly"]').prop( 'disabled', true );
if( !currentlySelected || currentlySelected == 'monthly' )
{
$('#elSelect_event_repeats').find('option[value="monthly"]').prop( 'selected', false );
$('#elSelect_event_repeats').find('option[value="yearly"]').prop( 'selected', true );
currentlySelected = 'yearly';
}
} else {
$('#elSelect_event_repeats').find('option[value="monthly"]').prop( 'disabled', false );
}
// If the event spans more than one year, remove ability for event to repeat entirely
if( dayDifference > 365 )
{
$('#elSelect_event_repeats').find('option[value="yearly"]').prop( 'selected', false );
this.disableRecurring();
$('#elRepeatRow_hidden').hide();
}
else
{
$('#elRepeatRow_hidden').show();
}
// And update the summary in case anything changed
this._updateSummary();
},
/**
* Finish setting the options
*
* @returns {void}
*/
finishRepeat: function () {
this._updateSummary();
$('#elRecurEdit_menu').trigger('closeMenu');
},
/**
* Setup method
*
* @returns {void}
*/
setup: function () {
this._updateSummary();
this.toggleFields();
this.updateTimezone();
// Make Event End cell as big as Event Start cell
this._resizeEndGrid();
},
/**
* Toggle the end time enabled/disabled status
*
* @param {event} e Event object
* @returns {void}
*/
toggleEndtimeFields: function(e) {
if( this.scope.find('#check_no_end_time').is(':checked') )
{
this.scope.find('#end_time').prop('disabled', true);
}
else
{
this.scope.find('#end_time').prop('disabled', false);
}
},
/**
* Manual toggle functionality for the Single/All Day checkboxes
*
* @param {event} e Event object
* @returns {void}
*/
toggleFields: function (e) {
this.toggleEndtimeFields();
var singleDay = this.scope.find('#check_single_day');
var allDay = this.scope.find('#check_all_day');
var self = this;
var toggles = {
start_time_wrap: true,
end_time_wrap: true,
event_end_date_wrap: true,
elDateGrid_arrow: true,
elDateGrid_end: true,
end_date_controls: true
};
// Single day, not all day
if( singleDay.is(':checked') && !allDay.is(':checked') ){
toggles.event_end_date_wrap = false;
// Single day, all day
} else if( singleDay.is(':checked') && allDay.is(':checked') ){
toggles.elDateGrid_arrow = false;
toggles.elDateGrid_end = false;
toggles.start_time_wrap = false;
// Multiple days, all day
} else if( !singleDay.is(':checked') && allDay.is(':checked') ){
toggles.start_time_wrap = false;
toggles.end_time_wrap = false;
toggles.end_date_controls = false;
}
// Multiple days, not all day
else
{
toggles.end_date_controls = false;
this.scope.find('#end_time').prop('disabled', false);
}
// Hide appropriate elements
_.each( toggles, function (val, key) {
self.scope.find( '#' + key ).toggle( val );
});
// Manual check for ful;-width start date
this.scope.find('#elDateGrid_start').toggleClass('ipsGrid_span5', this.scope.find('#elDateGrid_end').is(':visible') );
},
/**
* Monitor changes to the repeat options and update the summary
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
checkForSummaryChange: function (e) {
if( $( e.currentTarget ).attr('name').startsWith('event_dates[') ){
this._updateSummary();
}
},
/**
* Updates the timezone value both in the hidden field and in the display to the end user
*
* @param {event} e Event object
* @returns {void}
*/
updateTimezone: function (e) {
if( e ){
e.preventDefault();
}
// Update displayed timezone
this.scope.find('[data-role="timezone_display"]').text( $('#event_timezone option:selected').data('abbreviated') ).trigger('closeMenu');
},
/**
* Removes the Repeat summary and unchecks the Repeat checkbox
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
disableRecurring: function (e) {
if( e )
{
e.preventDefault();
}
this.scope.find('#elRepeatCb').prop( 'checked', false );
this._updateSummary();
},
/**
* Finalize our recurring options
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
finishRepeatConfiguration: function (e, data) {
e.preventDefault();
// Copy the summary from the menu to the main display and close the menu
this.scope.find('[data-role="recur_summary_final"]').text( this.scope.find('[data-role="recur_summary"]').text() );
this.scope.find('#elRecurEdit').trigger('closeMenu');
},
/**
* Update the recurring string
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
_updateSummary: function () {
var summary = this.scope.find('[data-role="recurringSummary"]');
// Update repeating text
if( this.scope.find('#elRepeatCb').is(':checked') ){
summary.text( this._getSummary() );
this.scope.find('#elRepeatRow_hidden').hide();
this.scope.find('#elRepeatRow_shown').show();
} else {
summary.html( "" + ips.getString('doesnt_repeat') + "" );
this.scope.find('#elRepeatRow_hidden').show();
this.scope.find('#elRepeatRow_shown').hide();
}
// Update the dates
this.scope.find('[data-role="dateSummary"]').html( this._dateSummary() );
},
/**
* Builds a summary of the selected dates/times
*
* @returns {string}
*/
_dateSummary: function () {
// Build start date
var startDate = ips.utils.time.getDateFromInput( this.scope.find('input[name="event_dates[start_date]"]') );
var singleDay = this.scope.find('#check_single_day');
var allDay = this.scope.find('#check_all_day');
// If there's no start time, then abandon showing any summary for now
if( !ips.utils.time.isValidDateObj( startDate ) || startDate.getFullYear() < 1900 ) {
return '';
}
ips.utils.time.removeTimezone( startDate );
var startDateString = ips.utils.time.localeDateString( startDate, { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC' } );
var startTime = this._getTime( this.scope.find('input[name="event_dates[start_time]"]').val() );
var endTime = this._getTime( this.scope.find('input[name="event_dates[end_time]"]').val() );
var endDate = ips.utils.time.getDateFromInput( this.scope.find('input[name="event_dates[end_date]"]') );
var endDateString = '';
// If we have a valid end date...
if( !singleDay.is(':checked') && ips.utils.time.isValidDateObj( endDate ) ){
ips.utils.time.removeTimezone( endDate );
endDateString = ips.utils.time.localeDateString( endDate, { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC' } );
}
// Now build strings
var finalString = '';
if( singleDay.is(':checked') && !allDay.is(':checked') ){
if( this.scope.find('#check_no_end_time').is(':checked') )
{
finalString = ips.getString( 'single_not_allday_noendtime', { startDate: startDateString, startTime: startTime } );
}
else
{
finalString = ips.getString( 'single_not_allday', { startDate: startDateString, startTime: startTime, endTime: endTime } );
}
} else if ( ( !singleDay.is(':checked') && !allDay.is(':checked') ) && endDateString && startTime && endTime ) {
finalString = ips.getString( 'not_single_not_allday', { startDate: startDateString, endDate: endDateString, startTime: startTime, endTime: endTime } );
} else if( !singleDay.is(':checked') && allDay.is(':checked') && endDateString ) {
finalString = ips.getString( 'not_single_allday', { startDate: startDateString, endDate: endDateString } );
} else {
finalString = ips.getString( 'single_allday', { startDate: startDateString } );
}
return finalString;
},
/**
* Returns the given time, or a placeholder string if empty
*
* @param {string} time Time string
* @returns {string}
*/
_getTime: function (time) {
if( !time ){
return "" + ips.getString('select_time') + "";
}
var date = new Date('1970-01-01T' + time + 'Z');
var time = date.toLocaleTimeString( $('html').attr('lang'), { hour: '2-digit', minute: '2-digit', timeZone: 'UTC' } );
return time;
},
/**
* Returns the summary string for recurring events
*
* @returns {string}
*/
_getSummary: function () {
var type = this.scope.find('#elSelect_event_repeats').val();
var intervalString = '';
var endString = '';
// Build the 'interval' string
switch( type ){
case 'daily':
case 'monthly':
case 'yearly':
intervalString = this._buildString( type );
break;
case 'weekly':
intervalString = this._buildWeekly();
break;
}
// Build the 'end after' string
if( this.scope.find('#event_repeat_end_afterx').is(':checked') ){
var occurrences = parseInt( this.scope.find('input[name="event_dates[repeat_end_occurrences]"]').val() );
if( _.isNumber( occurrences ) && !_.isNaN( occurrences ) ){
endString = ips.pluralize( ips.getString( 'x_times' ), occurrences );
}
} else if( this.scope.find('#event_repeat_end_ondate').is(':checked') ){
var dateObj = ips.utils.time.getDateFromInput( this.scope.find('input[name="event_dates[repeat_end_date]"]') );
if( ips.utils.time.isValidDateObj( dateObj ) && dateObj.getFullYear() > 1900 ){ // > 1900 just so it doesn't start updating on year 19 etc.
ips.utils.time.removeTimezone( dateObj );
endString = ips.getString('until', { date: ips.utils.time.localeDateString( dateObj, { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' } ) } );
}
}
// Put it together
if( endString ){
return ips.getString( 'with_end', { interval: intervalString, endAfter: endString } );
} else {
return intervalString;
}
},
/**
* Builds a summary string for daily, monthly and yearly repeats
*
* @param {string} type The type of repeat
* @returns {string}
*/
_buildString: function ( type ) {
var val = parseInt( this.scope.find('#elSelect_event_repeat_freq').val() ) || 1;
return ips.pluralize( ips.getString( 'every_x', { period: ips.pluralize( ips.getString( 'x_' + type ), val ) } ), val );
},
/**
* Builds a summary string for weekly repeats
*
* @returns {string}
*/
_buildWeekly: function () {
var selectedDays = this.scope.find('[data-iCal]:checked');
var val = parseInt( this.scope.find('#elSelect_event_repeat_freq').val() ) || 1;
var weekString = '';
weekString = ips.pluralize( ips.getString( 'x_weekly' ), val );
// If no days are selected, we can bypass and finish now
if( !selectedDays.length ){
return weekString;
}
// Get full days
var fullDays = _.map( selectedDays, function (day, key) {
return ips.getString( $( day ).attr('data-iCal') );
});
// Build string
var dayString = '';
if( fullDays.length === 1 ){
dayString = ips.getString( 'one_day', { first: fullDays[0] } );
} else {
dayString = ips.getString( 'multiple_day', { days: fullDays.slice(0, -1).join(', '), last: fullDays[ fullDays.length - 1 ] });
}
return ips.getString( 'week_string', { week: weekString, days: dayString } );
},
/**
* Resizes the "Event End" grid box to be the same height as the start box
*
* @returns {void}
*/
_resizeEndGrid: function () {
var height = 'auto';
if( !ips.utils.responsive.enabled() || !ips.utils.responsive.currentIs('phone') ){
height = this.scope.find('#elDateGrid_start').outerHeight() + 'px';
}
this.scope.find('#elDateGrid_end').css({
height: height
});
}
});
}(jQuery, _));]]>
/**
* Invision Community
* (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
*
* ips.calendar.reminderButton.js - Controller for reminder button
*
* Author: Andrew Millne
*/
;( function($, _, undefined){
"use strict";
ips.controller.register('calendar.front.view.reminderForm', {
initialize: function () {
this.on( 'submit', this.submitForm );
this.on( 'click', '[data-action="removereminder"]', this.removereminder );
this.setup();
},
/**
* Setup method
*
* @returns {void}
*/
setup: function () {
},
/**
* Event handler for removing reminder
*
* @param {event} e Event object
* @returns {void}
*/
removereminder: function (e) {
e.preventDefault();
this._doRemindAction( $( e.currentTarget ).attr('href'), {}, true );
},
/**
* Event handler for submitting the reminder form
*
* @param {event} e Event object
* @returns {void}
*/
submitForm: function (e) {
e.preventDefault();
this._doRemindAction( this.scope.attr('action'), this.scope.serialize(), false );
},
/**
* Performs an ajax action.
*
* @param {string} url URL to call
* @param {object} data Object of data to include in the request
* @returns {void}
*/
_doRemindAction: function (url, data, removereminder ) {
var self = this;
var dims = ips.utils.position.getElemDims( this.scope.parent('div') );
// Set it to loading
this.scope
.hide()
.parent('div')
.css({
width: dims.outerWidth + 'px',
height: dims.outerHeight + 'px'
})
.addClass('ipsLoading');
// Update reminder preference via ajax
ips.getAjax()( url, {
data: data,
type: 'post'
})
.done( function (response, status, jqXHR) {
if( jqXHR.getAllResponseHeaders().indexOf('X-IPS-FormError: true') !== -1 || jqXHR.getAllResponseHeaders().indexOf('X-IPS-FormNoSubmit: true') !== -1 || jqXHR.getAllResponseHeaders().indexOf('x-ips-formerror: true') !== -1 || jqXHR.getAllResponseHeaders().indexOf('x-ips-formnosubmit: true') !== -1 ){
self.scope
.show()
.html( response )
.parent('div')
.removeClass('ipsLoading')
.css({
width: 'auto',
height: 'auto'
});
} else {
// Success, so trigger event to update button
self.trigger('reminderItem');
if( removereminder ) {
ips.ui.flashMsg.show(ips.getString('event_reminder_removed'));
}
else {
ips.ui.flashMsg.show(ips.getString('event_reminder_added'));
}
self.scope.parents('.ipsHovercard').remove();
}
})
.fail( function (jqXHR, textStatus, errorThrown) {
window.location = url;
})
.always( function () {
// If we're in a hovercard, remove it
});
}
});
}(jQuery, _));
\
");]]>
templates
common/ips.loader.js
common/ui
common/utils
common
controllers
underscore
jquery
mustache
jstz
IntersectionObserver
Debug.js
app.js
jquery.js
jquery-migrate.js
jquery.history.js
jquery.transform.js
linkify.min.js
linkify-jquery.min.js