/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ (function() { 'use strict'; /** * @name Card * @constructor * @param {object} futureCardData * @param {string} [partial] */ function Card(futureCardData, partial) { // Data is immediately available if (typeof futureCardData.then !== 'function') { this.init(futureCardData, partial); if (this.pid && !this.id) { // Prepare for the creation of a new card; // Get UID from the server. var newCardData = Card.$$resource.newguid(this.pid); this.$unwrap(newCardData); this.isNew = true; } } else { // The promise will be unwrapped first this.$unwrap(futureCardData); } } Card.$TEL_TYPES = ['work', 'home', 'cell', 'fax', 'pager']; Card.$EMAIL_TYPES = ['work', 'home', 'pref']; Card.$URL_TYPES = ['work', 'home', 'pref']; Card.$ADDRESS_TYPES = ['work', 'home']; /** * @memberof Card * @desc The factory we'll use to register with Angular. * @returns the Card constructor */ Card.$factory = ['$timeout', 'sgSettings', 'Resource', 'Preferences', 'Gravatar', function($timeout, Settings, Resource, Preferences, Gravatar) { angular.extend(Card, { $$resource: new Resource(Settings.activeUser('folderURL') + 'Contacts', Settings.activeUser()), $timeout: $timeout, $gravatar: Gravatar }); // Initialize categories from user's defaults Preferences.ready().then(function() { if (Preferences.defaults.SOGoContactsCategories) { Card.$categories = Preferences.defaults.SOGoContactsCategories; } }); return Card; // return constructor }]; /** * @module SOGo.ContactsUI * @desc Factory registration of Card in Angular module. */ try { angular.module('SOGo.ContactsUI'); } catch(e) { angular.module('SOGo.ContactsUI', ['SOGo.Common']); } angular.module('SOGo.ContactsUI') .factory('Card', Card.$factory); /** * @memberof Card * @desc Fetch a card from a specific addressbook. * @param {string} addressbookId - the addressbook ID * @param {string} cardId - the card ID * @see {@link AddressBook.$getCard} */ Card.$find = function(addressbookId, cardId) { var futureCardData = this.$$resource.fetch([addressbookId, cardId].join('/'), 'view'); if (cardId) return new Card(futureCardData); // a single card return Card.$unwrapCollection(futureCardData); // a collection of cards }; /** * @function filterCategories * @memberof Card.prototype * @desc Search for categories matching some criterias * @param {string} search - the search string to match * @returns a collection of strings */ Card.filterCategories = function(query) { var re = new RegExp(query, 'i'); return _.filter(Card.$categories, function(category) { return category.search(re) != -1; }); }; /** * @memberof Card * @desc Unwrap to a collection of Card instances. * @param {object} futureCardData */ Card.$unwrapCollection = function(futureCardData) { var collection = {}; collection.$futureCardData = futureCardData; futureCardData.then(function(cards) { Card.$timeout(function() { angular.forEach(cards, function(data, index) { collection[data.id] = new Card(data); }); }); }); return collection; }; /** * @function init * @memberof Card.prototype * @desc Extend instance with required attributes and new data. * @param {object} data - attributes of card */ Card.prototype.init = function(data, partial) { this.refs = []; angular.extend(this, data); if (!this.$$fullname) this.$$fullname = this.$fullname(); if (!this.$$email) this.$$email = this.$preferredEmail(partial); if (!this.$$image) this.$$image = this.image || Card.$gravatar(this.$preferredEmail(partial), 32); this.selected = false; // An empty attribute to trick md-autocomplete when adding attendees from the appointment editor this.empty = ' '; }; /** * @function $id * @memberof Card.prototype * @desc Return the card ID. * @returns the card ID */ Card.prototype.$id = function() { return this.$futureCardData.then(function(data) { return data.id; }); }; /** * @function $save * @memberof Card.prototype * @desc Save the card to the server. */ Card.prototype.$save = function() { var _this = this, action = 'saveAsContact'; if (this.c_component == 'vlist') action = 'saveAsList'; return Card.$$resource.save([this.pid, this.id || '_new_'].join('/'), this.$omit(), { action: action }) .then(function(data) { // Make a copy of the data for an eventual reset _this.$shadowData = _this.$omit(true); return data; }); }; Card.prototype.$delete = function(attribute, index) { if (attribute) { if (index > -1 && this[attribute].length > index) { this[attribute].splice(index, 1); } else delete this[attribute]; } else { // No arguments -- delete card return Card.$$resource.remove([this.pid, this.id].join('/')); } }; Card.prototype.$fullname = function() { var fn = this.c_cn || '', names; if (fn.length === 0) { names = []; if (this.c_givenname && this.c_givenname.length > 0) names.push(this.c_givenname); if (this.nickname && this.nickname.length > 0) names.push('' + this.nickname + ''); if (this.c_sn && this.c_sn.length > 0) names.push(this.c_sn); if (names.length > 0) fn = names.join(' '); else if (this.c_org && this.c_org.length > 0) { fn = this.c_org; } else if (this.emails && this.emails.length > 0) { fn = _.find(this.emails, function(i) { return i.value !== ''; }).value; } else if (this.c_cn && this.c_cn.length > 0) { fn = this.c_cn; } } return fn; }; Card.prototype.$description = function() { var description = []; if (this.title) description.push(this.title); if (this.role) description.push(this.role); if (this.orgUnits && this.orgUnits.length > 0) _.forEach(this.orgUnits, function(unit) { if (unit.value !== '') description.push(unit.value); }); if (this.org) description.push(this.org); if (this.description) description.push(this.description); return description.join(', '); }; /** * @function $preferredEmail * @memberof Card.prototype * @desc Get the preferred email address. * @param {string} [partial] - a partial string that the email must match * @returns the first email address of type "pref" or the first address if none found */ Card.prototype.$preferredEmail = function(partial) { var email, re; if (partial) { re = new RegExp(partial, 'i'); email = _.find(this.emails, function(o) { return re.test(o.value); }); } if (email) { email = email.value; } else { email = _.find(this.emails, function(o) { return o.type == 'pref'; }); if (email) { email = email.value; } else if (this.emails && this.emails.length) { email = this.emails[0].value; } else { email = ''; } } return email; }; /** * @function $shortFormat * @memberof Card.prototype * @param {string} [partial] - a partial string that the email must match * @returns the fullname along with a matching email address in parentheses */ Card.prototype.$shortFormat = function(partial) { var fullname = this.$fullname(), email = this.$preferredEmail(partial); if (email && email != fullname) fullname += ' <' + email + '>'; return fullname; }; /** * @function $birthday * @memberof Card.prototype * @returns the formatted birthday object */ Card.prototype.$birthday = function() { if (this.birthday) { return [this.birthday.getFullYear(), this.birthday.getMonth() + 1, this.birthday.getDate()].join('/'); } return ''; }; Card.prototype.$isCard = function() { return this.c_component == 'vcard'; }; Card.prototype.$isList = function() { return this.c_component == 'vlist'; }; Card.prototype.$addOrgUnit = function(orgUnit) { if (angular.isUndefined(this.orgUnits)) { this.orgUnits = [{value: orgUnit}]; } else { for (var i = 0; i < this.orgUnits.length; i++) { if (this.orgUnits[i].value == orgUnit) { break; } } if (i == this.orgUnits.length) this.orgUnits.push({value: orgUnit}); } return this.orgUnits.length - 1; }; Card.prototype.$addCategory = function(category) { if (angular.isUndefined(this.categories)) { this.categories = [{value: category}]; } else { for (var i = 0; i < this.categories.length; i++) { if (this.categories[i].value == category) { break; } } if (i == this.categories.length) this.categories.push({value: category}); } }; Card.prototype.$addEmail = function(type) { if (angular.isUndefined(this.emails)) { this.emails = [{type: type, value: ''}]; } else if (_.isUndefined(_.find(this.emails, function(i) { return i.value === ''; }))) { this.emails.push({type: type, value: ''}); } return this.emails.length - 1; }; Card.prototype.$addPhone = function(type) { if (angular.isUndefined(this.phones)) { this.phones = [{type: type, value: ''}]; } else if (_.isUndefined(_.find(this.phones, function(i) { return i.value === ''; }))) { this.phones.push({type: type, value: ''}); } return this.phones.length - 1; }; Card.prototype.$addUrl = function(type, url) { if (angular.isUndefined(this.urls)) { this.urls = [{type: type, value: url}]; } else if (_.isUndefined(_.find(this.urls, function(i) { return i.value == url; }))) { this.urls.push({type: type, value: url}); } return this.urls.length - 1; }; Card.prototype.$addAddress = function(type, postoffice, street, street2, locality, region, country, postalcode) { if (angular.isUndefined(this.addresses)) { this.addresses = [{type: type, postoffice: postoffice, street: street, street2: street2, locality: locality, region: region, country: country, postalcode: postalcode}]; } else if (!_.find(this.addresses, function(i) { return i.street == street && i.street2 == street2 && i.locality == locality && i.country == country && i.postalcode == postalcode; })) { this.addresses.push({type: type, postoffice: postoffice, street: street, street2: street2, locality: locality, region: region, country: country, postalcode: postalcode}); } return this.addresses.length - 1; }; Card.prototype.$addMember = function(email) { var card = new Card({email: email, emails: [{value: email}]}), i; if (angular.isUndefined(this.refs)) { this.refs = [card]; } else if (email.length === 0) { this.refs.push(card); } else { for (i = 0; i < this.refs.length; i++) { if (this.refs[i].email == email) { break; } } if (i == this.refs.length) this.refs.push(card); } return this.refs.length - 1; }; /** * @function $reset * @memberof Card.prototype * @desc Reset the original state the card's data. */ Card.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); // Reinstanciate Card objects for list members angular.forEach(this.refs, function(o, i) { if (o.email) o.emails = [{value: o.email}]; _this.refs[i] = new Card(o); }); this.$shadowData = this.$omit(true); }; /** * @function $updateMember * @memberof Card.prototype * @desc Update an existing list member from a Card instance. * A list member has the following attribtues: * - email * - reference * - fn * @param {number} index * @param {string} email * @param {Card} card */ // Card.prototype.$updateMember = function(index, email, card) { // var ref = { // email: email, // emails: [{value: email}], // reference: card.c_name, // c_cn: card.$fullname() // }; // this.refs[index] = new Card(ref); // }; /** * @function $unwrap * @memberof Card.prototype * @desc Unwrap a promise and make a copy of the resolved data. * @param {object} futureCardData - a promise of the Card's data */ Card.prototype.$unwrap = function(futureCardData) { var _this = this; // Expose the promise this.$futureCardData = futureCardData; // Resolve the promise this.$futureCardData.then(function(data) { // Calling $timeout will force Angular to refresh the view Card.$timeout(function() { _this.init(data); // Instanciate Card objects for list members angular.forEach(_this.refs, function(o, i) { if (o.email) o.emails = [{value: o.email}]; o.id = o.reference; _this.refs[i] = new Card(o); }); if (_this.birthday) { _this.birthday = new Date(_this.birthday * 1000); } // Make a copy of the data for an eventual reset _this.$shadowData = _this.$omit(true); }); }); }; /** * @function $omit * @memberof Card.prototype * @desc Return a sanitized object used to send to the server. * @param {boolean} [deep] - make a deep copy if true * @return an object literal copy of the Card instance */ Card.prototype.$omit = function(deep) { var card = {}; angular.forEach(this, function(value, key) { if (key == 'refs') { card.refs = _.map(value, function(o) { return o.$omit(deep); }); } else if (key != 'constructor' && key[0] != '$') { if (deep) card[key] = angular.copy(value); else card[key] = value; } }); // We convert back our birthday object if (card.birthday) card.birthday = card.birthday.getTime()/1000; else card.birthday = 0; return card; }; Card.prototype.toString = function() { var desc = this.id + ' ' + this.$$fullname; if (this.$$email) desc += ' <' + this.$$email + '>'; return '[' + desc + ']'; }; })();