mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-03-20 05:45:57 +00:00
Also removed explicit settings and defaults that were imported in various app modules. We now always use the Preferences ng service.
759 lines
25 KiB
JavaScript
759 lines
25 KiB
JavaScript
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
/**
|
|
* @name Component
|
|
* @constructor
|
|
* @param {object} futureComponentData - either an object literal or a promise
|
|
*/
|
|
function Component(futureComponentData) {
|
|
// Data is immediately available
|
|
if (typeof futureComponentData.then !== 'function') {
|
|
this.init(futureComponentData);
|
|
if (this.pid && !this.id) {
|
|
// Prepare for the creation of a new component;
|
|
// Get UID from the server.
|
|
var newComponentData = Component.$$resource.newguid(this.pid);
|
|
this.$unwrap(newComponentData);
|
|
this.isNew = true;
|
|
}
|
|
}
|
|
else {
|
|
// The promise will be unwrapped first
|
|
this.$unwrap(futureComponentData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @memberof Component
|
|
* @desc The factory we'll use to register with Angular
|
|
* @returns the Component constructor
|
|
*/
|
|
Component.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Preferences', 'Gravatar', 'Resource', function($q, $timeout, $log, Settings, Preferences, Gravatar, Resource) {
|
|
angular.extend(Component, {
|
|
$q: $q,
|
|
$timeout: $timeout,
|
|
$log: $log,
|
|
$Preferences: Preferences,
|
|
$gravatar: Gravatar,
|
|
$$resource: new Resource(Settings.baseURL(), Settings.activeUser()),
|
|
timeFormat: "%H:%M",
|
|
// Filter parameters common to events and tasks
|
|
$query: { value: '', search: 'title_Category_Location' },
|
|
// Filter paramaters specific to events
|
|
$queryEvents: { sort: 'start', asc: 1, filterpopup: 'view_next7' },
|
|
// Filter parameters specific to tasks
|
|
$queryTasks: { sort: 'status', asc: 1, filterpopup: 'view_next7' } //'view_incomplete' }
|
|
});
|
|
Preferences.ready().then(function() {
|
|
// Initialize filter parameters from user's settings
|
|
Component.$queryEvents.filterpopup = Preferences.settings.CalendarDefaultFilter;
|
|
Component.$queryTasks.show_completed = parseInt(Preferences.settings.ShowCompletedTasks);
|
|
// Initialize categories from user's defaults
|
|
Component.$categories = Preferences.defaults.SOGoCalendarCategoriesColors;
|
|
// Initialize time format from user's defaults
|
|
if (Preferences.defaults.SOGoTimeFormat) {
|
|
Component.timeFormat = Preferences.defaults.SOGoTimeFormat;
|
|
}
|
|
});
|
|
|
|
return Component; // return constructor
|
|
}];
|
|
|
|
/**
|
|
* @module SOGo.SchedulerUI
|
|
* @desc Factory registration of Component in Angular module.
|
|
*/
|
|
try {
|
|
angular.module('SOGo.SchedulerUI');
|
|
}
|
|
catch(e) {
|
|
angular.module('SOGo.SchedulerUI', ['SOGo.Common']);
|
|
}
|
|
angular.module('SOGo.SchedulerUI')
|
|
.factory('Component', Component.$factory);
|
|
|
|
/**
|
|
* @function $filter
|
|
* @memberof Component.prototype
|
|
* @desc Search for components matching some criterias
|
|
* @param {string} type - either 'events' or 'tasks'
|
|
* @param {object} [options] - additional options to the query
|
|
* @returns a collection of Components instances
|
|
*/
|
|
Component.$filter = function(type, options) {
|
|
var _this = this,
|
|
now = new Date(),
|
|
day = now.getDate(),
|
|
month = now.getMonth() + 1,
|
|
year = now.getFullYear(),
|
|
queryKey = '$query' + type.capitalize(),
|
|
params = {
|
|
day: '' + year + (month < 10?'0':'') + month + (day < 10?'0':'') + day,
|
|
};
|
|
|
|
return this.$Preferences.ready().then(function() {
|
|
var futureComponentData,
|
|
dirty = false,
|
|
otherType;
|
|
|
|
angular.extend(_this.$query, params);
|
|
|
|
if (options) {
|
|
_.each(_.keys(options), function(key) {
|
|
// Query parameters common to events and tasks are compared
|
|
dirty |= (_this.$query[key] && options[key] != Component.$query[key]);
|
|
if (key == 'reload' && options[key])
|
|
dirty = true;
|
|
// Update either the common parameters or the type-specific parameters
|
|
else if (angular.isDefined(_this.$query[key]))
|
|
_this.$query[key] = options[key];
|
|
else
|
|
_this[queryKey][key] = options[key];
|
|
});
|
|
}
|
|
|
|
// Perform query with both common and type-specific parameters
|
|
futureComponentData = _this.$$resource.fetch(null, type + 'list',
|
|
angular.extend(_this[queryKey], _this.$query));
|
|
|
|
// Invalidate cached results of other type if $query has changed
|
|
otherType = (type == 'tasks')? '$events' : '$tasks';
|
|
if (dirty) {
|
|
delete Component[otherType];
|
|
Component.$log.debug('force reload of ' + otherType);
|
|
}
|
|
|
|
return _this.$unwrapCollection(type, futureComponentData);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @function $find
|
|
* @desc Fetch a component from a specific calendar.
|
|
* @param {string} calendarId - the calendar ID
|
|
* @param {string} componentId - the component ID
|
|
* @param {string} [occurrenceId] - the component ID
|
|
* @see {@link Calendar.$getComponent}
|
|
*/
|
|
Component.$find = function(calendarId, componentId, occurrenceId) {
|
|
var futureComponentData, path = [calendarId, componentId];
|
|
|
|
if (occurrenceId)
|
|
path.push(occurrenceId);
|
|
|
|
futureComponentData = this.$$resource.fetch(path.join('/'), 'view');
|
|
|
|
return new Component(futureComponentData);
|
|
};
|
|
|
|
/**
|
|
* @function filterCategories
|
|
* @desc Search for categories matching some criterias
|
|
* @param {string} search - the search string to match
|
|
* @returns a collection of strings
|
|
*/
|
|
Component.filterCategories = function(query) {
|
|
var re = new RegExp(query, 'i');
|
|
return _.filter(_.keys(Component.$categories), function(category) {
|
|
return category.search(re) != -1;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @function $eventsBlocksForView
|
|
* @desc Events blocks for a specific week
|
|
* @param {string} view - Either 'day' or 'week'
|
|
* @param {Date} type - Date of any day of the desired period
|
|
* @returns a promise of a collection of objects describing the events blocks
|
|
*/
|
|
Component.$eventsBlocksForView = function(view, date) {
|
|
var viewAction, startDate, endDate, params;
|
|
|
|
if (view == 'day') {
|
|
viewAction = 'dayView';
|
|
startDate = endDate = date;
|
|
}
|
|
else if (view == 'week') {
|
|
viewAction = 'weekView';
|
|
startDate = date.beginOfWeek();
|
|
endDate = new Date();
|
|
endDate.setTime(startDate.getTime());
|
|
endDate.addDays(6);
|
|
}
|
|
else if (view == 'month') {
|
|
viewAction = 'monthView';
|
|
startDate = date;
|
|
startDate.setDate(1);
|
|
startDate = startDate.beginOfWeek();
|
|
endDate = new Date();
|
|
endDate.setTime(startDate.getTime());
|
|
endDate.setMonth(endDate.getMonth() + 1);
|
|
endDate.addDays(-1);
|
|
endDate = endDate.endOfWeek();
|
|
}
|
|
return this.$eventsBlocks(viewAction, startDate, endDate);
|
|
};
|
|
|
|
/**
|
|
* @function $eventsBlocks
|
|
* @desc Events blocks for a specific view and period
|
|
* @param {string} view - Either 'day' or 'week'
|
|
* @param {Date} startDate - period's start date
|
|
* @param {Date} endDate - period's end date
|
|
* @returns a promise of a collection of objects describing the events blocks
|
|
*/
|
|
Component.$eventsBlocks = function(view, startDate, endDate) {
|
|
var params, futureComponentData, i,
|
|
deferred = Component.$q.defer();
|
|
|
|
params = { view: view.toLowerCase(), sd: startDate.getDayString(), ed: endDate.getDayString() };
|
|
Component.$log.debug('eventsblocks ' + JSON.stringify(params, undefined, 2));
|
|
futureComponentData = this.$$resource.fetch(null, 'eventsblocks', params);
|
|
futureComponentData.then(function(data) {
|
|
Component.$timeout(function() {
|
|
var components = [], blocks = {};
|
|
|
|
// Instantiate Component objects
|
|
_.reduce(data.events, function(objects, eventData, i) {
|
|
var componentData = _.object(data.eventsFields, eventData),
|
|
start = new Date(componentData.c_startdate * 1000);
|
|
componentData.hour = start.getHourString();
|
|
objects.push(new Component(componentData));
|
|
return objects;
|
|
}, components);
|
|
|
|
// Associate Component objects to blocks positions
|
|
_.each(_.flatten(data.blocks), function(block) {
|
|
block.component = components[block.nbr];
|
|
});
|
|
|
|
// Convert array of blocks to object with days as keys
|
|
for (i = 0; i < data.blocks.length; i++) {
|
|
blocks[startDate.getDayString()] = data.blocks[i];
|
|
startDate.addDays(1);
|
|
}
|
|
|
|
Component.$log.debug('blocks ready (' + _.keys(blocks).length + ')');
|
|
|
|
// Save the blocks to the object model
|
|
Component.$blocks = blocks;
|
|
|
|
deferred.resolve(blocks);
|
|
});
|
|
}, deferred.reject);
|
|
|
|
return deferred.promise;
|
|
};
|
|
|
|
/**
|
|
* @function $unwrapCollection
|
|
* @desc Unwrap a promise and instanciate new Component objects using received data.
|
|
* @param {string} type - either 'events' or 'tasks'
|
|
* @param {promise} futureComponentData - a promise of the components' metadata
|
|
* @returns a promise of the HTTP operation
|
|
*/
|
|
Component.$unwrapCollection = function(type, futureComponentData) {
|
|
var _this = this,
|
|
components = [];
|
|
|
|
return futureComponentData.then(function(data) {
|
|
return Component.$timeout(function() {
|
|
var fields = _.invoke(data.fields, 'toLowerCase');
|
|
|
|
// Instanciate Component objects
|
|
_.reduce(data[type], function(components, componentData, i) {
|
|
var data = _.object(fields, componentData);
|
|
components.push(new Component(data));
|
|
return components;
|
|
}, components);
|
|
|
|
Component.$log.debug('list of ' + type + ' ready (' + components.length + ')');
|
|
|
|
// Save the list of components to the object model
|
|
Component['$' + type] = components;
|
|
|
|
return components;
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @function init
|
|
* @memberof Component.prototype
|
|
* @desc Extend instance with required attributes and new data.
|
|
* @param {object} data - attributes of component
|
|
*/
|
|
Component.prototype.init = function(data) {
|
|
var _this = this;
|
|
|
|
this.categories = [];
|
|
this.repeat = {};
|
|
this.alarm = { action: 'display', quantity: 5, unit: 'MINUTES', reference: 'BEFORE', relation: 'START' };
|
|
this.status = 'not-specified';
|
|
angular.extend(this, data);
|
|
|
|
Component.$Preferences.ready().then(function() {
|
|
var type = (_this.type == 'appointment')? 'Events' : 'Tasks';
|
|
|
|
// Set default values from user's defaults
|
|
_this.classification = _this.classification ||
|
|
Component.$Preferences.defaults['SOGoCalendar' + type + 'DefaultClassification'].toLowerCase();
|
|
});
|
|
|
|
if (this.startDate)
|
|
this.start = new Date(this.startDate.substring(0,10) + ' ' + this.startDate.substring(11,16));
|
|
if (this.endDate)
|
|
this.end = new Date(this.endDate.substring(0,10) + ' ' + this.endDate.substring(11,16));
|
|
if (this.dueDate)
|
|
this.due = new Date(this.dueDate.substring(0,10) + ' ' + this.dueDate.substring(11,16));
|
|
|
|
// Parse recurrence rule definition and initialize default values
|
|
if (this.repeat.days) {
|
|
var byDayMask = _.find(this.repeat.days, function(o) {
|
|
return angular.isDefined(o.occurrence);
|
|
});
|
|
if (byDayMask)
|
|
if (this.repeat.frequency == 'yearly')
|
|
this.repeat.year = { byday: true };
|
|
this.repeat.month = {
|
|
type: 'byday',
|
|
occurrence: byDayMask.occurrence.toString(),
|
|
day: byDayMask.day
|
|
};
|
|
}
|
|
else {
|
|
this.repeat.days = [];
|
|
}
|
|
if (angular.isUndefined(this.repeat.frequency))
|
|
this.repeat.frequency = 'never';
|
|
if (angular.isUndefined(this.repeat.interval))
|
|
this.repeat.interval = 1;
|
|
if (angular.isUndefined(this.repeat.month))
|
|
this.repeat.month = { occurrence: '1', day: 'SU', type: 'bymonthday' };
|
|
if (angular.isUndefined(this.repeat.monthdays))
|
|
this.repeat.monthdays = [];
|
|
if (angular.isUndefined(this.repeat.months))
|
|
this.repeat.months = [];
|
|
if (angular.isUndefined(this.repeat.year))
|
|
this.repeat.year = {};
|
|
if (this.repeat.count)
|
|
this.repeat.end = 'count';
|
|
else if (this.repeat.until) {
|
|
this.repeat.end = 'until';
|
|
this.repeat.until = this.repeat.until.substring(0,10).asDate();
|
|
}
|
|
else
|
|
this.repeat.end = 'never';
|
|
this.$hasCustomRepeat = this.hasCustomRepeat();
|
|
|
|
if (this.isNew) {
|
|
// Set default alarm
|
|
Component.$Preferences.ready().then(function() {
|
|
var units = { M: 'MINUTES', H: 'HOURS', D: 'DAYS', W: 'WEEKS' };
|
|
var match = /-PT?([0-9]+)([MHDW])/.exec(Component.$Preferences.defaults.SOGoCalendarDefaultReminder);
|
|
if (match) {
|
|
_this.$hasAlarm = true;
|
|
_this.alarm.quantity = parseInt(match[1]);
|
|
_this.alarm.unit = units[match[2]];
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
this.$hasAlarm = angular.isDefined(data.alarm);
|
|
}
|
|
|
|
// Allow the component to be moved to a different calendar
|
|
this.destinationCalendar = this.pid;
|
|
|
|
// Load freebusy of attendees
|
|
this.freebusy = this.updateFreeBusyCoverage();
|
|
|
|
if (this.attendees) {
|
|
_.each(this.attendees, function(attendee) {
|
|
attendee.image = Component.$gravatar(attendee.email, 32);
|
|
_this.updateFreeBusy(attendee);
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @function hasCustomRepeat
|
|
* @memberof Component.prototype
|
|
* @desc Check if the component has a custom recurrence rule.
|
|
* @returns true if the recurrence rule requires the full recurrence editor
|
|
*/
|
|
Component.prototype.hasCustomRepeat = function() {
|
|
var b = angular.isDefined(this.repeat) &&
|
|
(this.repeat.interval > 1 ||
|
|
this.repeat.days && this.repeat.days.length > 0 ||
|
|
this.repeat.monthdays && this.repeat.monthdays.length > 0 ||
|
|
this.repeat.months && this.repeat.months.length > 0);
|
|
return b;
|
|
};
|
|
|
|
/**
|
|
* @function enablePercentComplete
|
|
* @memberof Component.prototype
|
|
* @desc Check if the percent completion should be enabled with respect to the
|
|
* component's type and status.
|
|
* @returns true if the percent completion should be displayed
|
|
*/
|
|
Component.prototype.enablePercentComplete = function() {
|
|
return (this.component = 'vtodo' &&
|
|
this.status != 'not-specified' &&
|
|
this.status != 'cancelled');
|
|
};
|
|
|
|
/**
|
|
* @function coversFreeBusy
|
|
* @memberof Component.prototype
|
|
* @desc Check if a specific quarter matches the component's period
|
|
* @returns true if the quarter covers the component's period
|
|
*/
|
|
Component.prototype.coversFreeBusy = function(day, hour, quarter) {
|
|
var b = (angular.isDefined(this.freebusy[day]) &&
|
|
angular.isDefined(this.freebusy[day][hour]) &&
|
|
this.freebusy[day][hour][quarter] == 1);
|
|
return b;
|
|
};
|
|
|
|
/**
|
|
* @function updateFreeBusyCoverage
|
|
* @memberof Component.prototype
|
|
* @desc Build a 15-minute-based representation of the component's period.
|
|
* @returns an object literal hashed by days and hours and arrays of four 1's and 0's
|
|
*/
|
|
Component.prototype.updateFreeBusyCoverage = function() {
|
|
var _this = this, freebusy = {};
|
|
|
|
if (this.start && this.end) {
|
|
var roundedStart = new Date(this.start.getTime()),
|
|
roundedEnd = new Date(this.end.getTime()),
|
|
startQuarter = parseInt(roundedStart.getMinutes()/15 + 0.5),
|
|
endQuarter = parseInt(roundedEnd.getMinutes()/15 + 0.5);
|
|
roundedStart.setMinutes(15*startQuarter);
|
|
roundedEnd.setMinutes(15*endQuarter);
|
|
|
|
_.each(roundedStart.daysUpTo(roundedEnd), function(date, index) {
|
|
var currentDay = date.getDate(),
|
|
dayKey = date.getDayString(),
|
|
hourKey;
|
|
if (dayKey == _this.start.getDayString()) {
|
|
hourKey = date.getHours().toString();
|
|
freebusy[dayKey] = {};
|
|
freebusy[dayKey][hourKey] = [];
|
|
while (startQuarter > 0) {
|
|
freebusy[dayKey][hourKey].push(0);
|
|
startQuarter--;
|
|
}
|
|
}
|
|
else {
|
|
date = date.beginOfDay();
|
|
freebusy[dayKey] = {};
|
|
}
|
|
while (date.getTime() < _this.end.getTime() &&
|
|
date.getDate() == currentDay) {
|
|
hourKey = date.getHours().toString();
|
|
if (angular.isUndefined(freebusy[dayKey][hourKey]))
|
|
freebusy[dayKey][hourKey] = [];
|
|
freebusy[dayKey][hourKey].push(1);
|
|
date.addMinutes(15);
|
|
}
|
|
});
|
|
return freebusy;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @function updateFreeBusy
|
|
* @memberof Component.prototype
|
|
* @desc Update the freebusy information for the component's period for a specific attendee.
|
|
* @param {Object} card - an Card object instance of the attendee
|
|
*/
|
|
Component.prototype.updateFreeBusy = function(attendee) {
|
|
var params, url, days;
|
|
if (attendee.uid) {
|
|
params =
|
|
{
|
|
sday: this.start.getDayString(),
|
|
eday: this.end.getDayString()
|
|
};
|
|
url = ['..', '..', attendee.uid, 'freebusy.ifb'];
|
|
days = _.map(this.start.daysUpTo(this.end), function(day) { return day.getDayString(); });
|
|
|
|
if (angular.isUndefined(attendee.freebusy))
|
|
attendee.freebusy = {};
|
|
|
|
// Fetch FreeBusy information
|
|
Component.$$resource.fetch(url.join('/'), 'ajaxRead', params).then(function(data) {
|
|
_.each(days, function(day) {
|
|
var hour;
|
|
|
|
if (angular.isUndefined(attendee.freebusy[day]))
|
|
attendee.freebusy[day] = {};
|
|
|
|
if (angular.isUndefined(data[day]))
|
|
data[day] = {};
|
|
|
|
for (var i = 0; i <= 23; i++) {
|
|
hour = i.toString();
|
|
if (data[day][hour])
|
|
attendee.freebusy[day][hour] = [
|
|
data[day][hour]["0"],
|
|
data[day][hour]["15"],
|
|
data[day][hour]["30"],
|
|
data[day][hour]["45"]
|
|
];
|
|
else
|
|
attendee.freebusy[day][hour] = [0, 0, 0, 0];
|
|
}
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @function getClassName
|
|
* @memberof Component.prototype
|
|
* @desc Return the component CSS class name based on its container (calendar) ID.
|
|
* @param {string} [base] - the prefix to add to the class name (defaults to "fg")
|
|
* @returns a string representing the foreground CSS class name
|
|
*/
|
|
Component.prototype.getClassName = function(base) {
|
|
if (angular.isUndefined(base))
|
|
base = 'fg';
|
|
return base + '-folder' + (this.destinationCalendar || this.c_folder);
|
|
};
|
|
|
|
/**
|
|
* @function addAttendee
|
|
* @memberof Component.prototype
|
|
* @desc Add an attendee and fetch his freebusy info.
|
|
* @param {Object} card - an Card object instance to be added to the attendees list
|
|
*/
|
|
Component.prototype.addAttendee = function(card) {
|
|
var attendee, url, params;
|
|
if (card) {
|
|
attendee = {
|
|
name: card.c_cn,
|
|
email: card.$preferredEmail(),
|
|
role: 'req-participant',
|
|
status: 'needs-action',
|
|
uid: card.c_uid
|
|
};
|
|
if (!_.find(this.attendees, function(o) {
|
|
return o.email == attendee.email;
|
|
})) {
|
|
attendee.image = Component.$gravatar(attendee.email, 32);
|
|
if (this.attendees)
|
|
this.attendees.push(attendee);
|
|
else
|
|
this.attendees = [attendee];
|
|
this.updateFreeBusy(attendee);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @function hasAttendee
|
|
* @memberof Component.prototype
|
|
* @desc Verify if one of the email addresses of a Card instance matches an attendee.
|
|
* @param {Object} card - an Card object instance
|
|
* @returns true if the Card matches an attendee
|
|
*/
|
|
Component.prototype.hasAttendee = function(card) {
|
|
var attendee = _.find(this.attendees, function(attendee) {
|
|
return _.find(card.emails, function(email) {
|
|
return email.value == attendee.email;
|
|
});
|
|
});
|
|
return angular.isDefined(attendee);
|
|
};
|
|
|
|
/**
|
|
* @function canRemindAttendeesByEmail
|
|
* @memberof Component.prototype
|
|
* @desc Verify if the component's reminder must be send by email and if it has at least one attendee.
|
|
* @returns true if attendees can receive a reminder by email
|
|
*/
|
|
Component.prototype.canRemindAttendeesByEmail = function() {
|
|
return this.alarm.action == 'email' &&
|
|
this.attendees && this.attendees.length > 0;
|
|
};
|
|
|
|
/**
|
|
* @function addAttachUrl
|
|
* @memberof Component.prototype
|
|
* @desc Add a new attach URL if not already defined
|
|
* @param {string} attachUrl - the URL
|
|
* @returns the number of values in the list of attach URLs
|
|
*/
|
|
Component.prototype.addAttachUrl = function(attachUrl) {
|
|
if (angular.isUndefined(this.attachUrls)) {
|
|
this.attachUrls = [{value: attachUrl}];
|
|
}
|
|
else {
|
|
for (var i = 0; i < this.attachUrls.length; i++) {
|
|
if (this.attachUrls[i].value == attachUrl) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == this.attachUrls.length)
|
|
this.attachUrls.push({value: attachUrl});
|
|
}
|
|
return this.attachUrls.length - 1;
|
|
};
|
|
|
|
/**
|
|
* @function deleteAttachUrl
|
|
* @memberof Component.prototype
|
|
* @desc Remove an attach URL
|
|
* @param {number} index - the URL index in the list of attach URLs
|
|
*/
|
|
Component.prototype.deleteAttachUrl = function(index) {
|
|
if (index > -1 && this.attachUrls.length > index) {
|
|
this.attachUrls.splice(index, 1);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @function $reset
|
|
* @memberof Component.prototype
|
|
* @desc Reset the original state the component's data.
|
|
*/
|
|
Component.prototype.$reset = function() {
|
|
var _this = this;
|
|
angular.forEach(this, function(value, key) {
|
|
if (key != 'constructor' && key[0] != '$') {
|
|
delete _this[key];
|
|
}
|
|
});
|
|
angular.extend(this, this.$shadowData);
|
|
this.$shadowData = this.$omit(true);
|
|
};
|
|
|
|
/**
|
|
* @function $save
|
|
* @memberof Component.prototype
|
|
* @desc Save the component to the server.
|
|
*/
|
|
Component.prototype.$save = function() {
|
|
var _this = this, options, path = [this.pid, this.id];
|
|
|
|
if (this.isNew)
|
|
options = { action: 'saveAs' + this.type.capitalize() };
|
|
|
|
if (this.occurrenceId)
|
|
path.push(this.occurrenceId);
|
|
|
|
return Component.$$resource.save(path.join('/'), this.$omit(), options)
|
|
.then(function(data) {
|
|
// Make a copy of the data for an eventual reset
|
|
_this.$shadowData = _this.$omit(true);
|
|
return data;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @function $unwrap
|
|
* @memberof Component.prototype
|
|
* @desc Unwrap a promise.
|
|
* @param {promise} futureComponentData - a promise of some of the Component's data
|
|
*/
|
|
Component.prototype.$unwrap = function(futureComponentData) {
|
|
var _this = this;
|
|
|
|
// Expose the promise
|
|
this.$futureComponentData = futureComponentData;
|
|
|
|
// Resolve the promise
|
|
this.$futureComponentData.then(function(data) {
|
|
_this.init(data);
|
|
// Make a copy of the data for an eventual reset
|
|
_this.$shadowData = _this.$omit();
|
|
}, function(data) {
|
|
angular.extend(_this, data);
|
|
_this.isError = true;
|
|
Component.$log.error(_this.error);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @function $omit
|
|
* @memberof Component.prototype
|
|
* @desc Return a sanitized object used to send to the server.
|
|
* @return an object literal copy of the Component instance
|
|
*/
|
|
Component.prototype.$omit = function() {
|
|
var component = {}, date;
|
|
angular.forEach(this, function(value, key) {
|
|
if (key != 'constructor' && key[0] != '$') {
|
|
component[key] = angular.copy(value);
|
|
}
|
|
});
|
|
|
|
// Format times
|
|
component.startTime = component.startDate ? formatTime(component.startDate) : '';
|
|
component.endTime = component.endDate ? formatTime(component.endDate) : '';
|
|
|
|
// Update recurrence definition depending on selections
|
|
if (this.$hasCustomRepeat) {
|
|
if (this.repeat.frequency == 'monthly' && this.repeat.month.type && this.repeat.month.type == 'byday' ||
|
|
this.repeat.frequency == 'yearly' && this.repeat.year.byday) {
|
|
// BYDAY mask for a monthly or yearly recurrence
|
|
delete component.repeat.monthdays;
|
|
component.repeat.days = [{ day: this.repeat.month.day, occurrence: this.repeat.month.occurrence.toString() }];
|
|
}
|
|
else if (this.repeat.month.type) {
|
|
// montly recurrence by month days or yearly by month
|
|
delete component.repeat.days;
|
|
}
|
|
}
|
|
else if (this.repeat.frequency) {
|
|
component.repeat = { frequency: this.repeat.frequency };
|
|
}
|
|
if (this.repeat.frequency) {
|
|
if (this.repeat.end == 'until' && this.repeat.until)
|
|
component.repeat.until = this.repeat.until.stringWithSeparator('-');
|
|
else if (this.repeat.end == 'count' && this.repeat.count)
|
|
component.repeat.count = this.repeat.count;
|
|
else {
|
|
delete component.repeat.until;
|
|
delete component.repeat.count;
|
|
}
|
|
}
|
|
else {
|
|
delete component.repeat;
|
|
}
|
|
|
|
if (this.$hasAlarm) {
|
|
if (this.alarm.action == 'email' && !(this.attendees && this.attendees.length > 0)) {
|
|
// No attendees; email reminder must be sent to organizer only
|
|
this.alarm.attendees = 0;
|
|
this.alarm.organizer = 1;
|
|
}
|
|
}
|
|
else {
|
|
component.alarm = {};
|
|
}
|
|
|
|
function formatTime(dateString) {
|
|
// YYYY-MM-DDTHH:MM-ZZ:00 => YYYY-MM-DD HH:MM
|
|
var date = new Date(dateString.substring(0,10) + ' ' + dateString.substring(11,16)),
|
|
hours = date.getHours(),
|
|
minutes = date.getMinutes();
|
|
|
|
if (hours < 10) hours = '0' + hours;
|
|
if (minutes < 10) minutes = '0' + minutes;
|
|
|
|
return hours + ':' + minutes;
|
|
}
|
|
|
|
return component;
|
|
};
|
|
|
|
})();
|