diff --git a/UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox b/UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox index b591d742e..f9ffbcf99 100644 --- a/UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox +++ b/UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox @@ -178,6 +178,8 @@ md-selected-item-change="editor.addAttendee(card)" md-items="card in editor.cardFilter(editor.searchText)" md-min-length="2" + md-no-cache="true" + md-delay="300" label:placeholder="Invite Attendees" sg-enter="editor.addAttendee(editor.searchText)"> diff --git a/UI/WebServerResources/js/Common/utils.js b/UI/WebServerResources/js/Common/utils.js index 7a6f68c6c..6168d3ca8 100644 --- a/UI/WebServerResources/js/Common/utils.js +++ b/UI/WebServerResources/js/Common/utils.js @@ -149,6 +149,11 @@ Date.prototype.daysUpTo = function(otherDate) { return days; }; +String.prototype.isValidEmail = function() { + var emailRE = /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i; + return emailRE.test(this); +}; + String.prototype.asCSSIdentifier = function() { var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ' , "'", '&', '\\+' ]; diff --git a/UI/WebServerResources/js/Scheduler/Component.service.js b/UI/WebServerResources/js/Scheduler/Component.service.js index bc3acaa0c..f2dfddb35 100644 --- a/UI/WebServerResources/js/Scheduler/Component.service.js +++ b/UI/WebServerResources/js/Scheduler/Component.service.js @@ -468,6 +468,22 @@ } }; + /** + * @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 $reset * @memberof Component.prototype diff --git a/UI/WebServerResources/js/Scheduler/ComponentController.js b/UI/WebServerResources/js/Scheduler/ComponentController.js index 533704dae..2505ce9d2 100644 --- a/UI/WebServerResources/js/Scheduler/ComponentController.js +++ b/UI/WebServerResources/js/Scheduler/ComponentController.js @@ -78,12 +78,28 @@ // Autocomplete cards for attendees function cardFilter($query) { + var index, indexResult, card; if ($query) { AddressBook.$filterAll($query).then(function(results) { - vm.cardResults.splice(0, vm.cardResults.length); + // Remove cards that no longer match the search query + for (index = vm.cardResults.length - 1; index >= 0; index--) { + card = vm.cardResults[index]; + indexResult = _.findIndex(results, function(result) { + return _.find(card.emails, function(data) { + return _.find(result.emails, function(resultData) { + return resultData.value == data.value; + }); + }); + }); + if (indexResult >= 0) + results.splice(indexResult, 1); + else + vm.cardResults.splice(index, 1); + } _.each(results, function(card) { - // TODO don't show cards matching an attendee's email address - vm.cardResults.push(card); + // Add cards matching the search query but not already in the list of attendees + if (!vm.event.hasAttendee(card)) + vm.cardResults.push(card); }); }); } @@ -93,11 +109,14 @@ 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 = ''; + if (card.isValidEmail()) { + vm.event.addAttendee(new Card({ emails: [{ value: card }] })); + vm.searchText = ''; + } + } + else { + vm.event.addAttendee(card); } - vm.event.addAttendee(card); } function save(form) {