Skip to content

Instantly share code, notes, and snippets.

@dun4n
Last active November 4, 2024 15:26
Show Gist options
  • Select an option

  • Save dun4n/9353031 to your computer and use it in GitHub Desktop.

Select an option

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("foor@bar.com", 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, "john.doe@work.com", vCard.Type.WORK)
johnDoe.add(vCard.Entry.EMAIL, "john.doe@home.com", 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": "john.smith@work.com", "type": "work" },
{ "value": "john.smith@home.com", "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)
@brunolaranjeira

Copy link
Copy Markdown

Thanks for sharing brother.

@devamatkotiya

Copy link
Copy Markdown

how i attach image in vcard using your code??

@schevin-kick

schevin-kick commented Sep 8, 2021

Copy link
Copy Markdown

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
Copy Markdown

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

nainzen commented Jan 11, 2022

Copy link
Copy Markdown

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

@vijaysolankiii

Copy link
Copy Markdown

any update
how to attach the image in card

@adymuchlis

Copy link
Copy Markdown

card can't download in chrome iphone

@MHMss

MHMss commented Sep 24, 2022

Copy link
Copy Markdown

hi
any way for UTF-8 ?

@rajavinoth-pfx

Copy link
Copy Markdown

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:john.smith@work.com
EMAIL;TYPE=HOME:john.smith@home.com
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