mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-05-23 04:15:26 +00:00
Initial ng/md version of the attendees editor
This commit is contained in:
@@ -151,6 +151,43 @@
|
||||
<input type="date" label:aria-label="Repeat until" ng-model="editor.event.repeat.until"/>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<!-- attendees -->
|
||||
<div layout="row" layout-align="space-between end">
|
||||
<div class="pseudo-input-container">
|
||||
<label class="pseudo-input-label"><var:string label:value="Attendees:"/></label>
|
||||
<div class="sg-avatars" ng-hide="editor.showAttendeesEditor">
|
||||
<sg-gravatar-image ng-repeat="currentAttendee in editor.event.attendees track by currentAttendee.email"
|
||||
email="{{currentAttendee.email}}"
|
||||
size="32">
|
||||
<!-- gravatar -->
|
||||
</sg-gravatar-image>
|
||||
</div>
|
||||
</div>
|
||||
<md-button type="button" class="iconButton"
|
||||
label:aria-label="repeat_CUSTOM"
|
||||
ng-click="editor.toggleAttendeesEditor()">
|
||||
<i ng-class="{'md-icon-expand-more': !editor.showAttendeesEditor, 'md-icon-expand-less': editor.showAttendeesEditor}"><!-- toggle attendees details --></i>
|
||||
</md-button>
|
||||
</div>
|
||||
<div ng-show="editor.showAttendeesEditor" class="sg-subcontent attendees">
|
||||
<var:component className="UIxAttendeesEditor" />
|
||||
</div>
|
||||
<md-autocomplete class="md-flex"
|
||||
md-selected-item="attendeeToAdd"
|
||||
md-search-text="editor.searchText"
|
||||
md-selected-item-change="editor.addAttendee(card)"
|
||||
md-items="card in editor.cardFilter(editor.searchText)"
|
||||
md-min-length="2"
|
||||
label:placeholder="Invite Attendees"
|
||||
sg-enter="editor.addAttendee(editor.searchText)">
|
||||
<span class="md-contact-suggestion" layout="row" layout-align="space-between center">
|
||||
<span class="md-contact-name"
|
||||
md-highlight-text="editor.searchText"
|
||||
md-highlight-flags="^i">{{card.$$fullname}}</span> <span class="md-contact-email"
|
||||
md-highlight-text="editor.searchText"
|
||||
md-highlight-flags="^i">{{card.$$email}}</span>
|
||||
</span>
|
||||
</md-autocomplete>
|
||||
<!-- cancel/reset/save -->
|
||||
<div class="fieldset md-layout-margin" layout="row" layout-align="end center">
|
||||
<md-button type="button" ng-click="editor.cancel()">
|
||||
|
||||
@@ -1,141 +1,57 @@
|
||||
<?xml version='1.0' standalone='yes'?>
|
||||
<!DOCTYPE var:component>
|
||||
<var:component
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:var="http://www.skyrix.com/od/binding"
|
||||
xmlns:const="http://www.skyrix.com/od/constant"
|
||||
xmlns:uix="OGo:uix"
|
||||
xmlns:rsrc="OGo:url"
|
||||
xmlns:label="OGo:label"
|
||||
className="UIxPageFrame"
|
||||
const:toolbar="none"
|
||||
const:popup="YES"
|
||||
const:cssFiles="datepicker.css,SOGoTimePicker.css"
|
||||
const:jsFiles="datepicker.js,SOGoTimePicker.js">
|
||||
<div class="popupMenu" id="attendeesMenu">
|
||||
<ul><!-- space --></ul>
|
||||
</div>
|
||||
<container
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:var="http://www.skyrix.com/od/binding"
|
||||
xmlns:const="http://www.skyrix.com/od/constant"
|
||||
xmlns:label="OGo:label"
|
||||
>
|
||||
<script type="text/javascript">
|
||||
var dayStartHour = <var:string value="dayStartHour"/>;
|
||||
var dayEndHour = <var:string value="dayEndHour"/>;
|
||||
var timeFormat = '<var:string value="userDefaults.timeFormat" const:escapeHTML="NO"/>';
|
||||
</script>
|
||||
<div id="attendeesView">
|
||||
<form const:href=""
|
||||
><div id="freeBusyViewButtons">
|
||||
<span id="freeBusyViewOptions">
|
||||
<var:string label:value="Suggest time slot:"/>
|
||||
|
||||
<span id="freeBusyTimeRange">
|
||||
<var:string label:value="Between"/>
|
||||
<select const:id="timeSlotStartLimitHour"><!--space --></select>
|
||||
<select const:id="timeSlotStartLimitMinute"><!--space --></select>
|
||||
<var:string label:value="and"/>
|
||||
<select const:id="timeSlotEndLimitHour"><!--space --></select>
|
||||
<select const:id="timeSlotEndLimitMinute"><!--space --></select>
|
||||
</span>
|
||||
|
||||
<label><input type="checkbox" const:id="workDaysOnly" const:checked="YES"
|
||||
/><var:string label:value="Work days only"/></label>
|
||||
</span>
|
||||
|
||||
<a id="nextSlot" href="#" class="button"
|
||||
><span><var:string label:value="Next slot" /></span></a>
|
||||
<a id="previousSlot" href="#" class="button"
|
||||
><span><var:string label:value="Previous slot" /></span></a>
|
||||
</div></form>
|
||||
|
||||
<div id="freeBusyView">
|
||||
<table id="freeBusy" cellspacing="0" cellpadding="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><!--space --></td>
|
||||
<td class="freeBusyHeader">
|
||||
<div><table id="freeBusyHeader" cellspacing="0" cellpadding="0">
|
||||
<tr class="freeBusyHeader1"><!--space --></tr>
|
||||
<tr class="freeBusyHeader2"><!--space --></tr>
|
||||
<tr id="currentEventPosition" class="freeBusyHeader3"><!--space --></tr>
|
||||
</table></div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="freeBusyAttendees">
|
||||
<div><table id="freeBusyAttendees" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr class="futureAttendee"
|
||||
><td class="attendeeStatus"><div><!-- space --></div></td
|
||||
><td class="attendees"><a href="#" class="button"
|
||||
readonly="readonly"><span
|
||||
><var:string label:value="newAttendee" /></span></a></td
|
||||
></tr>
|
||||
<tr class="attendeeModel"
|
||||
><td class="attendeeStatus"><div><!-- space --></div></td
|
||||
><td class="attendees"
|
||||
><input type="text" class="textField" /></td
|
||||
></tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
</td>
|
||||
<td class="freeBusyData">
|
||||
<div><table id="freeBusyData" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr class="futureData"><!-- space --></tr>
|
||||
<tr class="dataModel"><!-- space --></tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="freeBusyFooter">
|
||||
<div id="legend" onmousedown="return false;">
|
||||
<ul class="roles-legend">
|
||||
<li role="req-participant"><span class="role-icon"><!-- space --></span
|
||||
><var:string label:value="Participant"/></li>
|
||||
<li role="opt-participant"><span class="role-icon"><!-- space --></span
|
||||
><var:string label:value="Optional Participant"/></li>
|
||||
<li role="non-participant"><span class="role-icon"><!-- space --></span
|
||||
><var:string label:value="Non Participant"/></li>
|
||||
<li role="chair"><span class="role-icon"><!-- space --></span
|
||||
><var:string label:value="Chair"/></li>
|
||||
</ul>
|
||||
|
||||
<ul class="freebusy-legend">
|
||||
<li><div class="colorBox free"><!-- spacer --></div
|
||||
><var:string label:value="Free" /></li>
|
||||
<li><div class="colorBox busy"><!-- spacer --></div
|
||||
><var:string label:value="Busy" /></li>
|
||||
<!-- li><div class="colorBox maybe-busy">\- spacer -\->/div -->
|
||||
<!-- >var:string label:value="Maybe busy" />/li> -->
|
||||
<li><div class="colorBox noFreeBusy"><!-- spacer --></div
|
||||
><var:string label:value="No free-busy information" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="freeBusyReplicas">
|
||||
<div><span><var:string label:value="Start:"
|
||||
/></span><var:component className="UIxTimeDateControl"
|
||||
const:controlID="startTime"
|
||||
date="aptStartDate"
|
||||
const:dayStartHour="0"
|
||||
const:dayEndHour="23"
|
||||
/></div>
|
||||
<div><span><var:string label:value="End:"
|
||||
/></span><var:component className="UIxTimeDateControl"
|
||||
const:controlID="endTime"
|
||||
date="aptEndDate"
|
||||
const:dayStartHour="0"
|
||||
const:dayEndHour="23"
|
||||
/></div>
|
||||
</div>
|
||||
<div id="windowButtons">
|
||||
<a id="okButton" href="#" class="button actionButton"
|
||||
><span><var:string label:value="OK"/></span></a>
|
||||
<a id="cancelButton" href="#" class="button"
|
||||
><span><var:string label:value="Cancel"/></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</var:component>
|
||||
<md-content>
|
||||
<!-- attendees -->
|
||||
<md-list>
|
||||
<md-list-item>
|
||||
<div><!-- empty --></div>
|
||||
</md-list-item>
|
||||
<md-list-item ng-repeat="currentAttendee in editor.event.attendees track by currentAttendee.email">
|
||||
<sg-gravatar-image email="{{currentAttendee.email}}"
|
||||
size="32">
|
||||
<!-- gravatar -->
|
||||
</sg-gravatar-image>
|
||||
<div class="sg-tile-content">
|
||||
<div class="sg-md-subhead-multi">{{currentAttendee.name}}</div>
|
||||
<div class="sg-md-body-multi">{{currentAttendee.email}}</div>
|
||||
</div>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
<!-- freebusy -->
|
||||
<md-list class="day"
|
||||
ng-repeat="day in editor.attendeesEditor.days">
|
||||
<md-list-item layout="column" layout-align="end start">
|
||||
<div>{{day.stringWithSeparator}}</div>
|
||||
<div class="hours" layout="row" layout-align="space-between center" layout-fill="layout-fill">
|
||||
<div class="hour" ng-repeat="hour in ::editor.attendeesEditor.hours">{{hour}}</div>
|
||||
</div>
|
||||
</md-list-item>
|
||||
<md-list-item ng-repeat="currentAttendee in editor.event.attendees track by currentAttendee.email">
|
||||
<div class="hour" ng-repeat="hour in ::editor.attendeesEditor.hours">
|
||||
<div class="quarter" ng-class="{event: editor.event.coversFreeBusy(day.getDayString, hour, 0)}">
|
||||
<div class="busy" ng-show="currentAttendee.freebusy[day.getDayString][hour][0]"><!-- 15 minutes --></div>
|
||||
</div>
|
||||
<div class="quarter" ng-class="{event: editor.event.coversFreeBusy(day.getDayString, hour, 1)}">
|
||||
<div class="busy" ng-show="currentAttendee.freebusy[day.getDayString][hour][1]"><!-- 15 minutes --></div>
|
||||
</div>
|
||||
<div class="quarter" ng-class="{event: editor.event.coversFreeBusy(day.getDayString, hour, 2)}">
|
||||
<div class="busy" ng-show="currentAttendee.freebusy[day.getDayString][hour][2]"><!-- 15 minutes --></div>
|
||||
</div>
|
||||
<div class="quarter" ng-class="{event: editor.event.coversFreeBusy(day.getDayString, hour, 3)}">
|
||||
<div class="busy" ng-show="currentAttendee.freebusy[day.getDayString][hour][3]"><!-- 15 minutes --></div>
|
||||
</div>
|
||||
</div>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</md-content>
|
||||
</container>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
String.prototype.endsWith = function(suffix) {
|
||||
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
||||
};
|
||||
@@ -101,6 +103,52 @@ String.prototype.asDate = function () {
|
||||
return newDate;
|
||||
};
|
||||
|
||||
String.prototype.formatTime = function(hours, minutes) {
|
||||
var newString = this;
|
||||
|
||||
// See http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSCalendarDate.html#method$NSCalendarDate-descriptionWithCalendarFormat$
|
||||
var p = 'am', i = hours, m = minutes;
|
||||
if (hours > 12) {
|
||||
p = 'pm';
|
||||
i = hours % 12;
|
||||
}
|
||||
if (minutes < 10) {
|
||||
m = '0' + minutes;
|
||||
}
|
||||
|
||||
// %H : hour as a decimal number using 24-hour clock
|
||||
newString = newString.replace("%H", hours < 10 ? '0' + hours : hours);
|
||||
// %I : hour as a decimal number using 12-hour clock
|
||||
newString = newString.replace("%I", i < 10 ? '0' + i : i);
|
||||
// %M : minute as decimal number
|
||||
newString = newString.replace("%M", m);
|
||||
// %p : 'am' or 'pm'
|
||||
newString = newString.replace("%p", p);
|
||||
|
||||
return newString;
|
||||
};
|
||||
|
||||
Date.prototype.daysUpTo = function(otherDate) {
|
||||
var days = new Array();
|
||||
|
||||
var day1 = this.getTime();
|
||||
var day2 = otherDate.getTime();
|
||||
if (day1 > day2) {
|
||||
var tmp = day1;
|
||||
day1 = day2;
|
||||
day2 = tmp;
|
||||
}
|
||||
|
||||
var nbrDays = Math.round((day2 - day1) / 86400000) + 1;
|
||||
for (var i = 0; i < nbrDays; i++) {
|
||||
var newDate = new Date();
|
||||
newDate.setTime(day1 + (i * 86400000));
|
||||
days.push(newDate);
|
||||
}
|
||||
|
||||
return days;
|
||||
};
|
||||
|
||||
String.prototype.asCSSIdentifier = function() {
|
||||
var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' '
|
||||
, "'", '&', '\\+' ];
|
||||
@@ -145,6 +193,18 @@ Date.prototype.addDays = function(nbrDays) {
|
||||
this.setTime(milliSeconds);
|
||||
};
|
||||
|
||||
Date.prototype.addHours = function(nbrHours) {
|
||||
var milliSeconds = this.getTime();
|
||||
milliSeconds += 3600000 * nbrHours;
|
||||
this.setTime(milliSeconds);
|
||||
};
|
||||
|
||||
Date.prototype.addMinutes = function(nbrMinutes) {
|
||||
var milliSeconds = this.getTime();
|
||||
milliSeconds += 60000 * nbrMinutes;
|
||||
this.setTime(milliSeconds);
|
||||
};
|
||||
|
||||
Date.prototype.beginOfDay = function() {
|
||||
var beginOfDay = new Date(this.getTime());
|
||||
beginOfDay.setHours(0);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
this.$unwrap(futureComponentData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @memberof Component
|
||||
* @desc The factory we'll use to register with Angular
|
||||
@@ -40,6 +40,11 @@
|
||||
$categories: window.UserDefaults.SOGoCalendarCategoriesColors
|
||||
});
|
||||
|
||||
if (window.UserDefaults && window.UserDefaults.SOGoTimeFormat)
|
||||
Component.timeFormat = window.UserDefaults.SOGoTimeFormat;
|
||||
else
|
||||
Component.timeFormat = "%H:%M";
|
||||
|
||||
return Component; // return constructor
|
||||
}];
|
||||
|
||||
@@ -236,10 +241,17 @@
|
||||
* @param {object} data - attributes of component
|
||||
*/
|
||||
Component.prototype.init = function(data) {
|
||||
var _this = this;
|
||||
|
||||
this.categories = [];
|
||||
this.repeat = {};
|
||||
angular.extend(this, data);
|
||||
|
||||
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));
|
||||
|
||||
// Parse recurrence rule definition and initialize default values
|
||||
if (this.repeat.days) {
|
||||
var byDayMask = _.find(this.repeat.days, function(o) {
|
||||
@@ -281,8 +293,23 @@
|
||||
|
||||
// Allow the event 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) {
|
||||
_this.updateFreeBusy(attendee);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @function hasCustomRepeat
|
||||
* @memberof Component.prototype
|
||||
* @desc Check if the component has a custom recurrence rule.
|
||||
* @returns true if the the recurrence rule requires the full recurrence editor
|
||||
*/
|
||||
Component.prototype.hasCustomRepeat = function() {
|
||||
var b = angular.isDefined(this.repeat) &&
|
||||
(this.repeat.interval > 1 ||
|
||||
@@ -292,6 +319,114 @@
|
||||
return b;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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
|
||||
@@ -305,6 +440,34 @@
|
||||
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;
|
||||
})) {
|
||||
if (this.attendees)
|
||||
this.attendees.push(attendee);
|
||||
else
|
||||
this.attendees = [attendee];
|
||||
this.updateFreeBusy(attendee);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $reset
|
||||
* @memberof Component.prototype
|
||||
@@ -343,7 +506,7 @@
|
||||
/**
|
||||
* @function $unwrap
|
||||
* @memberof Component.prototype
|
||||
* @desc Unwrap a promise.
|
||||
* @desc Unwrap a promise.
|
||||
* @param {promise} futureComponentData - a promise of some of the Component's data
|
||||
*/
|
||||
Component.prototype.$unwrap = function(futureComponentData) {
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
ComponentController.$inject = ['$scope', '$log', '$timeout', '$state', '$previousState', '$mdSidenav', '$mdDialog', 'Calendar', 'Component', 'stateCalendars', 'stateComponent'];
|
||||
function ComponentController($scope, $log, $timeout, $state, $previousState, $mdSidenav, $mdDialog, Calendar, Component, stateCalendars, stateComponent) {
|
||||
ComponentController.$inject = ['$scope', '$log', '$q', '$timeout', '$state', '$previousState', '$mdSidenav', '$mdDialog', 'User', 'Calendar', 'Component', 'AddressBook', 'Card', 'stateCalendars', 'stateComponent'];
|
||||
function ComponentController($scope, $log, $q, $timeout, $state, $previousState, $mdSidenav, $mdDialog, User, Calendar, Component, AddressBook, Card, stateCalendars, stateComponent) {
|
||||
var vm = this;
|
||||
|
||||
vm.calendars = stateCalendars;
|
||||
@@ -15,8 +15,19 @@
|
||||
vm.categories = {};
|
||||
vm.showRecurrenceEditor = vm.event.$hasCustomRepeat;
|
||||
vm.toggleRecurrenceEditor = toggleRecurrenceEditor;
|
||||
vm.showAttendeesEditor = angular.isDefined(vm.event.attendees);
|
||||
vm.toggleAttendeesEditor = toggleAttendeesEditor;
|
||||
vm.cardFilter = cardFilter;
|
||||
vm.cardResults = [];
|
||||
vm.addAttendee = addAttendee;
|
||||
vm.cancel = cancel;
|
||||
vm.save = save;
|
||||
vm.attendeesEditor = {
|
||||
startDate: vm.event.startDate,
|
||||
endDate: vm.event.endDate,
|
||||
days: getDays(),
|
||||
hours: getHours()
|
||||
};
|
||||
|
||||
// Open sidenav when loading the view;
|
||||
// Return to previous state when closing the sidenav.
|
||||
@@ -36,11 +47,59 @@
|
||||
}, 100); // don't ask why
|
||||
});
|
||||
|
||||
$scope.$watch('editor.event.startDate', function(newStartDate, oldStartDate) {
|
||||
if (newStartDate) {
|
||||
$timeout(function() {
|
||||
vm.event.start = new Date(newStartDate.substring(0,10) + ' ' + newStartDate.substring(11,16));
|
||||
vm.event.freebusy = vm.event.updateFreeBusyCoverage();
|
||||
vm.attendeesEditor.days = getDays();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('editor.event.endDate', function(newEndDate, oldEndDate) {
|
||||
if (newEndDate) {
|
||||
$timeout(function() {
|
||||
vm.event.end = new Date(newEndDate.substring(0,10) + ' ' + newEndDate.substring(11,16));
|
||||
vm.event.freebusy = vm.event.updateFreeBusyCoverage();
|
||||
vm.attendeesEditor.days = getDays();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function toggleRecurrenceEditor() {
|
||||
vm.showRecurrenceEditor = !vm.showRecurrenceEditor;
|
||||
vm.event.$hasCustomRepeat = vm.showRecurrenceEditor;
|
||||
}
|
||||
|
||||
function toggleAttendeesEditor() {
|
||||
vm.showAttendeesEditor = !vm.showAttendeesEditor;
|
||||
}
|
||||
|
||||
// Autocomplete cards for attendees
|
||||
function cardFilter($query) {
|
||||
if ($query) {
|
||||
AddressBook.$filterAll($query).then(function(results) {
|
||||
vm.cardResults.splice(0, vm.cardResults.length);
|
||||
_.each(results, function(card) {
|
||||
// TODO don't show cards matching an attendee's email address
|
||||
vm.cardResults.push(card);
|
||||
});
|
||||
});
|
||||
}
|
||||
return vm.cardResults;
|
||||
}
|
||||
|
||||
function addAttendee(card) {
|
||||
if (angular.isString(card)) {
|
||||
// User pressed "Enter" in search field, adding a non-matching card
|
||||
// TODO: only create card if the string is an email address
|
||||
card = new Card({ emails: [{ value: card }] });
|
||||
vm.searchText = '';
|
||||
}
|
||||
vm.event.addAttendee(card);
|
||||
}
|
||||
|
||||
function save(form) {
|
||||
if (form.$valid) {
|
||||
vm.event.$save()
|
||||
@@ -61,6 +120,27 @@
|
||||
}
|
||||
$mdSidenav('right').close();
|
||||
}
|
||||
|
||||
function getDays() {
|
||||
var days = [];
|
||||
|
||||
if (vm.event.start && vm.event.end)
|
||||
days = vm.event.start.daysUpTo(vm.event.end);
|
||||
|
||||
return _.map(days, function(date) {
|
||||
return { stringWithSeparator: date.stringWithSeparator(),
|
||||
getDayString: date.getDayString() };
|
||||
});
|
||||
}
|
||||
|
||||
function getHours() {
|
||||
var hours = [];
|
||||
for (var i = 0; i <= 23; i++) {
|
||||
//hours.push(Component.timeFormat.formatTime(i, 0));
|
||||
hours.push(i.toString());
|
||||
}
|
||||
return hours;
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
|
||||
@@ -80,6 +80,13 @@ div.md-tile-left {
|
||||
height:(7 * $line);
|
||||
|
||||
}
|
||||
.sg-avatars {
|
||||
margin: ($mg / 2) 0 0 ($mg / 2);
|
||||
img {
|
||||
border-radius: 100%;
|
||||
margin-right: ($mg / 2);
|
||||
}
|
||||
}
|
||||
// Avatar placeholder
|
||||
// ------------------------------------
|
||||
.md-tile-left:before {
|
||||
|
||||
@@ -50,11 +50,14 @@ $hours_margin: 50px;
|
||||
}
|
||||
|
||||
.sg-calendar-tile-header {
|
||||
color: sg-color($sogoPaper, 800);
|
||||
font-size: $sg-font-size-2;
|
||||
font-weight: $sg-font-light;
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: sg-color($sogoPaper, 800);
|
||||
font-size: $sg-font-size-2;
|
||||
font-weight: $sg-font-light;
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.daysView {
|
||||
@@ -156,3 +159,56 @@ $hours_margin: 50px;
|
||||
min-height: 15px; /* for 15-minute events */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Attendees Editor */
|
||||
.attendees {
|
||||
overflow: hidden;
|
||||
overflow-x: scroll;
|
||||
md-content {
|
||||
display: table-row;
|
||||
}
|
||||
md-list {
|
||||
display: table-cell;
|
||||
&.day {
|
||||
min-width: 408px;
|
||||
md-list-item {
|
||||
padding: 0;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
md-list-item {
|
||||
&:hover {
|
||||
background-color: initial;
|
||||
}
|
||||
img {
|
||||
margin-right: $mg/4;
|
||||
}
|
||||
}
|
||||
.hours {
|
||||
font-size: 9px;
|
||||
}
|
||||
.hour {
|
||||
display: flex;
|
||||
border-left: 1px solid sg-color($sogoPaper, 100);
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
flex-wrap: nowrap;
|
||||
flex-grow: 0;
|
||||
flex-basis: 17px; // hour's width + hour's border
|
||||
align-items: stretch;
|
||||
}
|
||||
.quarter {
|
||||
min-width: 4px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
.busy {
|
||||
margin: 8px 0;
|
||||
min-width: 4px;
|
||||
background-color: sg-color($sogoPaper, 600);
|
||||
}
|
||||
&.event {
|
||||
background-color: sg-color($sogoBlue, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user