NeahNew/node_modules/vcard-js/lib/VCard.js
2025-05-03 14:17:46 +02:00

714 lines
14 KiB
JavaScript

var util = require('util');
var fs = require('fs');
var quoted_printable = require('quoted-printable');
var utf8 = require('utf8');
/**
*
* @class VCard
* @version 1.2.1
* @param {array} lines
* @param {object} [opt]
* @param {string} [opt.filename]
* @param {VCard~filterCb} [opt.filter]
* @param {VCard~debugCb} [opt.debug]
*
*/
function VCard(lines, opt){
opt = opt || {};
var self = opt.vCard = this;
/**
* @member {array} VCard#_origin
* @protected
*/
this._origin = lines;
/**
*
* @member {string} VCard#_filename
* @protected
*/
this._filename = opt.filename;
/**
* @member {string} VCard#version
*/
this.version = getVersion(lines);
/**
* @member {VCard.Item[]} VCard#items
*/
this.items = [];
lines.forEach(function(line){
var item = new VCard.Item(line, opt);
if(opt.filter){
if(!opt.filter(item))
return;
}
self.items.push(item);
});
(opt.debug || VCard.debug)(self);
function getVersion(lines){
var line = find(lines, function(line){
if(util.isArray(line))
return /^version$/i.test(line[0]);
else
return /^version:/i.test(line);
});
if(util.isString(line))
return line.substr(8);
else if(util.isArray(line))
return line[3];
else
return null;
};
}
/**
*
* @function VCard#toString
* @param {object} [opt]
* @param {string} [opt.version]
* @param {VCard~filterCb} [opt.filter]
* @returns {string}
*/
/**
* @function VCard#toString
* @param {string} version
* @returns {string}
*/
VCard.prototype.toString = function(opt){
opt = opt || {};
var self = this;
var list = [];
if(util.isString(opt)){
opt = {
version : opt
};
}
self.items.forEach(function(item){
if(/^(begin|end):/i.test(item.name))
return;
if(opt.filter){
if(!opt.filter(item))
return;
}
var line = item.toString(opt);
line && list.push(line);
});
if(list.length){
list.unshift('BEGIN:VCARD');
list.push('END:VCARD');
return list.join(VCard.EOL);
}
return '';
};
/**
* @function VCard#toJSON
* @returns {array}
*/
VCard.prototype.toJSON = function(){
var self = this;
var list = [];
self.items.forEach(function(item){
if(/^(begin|end):/i.test(item.name))
return;
list.push(item.toJSON());
});
return ['vcard', list];
};
/**
* @function VCard#find
* @param {string} name
* @since 1.2.0
* @returns {VCard.Item[]}
*/
/**
* @function VCard#find
* @param {VCard~filterCb} filter
* @since 1.2.0
* @returns {VCard.Item[]}
*/
VCard.prototype.find = function(filter){
var self = this;
if(!util.isFunction(filter)){
var name = filter.toLowerCase();
filter = function(item){
return item.name.toLowerCase() === name;
};
}
return self.items.filter(filter);
};
/**
* @function VCard#add
* @param {(string|array)} line
* @since 1.2.0
* @returns {VCard.Item}
*/
VCard.prototype.add = function(line){
var self = this;
var item = new VCard.Item(line, {
vCard : self
});
self.items.push(item);
return item;
};
/**
* @function VCard#remove
* @param {string} name
* @since 1.2.0
* @returns {number}
*/
VCard.prototype.remove = function(name){
var self = this;
var i = 0, j = 0, item;
name = name.toLowerCase();
while(i < self.items.length){
item = self.items[i];
if(item.name.toLowerCase() === name){
self.items.splice(i, 1);
j++;
}else{
i++;
}
}
return j;
};
VCard.VCard = VCard;
/**
* @constant {string} VCard.EOL
* @default \r\n
*/
VCard.EOL = '\r\n';
/**
* @constant {number} VCard.MAX_WIDTH
* @default 1024
*/
VCard.MAX_WIDTH = 1024;
/**
* @function VCard.identifyType
* @param {VCard.Item} item
* @since 1.2.0
*/
VCard.identifyType = function(item){
if(item.name === 'LANG'){
item.type = 'language-tag';
}else if(item.name === 'REV'){
item.type = 'timestamp';
}else if(/^(http|https)\:\/\//i.test(item.value)){
item.type = 'uri';
}
};
/**
* @function VCard.debug
* @param {*} any
* @abstract
*/
VCard.debug = function(any){
};
/**
* @class VCard.Item
* @param {(string|array)} line
* @param {object} [opt]
* @param {VCard} [opt.vCard]
* @param {VCard~debugCb} [opt.debug]
*/
VCard.Item = function(line, opt){
opt = opt || {};
var self = this;
/**
* @member {VCard} VCard.Item#_vCard
* @protected
*/
this._vCard = opt.vCard;
/**
* @member {(string|array)} VCard.Item#_origin
* @protected
*/
this._origin = line;
/**
* @member {string} VCard.Item#name
*/
this.name = '';
/**
* @member {string} VCard.Item#value
*/
this.value = '';
/**
* @member {object} VCard.Item#params
*/
this.params = {};
/**
* @member {string} VCard.Item#type
* @default text
* @since 1.2.0
*/
this.type = 'text';
if(!line)
return;
else if(util.isString(line))
parse();
else if(util.isArray(line))
init();
(opt.debug || VCard.debug)(self);
function parse(){
var frag = line, results, k, v, i;
while(frag){
if(!self.name){
results = frag.match(/^([\w\-]+)(?=:|;|\r\n|\r|\n)/g);
if(results){
self.name = v = results[0];
frag = frag.substr(v.length);
continue;
}
}
if(frag.indexOf(':') !== 0){
frag = frag.replace(/^;/, '');
results = frag.match(/^([\w\-]+)="/);
if(results){
k = results[1];
frag = frag.substr(k.length + 2);
i = frag.search(/[^\\]"/);
if(i !== -1)
v = frag.substr(0, i + 1);
else
v = frag;
setParams(k, v);
frag = frag.substr(v.length + 1);
continue;
}
results = frag.match(/^([\w\-]+)=/)
if(results){
k = results[1];
frag = frag.substr(k.length + 1);
i = frag.search(/[^\\](;|:)/);
if(i !== -1)
v = frag.substr(0, i + 1);
else
v = frag;
setParams(k, v);
frag = frag.substr(v.length);
continue;
}
i = frag.search(/[^\\](;|:)/);
if(i !== -1){
v = frag.substr(0, i + 1);
setParams('type', v);
frag = frag.substr(v.length);
continue;
}
}
self.value = frag.replace(/^:/, '');
frag = null;
}
VCard.identifyType(self);
}
function init(){
self.name = line[0];
self.type = line[2];
self.value = line[3];
var k, o = line[1];
for(k in o){
self.params[k] = o[k];
}
self.encode(self.value);
}
function setParams(k, v){
var obj = self.params;
k = k.toUpperCase();
if(/^(type)$/i.test(k))
v = v.split(/\s*,\s*/);
if(obj.hasOwnProperty(k)){
if(!util.isArray(obj[k]))
obj[k] = [obj[k]];
if(util.isArray(v))
obj[k] = obj[k].concat(v);
else
obj[k].push(v);
return;
}
obj[k] = v;
}
};
/**
* @function VCard.Item#toString
* @param {object} [opt]
* @param {string} [opt.version]
* @param {VCard~replaceCb} [opt.replace]
* @returns {string}
*/
/**
* @function VCard.Item#toString
* @param {string} version
* @returns {string}
*/
VCard.Item.prototype.toString = function(opt){
opt = opt || {};
var self = this;
var line = self.name.toUpperCase(), k, v;
var value = self.value.toString();
if(util.isString(opt)){
opt = {
version : opt
};
}
var version = opt.version;
if(!version){
version = self._vCard ? self._vCard.version : '4.0';
}
for(k in self.params){
v = self.params[k];
k = k.toUpperCase();
if(version === '2.1' && /^(type)$/i.test(k)){
line += ';';
if(/^(photo)$/i.test(self.name))
line += k + '=';
if(util.isArray(v))
line += v.join(';');
else
line += v;
continue;
}
if(/^(impp)$/i.test(self.name)){
if(util.isArray(v)){
v.forEach(function(v){
line += ';' + k + '=' + v;
});
continue;
}
}
if(k === 'ENCODING'){
if(/^(BASE64|b)$/.test(v) || /^data\:.+\;base64\,/.test(value)){
if(version === '2.1')
v = 'BASE64';
else if(version === '3.0')
v = 'b';
else
v = '';
if(v)
value = value.replace(/^data\:.+\;base64\,/, '');
}
}
v = v.toString();
if(!v)
continue;
if(version >= 4 && /\s/.test(v)){
line += ';' + k + '="' + v + '"';
continue;
}
line += ';' + k + '=' + v;
}
if(version < 4)
value = value.replace(/^geo\:/i, '');
if(/^version$/i.test(self.name))
line += ':' + version;
else if(value)
line += ':' + value;
else
line = '';
if(line.length > VCard.MAX_WIDTH){
line = line.replace(/(^.{75})|(.{74})/g, function(v){
return v + VCard.EOL + ' ';
});
line = line.replace(/\s$/, '');
if(version === '2.1')
line += VCard.EOL;
}
if(opt.replace){
line = opt.replace(line, self);
}
return line;
};
/**
* @function VCard.Item#toJSON
* @returns {array}
*/
VCard.Item.prototype.toJSON = function(){
var self = this;
var name = self.name.toLowerCase();
var type = self.type;
var value = self.decode();
var params = {};
var k, v;
for(k in self.params){
v = self.params[k];
if(/^value$/i.test(k)){
type = v;
continue;
}else if(/^label$/i.test(k) && !value){
value = v;
}
params[k.toLowerCase()] = v;
}
switch(type){
case 'boolean':
value = value === 'TRUE';
break;
case 'integer':
value = parseInt(value);
break;
case 'float':
value = parseFloat(value);
break;
}
return [name, params, type, value];
};
/**
* @function VCard.Item#encode
* @param {string} value
* @since 1.1.0
*/
VCard.Item.prototype.encode = function(value){
switch(this.params.ENCODING){
case 'QUOTED-PRINTABLE':
this.quotedPrintable(value);
break;
case 'BASE64':
case 'b':
this.base64(value);
break;
}
};
/**
* @function VCard.Item#decode
* @returns {string}
* @since 1.1.0
*/
VCard.Item.prototype.decode = function(){
switch(this.params.ENCODING){
case 'QUOTED-PRINTABLE':
return this.quotedPrintable();
case 'BASE64':
case 'b':
return this.base64();
default:
return this.value;
}
};
/**
* @function VCard.Item#quotedPrintable
* @param {string} value
* @since 1.2.0
*/
/**
* @function VCard.Item#quotedPrintable
* @returns {string}
* @since 1.2.0
*/
VCard.Item.prototype.quotedPrintable = function(value){
if(value){
value = utf8.encode(value);
this.value = quoted_printable.encode(value);
this.params.CHARSET = 'UTF-8';
this.params.ENCODING = 'QUOTED-PRINTABLE';
}else{
value = quoted_printable.decode(this.value);
value = utf8.decode(value);
return value;
}
};
/**
* @function VCard.Item#base64
* @param {string} value
* @since 1.2.0
*/
/**
* @function VCard.Item#base64
* @returns {string}
* @since 1.2.0
*/
VCard.Item.prototype.base64 = function(value){
if(value){
this.value = value;
this.param.ENCODING = this.param.ENCODING || 'BASE64';
}else{
return this.value;
}
};
/**
* @function VCard.parse
* @param {(string|array)} data
* @param {object} [opt]
* @param {VCard~filterCb} [opt.filter]
* @returns {array}
*/
VCard.parse = function(data, opt){
opt = opt || {};
var vCards = [], lines, vCard;
if(util.isString(data)){
lines = [];
data = data.replace(/^=/mg, ' ');
data.split(/(\r\n|\r|\n)(?=\S)/).forEach(function(line){
line = line.replace(/^item\d+\.|\r\n\s*|\r\s*|\n\s*/g, '');
if(line){
switch(line){
case 'BEGIN:VCARD':
lines = [];
break;
case 'END:VCARD':
vCard = new VCard(lines, opt);
if(opt.filter){
if(!opt.filter(vCard))
break;
}
vCards.push(vCard);
break;
default:
lines.push(line);
}
}
});
}else if(util.isArray(data)){
data.forEach(function(arr){
vCards.push(new VCard(arr[1]));
});
}
return vCards;
};
/**
* @function VCard.serialize
* @param {(VCard[]|array)} vCards
* @param {string} version
* @returns {string}
*/
/**
* @function VCard.serialize
* @param {(VCard[]|array)} vCards
* @param {object} [opt]
* @param {VCard~filterCb} [opt.filter]
* @returns {string}
*/
VCard.serialize = function(vCards, opt){
opt = opt || {};
var list = [];
vCards.forEach(function(vCard){
if(util.isArray(vCard)){
vCard = new VCard(vCard[1]);
}
if(opt.filter){
if(!opt.filter(vCard))
return;
}
list.push(vCard.toString(opt));
});
return list.join(VCard.EOL);
};
/**
* @function VCard.readFile
* @param {string} filename
* @param {object} [opt]
* @param {string} [opt.encoding=utf8]
* @param {VCard~readFileCb} callback
*/
/**
*
* @callback VCard~readFileCb
* @param {?Error} err
* @param {array} vCards
* @param {string} data
*/
VCard.readFile = function (filename, opt, callback){
opt = opt || {};
if(util.isFunction(opt)){
callback = opt;
opt = {};
}
fs.readFile(opt.filename = filename, {
encoding : opt.encoding || 'utf8'
}, function(err, data){
var vCards = null;
if(!err)
try{
vCards = VCard.parse(data, opt);
}catch(e){
err = e;
}
callback(err, vCards, data);
});
};
function find(arr, predicate){
if(util.isArray(arr))
for(var i=0,len=arr.length,o; i<len; i++){
o = arr[i];
if(predicate(o))
return o;
}
return undefined;
}
module.exports = VCard;
/**
* @callback VCard~debugCb
* @param {*} any
*/
/**
* @callback VCard~filterCb
* @param {(VCard|VCard.Item)} any
* @returns {boolean}
*/
/**
* @callback VCard~replaceCb
* @param {string} line
* @param {VCard.Item} item
* @returns {string}
*/