123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- // exported globals
- var VCard;
- (function() {
- VCard = function(attributes) {
- this.changed = false;
- if(typeof(attributes) === 'object') {
- for(var key in attributes) {
- this[key] = attributes[key];
- this.changed = true;
- }
- }
- };
- VCard.prototype = {
- // Check validity of this VCard instance. Properties that can be generated,
- // will be generated. If any error is found, false is returned and vcard.errors
- // set to an Array of [attribute, errorType] arrays.
- // Otherwise true is returned.
- //
- // In case of multivalued properties, the "attribute" part of the error is
- // the attribute name, plus it's index (starting at 0). Example: email0, tel7, ...
- //
- // It is recommended to call this method even if this VCard object was imported,
- // as some software (e.g. Gmail) doesn't generate UIDs.
- validate: function() {
- var errors = [];
- function addError(attribute, type) {
- errors.push([attribute, type]);
- }
- if(! this.fn) { // FN is a required attribute
- addError("fn", "required");
- }
- // make sure multivalued properties are *always* in array form
- for(var key in VCard.multivaluedKeys) {
- if(this[key] && ! (this[key] instanceof Array)) {
- this[key] = [this[key]];
- }
- }
- // make sure compound fields have their type & value set
- // (to prevent mistakes such as vcard.addAttribute('email', 'foo@bar.baz')
- function validateCompoundWithType(attribute, values) {
- for(var i in values) {
- var value = values[i];
- if(typeof(value) !== 'object') {
- errors.push([attribute + '-' + i, "not-an-object"]);
- } else if(! value.type) {
- errors.push([attribute + '-' + i, "missing-type"]);
- } else if(! value.value) { // empty values are not allowed.
- errors.push([attribute + '-' + i, "missing-value"]);
- }
- }
- }
- if(this.email) {
- validateCompoundWithType('email', this.email);
- }
- if(this.tel) {
- validateCompoundWithType('email', this.tel);
- }
- if(! this.uid) {
- this.addAttribute('uid', uuidFast());
- }
- if(! this.rev) {
- this.addAttribute('rev', new Date());
- }
- this.errors = errors;
- return ! (errors.length > 0);
- },
- // generate a UID. This generates a UUID with uuid: URN namespace, as suggested
- // by RFC 6350, 6.7.6
- generateUID: function() {
- return 'uuid:' + Math.uuid();
- },
- // generate revision timestamp (a full ISO 8601 date/time string in basic format)
- generateRev: function() {
- return (new Date()).toISOString().replace(/[\.\:\-]/g, '');
- },
- // Set the given attribute to the given value.
- // This sets vcard.changed to true, so you can check later whether anything
- // was updated by your code.
- setAttribute: function(key, value) {
- this[key] = value;
- this.changed = true;
- },
- // Set the given attribute to the given value.
- // If the given attribute's key has cardinality > 1, instead of overwriting
- // the current value, an additional value is appended.
- addAttribute: function(key, value) {
- console.log('add attribute', key, value);
- if(! value) {
- return;
- }
- if(VCard.multivaluedKeys[key]) {
- if(this[key]) {
- console.log('multivalued push');
- this[key].push(value)
- } else {
- console.log('multivalued set');
- this.setAttribute(key, [value]);
- }
- } else {
- this.setAttribute(key, value);
- }
- },
- // convenience method to get a JSON serialized jCard.
- toJSON: function() {
- return JSON.stringify(this.toJCard());
- },
- // Copies all properties (i.e. all specified in VCard.allKeys) to a new object
- // and returns it.
- // Useful to serialize to JSON afterwards.
- toJCard: function() {
- var jcard = {};
- for(var k in VCard.allKeys) {
- var key = VCard.allKeys[k];
- if(this[key]) {
- jcard[key] = this[key];
- }
- }
- return jcard;
- },
- // synchronizes two vcards, using the mechanisms described in
- // RFC 6350, Section 7.
- // Returns a new VCard object.
- // If a property is present in both source vcards, and that property's
- // maximum cardinality is 1, then the value from the second (given) vcard
- // precedes.
- //
- // TODO: implement PID matching as described in 7.3.1
- merge: function(other) {
- if(typeof(other.uid) !== 'undefined' &&
- typeof(this.uid) !== 'undefined' &&
- other.uid !== this.uid) {
- // 7.1.1
- throw "Won't merge vcards without matching UIDs.";
- }
- var result = new VCard();
- function mergeProperty(key) {
- if(other[key]) {
- if(other[key] == this[key]) {
- result.setAttribute(this[key]);
- } else {
- result.addAttribute(this[key]);
- result.addAttribute(other[key]);
- }
- } else {
- result[key] = this[key];
- }
- }
- for(key in this) { // all properties of this
- mergeProperty(key);
- }
- for(key in other) { // all properties of other *not* in this
- if(! result[key]) {
- mergeProperty(key);
- }
- }
- }
- };
- VCard.enums = {
- telType: ["text", "voice", "fax", "cell", "video", "pager", "textphone"],
- relatedType: ["contact", "acquaintance", "friend", "met", "co-worker",
- "colleague", "co-resident", "neighbor", "child", "parent",
- "sibling", "spouse", "kin", "muse", "crush", "date",
- "sweetheart", "me", "agent", "emergency"],
- // FIXME: these aren't actually defined anywhere. just very commmon.
- // maybe there should be more?
- emailType: ["work", "home", "internet"],
- langType: ["work", "home"],
-
- };
- VCard.allKeys = [
- 'fn', 'n', 'nickname', 'photo', 'bday', 'anniversary', 'gender',
- 'tel', 'email', 'impp', 'lang', 'tz', 'geo', 'title', 'role', 'logo',
- 'org', 'member', 'related', 'categories', 'note', 'prodid', 'rev',
- 'sound', 'uid'
- ];
- VCard.multivaluedKeys = {
- email: true,
- tel: true,
- geo: true,
- title: true,
- role: true,
- logo: true,
- org: true,
- member: true,
- related: true,
- categories: true,
- note: true
- };
- })();
- module.exports = VCard;
- function uuidFast() {
- var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''), uuid = new Array(36), rnd=0, r;
- for (var i = 0; i < 36; i++) {
- if (i==8 || i==13 || i==18 || i==23) {
- uuid[i] = '-';
- } else if (i==14) {
- uuid[i] = '4';
- } else {
- if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
- r = rnd & 0xf;
- rnd = rnd >> 4;
- uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
- }
- }
- return uuid.join('');
- };
|