Skip to content

Instantly share code, notes, and snippets.

@dun4n
Last active November 4, 2024 15:26
Show Gist options
  • Save dun4n/9353031 to your computer and use it in GitHub Desktop.
Save dun4n/9353031 to your computer and use it in GitHub Desktop.
#JavaScript vcard generator example
<!doctype html>
<html>
<head>
<script type="text/javascript" src="vcard2.js"></script>
</head>
<body>
<script type="text/javascript">
// With helper methods
var fooBar = vCard.create(vCard.Version.FOUR)
fooBar.addFormattedname("Mr Foo Bar")
fooBar.addEmail("[email protected]", vCard.Type.HOME)
fooBar.addAddress("street", "code", "city", "country", vCard.Type.HOME)
var link = vCard.export(fooBar, "Foo Bar", false) // use parameter true to force download
document.body.appendChild(link)
</script>
<br/>
<script type="text/javascript">
// Without helper methods
var johnDoe = vCard.create(vCard.Version.FOUR)
johnDoe.add(vCard.Entry.NAME, "DOE;John;;")
johnDoe.add(vCard.Entry.FORMATTEDNAME, "John Doe")
johnDoe.add(vCard.Entry.NICKNAME, "jd")
johnDoe.add(vCard.Entry.TITLE, "Missing man")
johnDoe.add(vCard.Entry.PHONE, "555-555-555", vCard.Type.CELL)
johnDoe.add(vCard.Entry.EMAIL, "[email protected]", vCard.Type.WORK)
johnDoe.add(vCard.Entry.EMAIL, "[email protected]", vCard.Type.HOME)
johnDoe.add(vCard.Entry.ORGANIZATION, "JohnDoe Corp.")
johnDoe.add(vCard.Entry.ADDRESS, ";;street;city;state;zip code;country", vCard.Type.HOME)
johnDoe.add(vCard.Entry.URL, "http://john.doe")
var link = vCard.export(johnDoe, "John Doe", false) // use parameter true to force download
document.body.appendChild(link)
</script>
<br/>
<script type="text/javascript">
// From JSON
var johnSmith = {
"version": "4.0",
"n": "SMITH;John;;",
"fn": "John Smith",
"nickname":"js",
"title": "Missing man too",
"tel": [
{"value": "555-555-555", "type": "cell"}
],
"email": [
{ "value": "[email protected]", "type": "work" },
{ "value": "[email protected]", "type": "home" }
]
}
var link = vCard.export(johnSmith, "John Smith", false) // use parameter true to force download
document.body.appendChild(link)
</script>
</body>
</html>
(function(context) {
var version = {
"TWO": "2.1",
"THREE": "3.0",
"FOUR": "4.0"
}
var vCard = {
Version: version,
Entry: {
"ADDRESS": {"version": [version.TWO, version.THREE, version.FOUR], "key": "ADR", "format": ";;{0};{2};{4};{1};{3}", "@comment": "usage: addAdr(street, code, city, country, state)"},
"AGENT": {"version": [version.TWO, version.THREE], "key": "AGENT"},
"ANNIVERSARY": {"version": [version.FOUR], "key": "ANNIVERSARY"},
"BIRTHDAY": {"version": [version.TWO, version.THREE, version.FOUR], "key": "BDAY"},
"CALENDARADDURI": {"version": [version.FOUR], "key": "CALADRURI"},
"CALENDARURI": {"version": [version.FOUR], "key": "CALURI"},
"CATEGORIES": {"version": [version.TWO, version.THREE, version.FOUR], "key": "CATEGORIES"},
"CLASS": {"version": [version.THREE], "key": "CLASS"},
"CLIENTPIDMAP": {"version": [version.FOUR], "key": "CLIENTPIDMAP"},
"EMAIL": {"version": [version.TWO, version.THREE, version.FOUR], "key": "EMAIL"},
"FBURL": {"version": [version.FOUR], "key": "FBURL"},
"FORMATTEDNAME": {"version": [version.TWO, version.THREE, version.FOUR], "key": "FN"},
"GENDER": {"version": [version.FOUR], "key": "GENDER"},
"GEO": {"version": [version.TWO, version.THREE, version.FOUR], "key": "GEO"}, // FIXME two differents formats
"IMPP": {"version": [version.THREE, version.FOUR], "key": "IMPP"},
// TODO: KEY
"KIND": {"version": [version.FOUR], "key": "KIND"},
"LABEL": {"version": [version.TWO, version.THREE], "key": "LABEL"},
// TODO: LOGO
"MAILER": {"version": [version.TWO, version.THREE], "key": "MAILER"},
"MEMBER": {"version": [version.FOUR], "key": "MEMBER"},
"NAME": {"version": [version.TWO, version.THREE, version.FOUR], "key": "N", "format": "{1};{0};;{2}", "@comment": "usage: addName(firstname, lastname, title)"},
"NICKNAME": {"version": [version.THREE, version.FOUR], "key": "NICKNAME"},
"NOTE": {"version": [version.TWO, version.THREE, version.FOUR], "key": "NOTE"},
"ORGANIZATION": {"version": [version.TWO, version.THREE, version.FOUR], "key": "ORG"},
// TODO: PHOTO
"PRODID": {"version": [version.THREE, version.FOUR], "key": "PRODID"},
"PROFILE": {"version": [version.TWO, version.THREE], "key": "PROFILE"},
"RELATED": {"version": [version.FOUR], "key": "RELATED"},
"REVISION": {"version": [version.TWO, version.THREE, version.FOUR], "key": "REV"},
"ROLE": {"version": [version.TWO, version.THREE, version.FOUR], "key": "ROLE"},
"SORTSTRING": {"version": [version.TWO, version.THREE, version.FOUR], "key": "SORT-STRING"},
// TODO: SOUND
"SOURCE": {"version": [version.TWO, version.THREE, version.FOUR], "key": "SOURCE"},
"PHONE": {"version": [version.TWO, version.THREE, version.FOUR], "key": "TEL"},
"TITLE": {"version": [version.TWO, version.THREE, version.FOUR], "key": "TITLE"},
"TIMEZONE": {"version": [version.TWO, version.THREE, version.FOUR], "key": "TZ"}, // FIXME: two differents formats
"UID": {"version": [version.TWO, version.THREE, version.FOUR], "key": "UID"},
"URL": {"version": [version.TWO, version.THREE, version.FOUR], "key": "URL"},
"XML": {"version": [version.FOUR], "key": "XML"}
},
Type: {
"HOME": "HOME",
"WORK": "WORK",
"CELL": "CELL",
"MAIN": "MAIN",
"OTHER":"OTHER"
},
create: function(version) {
for(var key in this.Version) {
if(this.Version[key] === version)
return new Card(version)
}
throw new Error("Unknown vCard version")
},
dump: function(card) {
var str = "BEGIN:VCARD\n"
for(var key in card) {
var entry = card[key]
if(typeof entry === "function")
continue
if(Object.prototype.toString.call(entry) === "[object Array]") {
for(var i = 0, l = entry.length; i < l; i++) {
var e = entry[i]
str += key.toUpperCase() + (e.type ? ";TYPE=" + e.type.toUpperCase() + ":" : ":") + e.value + "\n"
}
} else if(typeof entry === "object") {
str += key.toUpperCase() + (entry.type ? ";TYPE=" + entry.type.toUpperCase() + ":" : ":") + entry.value + "\n"
} else {
str += key.toUpperCase() + ":" + entry + "\n"
}
}
str += "END:VCARD"
return str
},
export: function(card, name, force) {
var a = document.createElement('a')
a.download = name + ".vcf"
a.textContent = name
if(Blob) {
var blob = new Blob([this.dump(card)], {"type": "text/vcard"})
a.href = URL.createObjectURL(blob)
} else {
a.href = "data:text/vcard;base64," + this.btoa(this.dump(card))
}
force && a.click()
return a
},
btoa: function(str) {
str = unescape(encodeURIComponent(str))
if(!btoa) {
var b64c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var i, res = "", length = str.length;
for (i = 0; i < length - 2; i += 3) {
res += b64c[str.charCodeAt(i) >>> 2];
res += b64c[((str.charCodeAt(i) & 3) << 4) | (str.charCodeAt(i + 1) >>> 4)];
res += b64c[((str.charCodeAt(i + 1) & 15) << 2) | (str.charCodeAt(i + 2) >>> 6)];
res += b64c[str.charCodeAt(i + 2) & 63];
}
if (length % 3 === 2) {
res += b64c[str.charCodeAt(i) >>> 2];
res += b64c[((str.charCodeAt(i) & 3) << 4) | (str.charCodeAt(i + 1) >>> 4)];
res += b64c[((str.charCodeAt(i + 1) & 15) << 2)];
res += "=";
} else if (length % 3 === 1) {
res += b64c[str.charCodeAt(i) >>> 2];
res += b64c[((str.charCodeAt(i) & 3) << 4)];
res += "==";
}
return res;
} else {
return btoa(str)
}
}
}
var Card = function(version) {
this.version = version
for(var key in vCard.Entry) {
var property = vCard.Entry[key]
if(!property.version || property.version.indexOf(version) < 0)
continue
var fn = "add" + key[0].toUpperCase() + key.slice(1).toLowerCase()
Card.prototype[fn] = (function(key, format) {
return (function() {
var args = Array.prototype.slice.call(arguments)
var lastArg = args.length > 0 ? args[args.length - 1] : undefined
var model = vCard.Type.hasOwnProperty(lastArg) ? args.slice(0, args.length - 1) : args
var value = format && format.replace(/\{([0-9]*)\}/g, function(match, parameter) {
return model[parseInt(parameter)] || ''
}) || model[0]
this.add(key, value, vCard.Type.hasOwnProperty(lastArg) && lastArg)
})
})(property.key, property.format)
}
this.add = function(entry, value, type) {
var key = (typeof entry === "object" && entry.key) ? entry.key : entry
!this[key] && (this[key] = [])
var e = {"value": value}
type && (e.type = type)
this[key].push(e)
}
}
context.vCard = vCard
})(this)
@Ayyub-Kolsawala
Copy link

Thanks for this. 👍

@Tmjohnst
Copy link

This works well. The helper methods are cleverly created in the Card object.

@brunolaranjeira
Copy link

Thanks for sharing brother.

@devamatkotiya
Copy link

how i attach image in vcard using your code??

@schevin-kick
Copy link

schevin-kick commented Sep 8, 2021

Thanks for the code! Is it possible to create custom labels, for instance for the Work phone currently it just generates "phone:", how would i make it generate "office:" or "mobile:"?

Yes. I could achieve this by using version 3.0 instead of 4.0. I could then construct it like so

tel: [
        { value: '555-555-555', type: 'Group office' },
        { value: '111-111-2222', type: 'General Office' },
        { value: '111-223-2222', type: 'Another department' }
    ],

@harmanjotsingh1997
Copy link

I want to add new line in NOTE section of vcard. i tried doing it with \n and ; . but none of them is working

@nainzen
Copy link

nainzen commented Jan 11, 2022

Thanks for the code is very useful, could you guide me in how I can do to attach images?

@vijaysolankiii
Copy link

any update
how to attach the image in card

@adymuchlis
Copy link

card can't download in chrome iphone

@MHMss
Copy link

MHMss commented Sep 24, 2022

hi
any way for UTF-8 ?

@rajavinoth-pfx
Copy link

Check this "https://www.geeksforgeeks.org/how-to-convert-image-into-base64-string-using-javascript/" to convert the image to base64 string then add it to vcf card with PHOTO;TYPE=PNG;ENCODING=b:
example
BEGIN:VCARD
VERSION:4.0
N:SMITH;John;;
FN:John Smith
NICKNAME:js
TITLE:Missing man too
TEL;TYPE=CELL:555-555-555
EMAIL;TYPE=WORK:[email protected]
EMAIL;TYPE=HOME:[email protected]
PHOTO;TYPE=PNG;ENCODING=b:base64 string
END:VCARD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment