make vcard work again

* switch out problematic dependency
This commit is contained in:
Matthias Kretschmann 2018-05-07 10:48:34 +02:00
parent b53849fae1
commit e1787e39e6
Signed by: m
GPG Key ID: 606EEEF3C479A91F
7 changed files with 562 additions and 22 deletions

View File

@ -13,6 +13,7 @@
"deploy": "./deploy.sh"
},
"dependencies": {
"camel-case": "^3.0.0",
"file-saver": "^1.3.8",
"gatsby": "^1.9.259",
"gatsby-image": "^1.0.51",

View File

@ -4,8 +4,12 @@ import PropTypes from 'prop-types'
const SEO = ({ project, meta }) => {
const title = project.title ? project.title : meta.title
const description = project.description ? project.description : meta.description
const image = project.img ? project.img.childImageSharp.twitterImage.src : meta.img.childImageSharp.resize.src
const description = project.description
? project.description
: meta.description
const image = project.img
? project.img.childImageSharp.twitterImage.src
: meta.img.childImageSharp.resize.src
const url = project.slug ? `${meta.url}/${project.slug}` : meta.url
return (

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
// import vCard from 'vcf'
import FileSaver from 'file-saver'
import vCard from '../../lib/vcf/vcard'
import Social from '../molecules/Social'
import './Footer.scss'
@ -12,22 +12,20 @@ class Footer extends Component {
}
constructVcard() {
// const meta = this.props.meta
// const contact = new vCard()
// const photo = meta.avatarBase64
// contact.set('fn', meta.title)
// contact.set('title', meta.tagline)
// contact.set('email', meta.email)
// contact.set('url', meta.url, { type: 'Portfolio' })
// contact.add('url', meta.social.Blog, { type: 'Blog' })
// contact.set('nickname', 'kremalicious')
// contact.add('x-socialprofile', meta.social.Twitter, { type: 'twitter' })
// contact.add('x-socialprofile', meta.social.GitHub, { type: 'GitHub' })
// contact.set('photo', photo, { encoding: 'b', type: 'JPEG' })
// const vcard = contact.toString('3.0')
// this.downloadVcard(vcard, meta)
const meta = this.props.meta
const contact = new vCard()
const photo = meta.avatarBase64
contact.set('fn', meta.title)
contact.set('title', meta.tagline)
contact.set('email', meta.email)
contact.set('url', meta.url, { type: 'Portfolio' })
contact.add('url', meta.social.Blog, { type: 'Blog' })
contact.set('nickname', 'kremalicious')
contact.add('x-socialprofile', meta.social.Twitter, { type: 'twitter' })
contact.add('x-socialprofile', meta.social.GitHub, { type: 'GitHub' })
contact.set('photo', photo, { encoding: 'b', type: 'JPEG' })
const vcard = contact.toString('3.0')
this.downloadVcard(vcard, meta)
}
downloadVcard(vcard, meta) {
@ -48,9 +46,9 @@ class Footer extends Component {
<footer className="footer">
<Social meta={meta} minimal />
<p className="footer__actions">
{/* <a href="#" onClick={this.handleAddressbookClick}>
<a href="#" onClick={this.handleAddressbookClick}>
Add to addressbook
</a> */}
</a>
<a href={meta.gpg}>PGP/GPG key</a>
</p>
<p>

View File

@ -0,0 +1,71 @@
var camelCase = require('camel-case') // that's the only change
var Property = require('./property')
function set(object, key, value) {
if (Array.isArray(object[key])) {
object[key].push(value)
} else if (object[key] != null) {
object[key] = [object[key], value]
} else {
object[key] = value
}
}
function createParams(params, param) {
var parts = param.split('=')
var k = camelCase(parts[0])
var value = parts[1]
if (value == null || value === '') {
value = parts[0]
k = 'type'
}
if (k === 'type') {
value
.toLowerCase()
.split(',')
.forEach(function(value) {
set(params, k, value)
})
return params
}
set(params, k, value)
return params
}
function parseLines(lines) {
var data = {}
// NOTE: Line format:
// PROPERTY[;PARAMETER[=VALUE]]:Attribute[;Attribute]
var line = null
var pattern = /^([^;:]+)((?:;(?:[^;:]+))*)(?:\:(.+))?$/i // eslint-disable-line no-useless-escape
var len = lines.length - 1
for (var i = 1; i < len; i++) {
line = lines[i]
var match = pattern.exec(line)
if (!match) continue
var name = match[1].split('.')
var property = name.pop()
var group = name.pop()
var value = match[3]
var params = match[2] ? match[2].replace(/^;|;$/g, '').split(';') : []
var propParams = params.reduce(createParams, group ? { group: group } : {})
var propName = camelCase(property)
var propVal = new Property(propName, value, propParams)
set(data, propName, propVal)
}
return data
}
module.exports = parseLines

145
src/lib/vcf/property.js Normal file
View File

@ -0,0 +1,145 @@
/**
* vCard Property
* @constructor
* @memberOf vCard
* @param {String} field
* @param {String} value
* @param {Object} params
* @return {Property}
*/
function Property(field, value, params) {
if (!(this instanceof Property)) return new Property(value)
if (params != null) Object.assign(this, params)
this._field = field
this._data = value
Object.defineProperty(this, '_field', { enumerable: false })
Object.defineProperty(this, '_data', { enumerable: false })
}
/**
* Constructs a vCard.Property from jCard data
* @param {Array} data
* @return {Property}
*/
Property.fromJSON = function(data) {
var field = data[0]
var params = data[1]
if (!/text/i.test(data[2])) params.value = data[2]
var value = Array.isArray(data[3]) ? data[3].join(';') : data[3]
return new Property(field, value, params)
}
/**
* Turn a string into capitalized dash-case
* @internal used by `Property#toString()`
* @param {String} value
* @return {String}
* @ignore
*/
function capitalDashCase(value) {
return value.replace(/([A-Z])/g, '-$1').toUpperCase()
}
/**
* Property prototype
* @type {Object}
*/
Property.prototype = {
constructor: Property,
/**
* Check whether the property is of a given type
* @param {String} type
* @return {Boolean}
*/
is: function(type) {
type = (type + '').toLowerCase()
return Array.isArray(this.type)
? this.type.indexOf(type)
: this.type === type
},
/**
* Check whether the property is empty
* @return {Boolean}
*/
isEmpty: function() {
return this._data == null && Object.keys(this).length === 0
},
/**
* Clone the property
* @return {Property}
*/
clone: function() {
return new Property(this._field, this._data, this)
},
/**
* Format the property as vcf with given version
* @param {String} version
* @return {String}
*/
toString: function(version) { // eslint-disable-line no-unused-vars
var propName =
(this.group ? this.group + '.' : '') + capitalDashCase(this._field)
var keys = Object.keys(this)
var params = []
for (var i = 0; i < keys.length; i++) {
if (keys[i] === 'group') continue
params.push(capitalDashCase(keys[i]) + '=' + this[keys[i]])
}
return (
propName +
(params.length ? ';' + params.join(';') : params) +
':' +
(Array.isArray(this._data) ? this._data.join(';') : this._data)
)
},
/**
* Get the property's value
* @return {String}
*/
valueOf: function() {
return this._data
},
/**
* Format the property as jCard data
* @return {Array}
*/
toJSON: function() {
var params = Object.assign({}, this)
if (params.value === 'text') {
params.value = void 0
delete params.value
}
var data = [this._field, params, this.value || 'text']
switch (this._field) {
default:
data.push(this._data)
break
case 'adr':
case 'n':
data.push(this._data.split(';'))
}
return data
},
}
// Exports
module.exports = Property

320
src/lib/vcf/vcard.js Normal file
View File

@ -0,0 +1,320 @@
/**
* vCard
* @constructor
* @return {vCard}
*/
function vCard() {
if (!(this instanceof vCard)) return new vCard()
/** @type {String} Version number */
this.version = vCard.versions[vCard.versions.length - 1]
/** @type {Object} Card data */
this.data = {}
}
/**
* vCard MIME type
* @type {String}
*/
vCard.mimeType = 'text/vcard'
/**
* vCard file extension
* @type {String}
*/
vCard.extension = '.vcf'
/**
* vCard versions
* @type {Array}
*/
vCard.versions = ['2.1', '3.0', '4.0']
/**
* Folds a long line according to the RFC 5322.
* @see http://tools.ietf.org/html/rfc5322#section-2.1.1
* @param {String} input
* @param {Number} maxLength
* @param {Boolean} hardWrap
* @return {String}
*/
vCard.foldLine = require('foldline')
/**
* Normalizes input (cast to string, line folding, whitespace)
* @param {String} input
* @return {String}
*/
vCard.normalize = function(input) {
return (
(input + '')
// Trim whitespace
.replace(/^[\s\r\n]+|[\s\r\n]+$/g, '')
// Trim blank lines
.replace(/(\r?\n)\s*(\r?\n)|$/g, '$1')
// Unfold folded lines
.replace(/\r?\n[\x20\x09]+/g, '') // eslint-disable-line no-control-regex
)
}
/**
* Check whether a given version is supported
* @param {String} version
* @return {Boolean}
*/
vCard.isSupported = function(version) {
return /^\d\.\d$/.test(version) && vCard.versions.indexOf(version) !== -1
}
/**
* Parses a string or buffer into a vCard object
* @param {String|Buffer} value
* @return {Array<vCard>}
*/
vCard.parse = function(value) {
var objects = (value + '').split(/(?=BEGIN\:VCARD)/gi) // eslint-disable-line no-useless-escape
var cards = []
for (var i = 0; i < objects.length; i++) {
cards.push(new vCard().parse(objects[i]))
}
return cards
}
/**
* Parse an array of vcf formatted lines
* @internal used by `vCard#parse()`
* @type {Function}
*/
vCard.parseLines = require('./parse-lines')
/**
* Constructs a vCard from jCard data
* @param {Array} jcard
* @return {vCard}
*/
vCard.fromJSON = function(jcard) {
jcard = typeof jcard === 'string' ? JSON.parse(jcard) : jcard
if (jcard == null || !Array.isArray(jcard)) return new vCard()
if (!/vcard/i.test(jcard[0])) throw new Error('Object not in jCard format')
var card = new vCard()
jcard[1].forEach(function(prop) {
card.addProperty(vCard.Property.fromJSON(prop))
})
return card
}
/**
* Format a card object according to the given version
* @param {vCard} card
* @param {String} version
* @return {String}
*/
vCard.format = function(card, version) {
version = version || card.version || vCard.versions[vCard.versions.length - 1]
if (!vCard.isSupported(version))
throw new Error('Unsupported vCard version "' + version + '"')
var vcf = []
vcf.push('BEGIN:VCARD')
vcf.push('VERSION:' + version)
var props = Object.keys(card.data)
var prop = ''
for (var i = 0; i < props.length; i++) {
if (props[i] === 'version') continue
prop = card.data[props[i]]
if (Array.isArray(prop)) {
for (var k = 0; k < prop.length; k++) {
if (prop[k].isEmpty()) continue
vcf.push(vCard.foldLine(prop[k].toString(version), 75))
}
} else if (!prop.isEmpty()) {
vcf.push(vCard.foldLine(prop.toString(version), 75))
}
}
vcf.push('END:VCARD')
return vcf.join('\n')
}
// vCard Property constructor
vCard.Property = require('./property')
/**
* vCard prototype
* @type {Object}
*/
vCard.prototype = {
constructor: vCard,
/**
* Get a vCard property
* @param {String} key
* @return {Object|Array}
*/
get: function(key) {
if (this.data[key] == null) {
return this.data[key]
}
if (Array.isArray(this.data[key])) {
return this.data[key].map(function(prop) {
return prop.clone()
})
} else {
return this.data[key].clone()
}
},
/**
* Set a vCard property
* @param {String} key
* @param {String} value
* @param {Object} params
*/
set: function(key, value, params) {
return this.setProperty(new vCard.Property(key, value, params))
},
/**
* Add a vCard property
* @param {String} key
* @param {String} value
* @param {Object} params
*/
add: function(key, value, params) {
var prop = new vCard.Property(key, value, params)
this.addProperty(prop)
return this
},
/**
* Set a vCard property from an already
* constructed vCard.Property
* @param {vCard.Property} prop
*/
setProperty: function(prop) {
this.data[prop._field] = prop
return this
},
/**
* Add a vCard property from an already
* constructed vCard.Property
* @param {vCard.Property} prop
*/
addProperty: function(prop) {
var key = prop._field
if (Array.isArray(this.data[key])) {
this.data[key].push(prop)
} else if (this.data[key] != null) {
this.data[key] = [this.data[key], prop]
} else {
this.data[key] = prop
}
return this
},
/**
* Parse a vcf formatted vCard
* @param {String} value
* @return {vCard}
*/
parse: function(value) {
// Normalize & split
var lines = vCard.normalize(value).split(/\r?\n/g)
// Keep begin and end markers
// for eventual error messages
var begin = lines[0]
var version = lines[1]
var end = lines[lines.length - 1]
if (!/BEGIN:VCARD/i.test(begin))
throw new SyntaxError(
'Invalid vCard: Expected "BEGIN:VCARD" but found "' + begin + '"'
)
if (!/END:VCARD/i.test(end))
throw new SyntaxError(
'Invalid vCard: Expected "END:VCARD" but found "' + end + '"'
)
// TODO: For version 2.1, the VERSION can be anywhere between BEGIN & END
if (!/VERSION:\d\.\d/i.test(version))
throw new SyntaxError(
'Invalid vCard: Expected "VERSION:\\d.\\d" but found "' + version + '"'
)
this.version = version.substring(8, 11)
if (!vCard.isSupported(this.version))
throw new Error('Unsupported version "' + this.version + '"')
this.data = vCard.parseLines(lines)
return this
},
/**
* Format the vCard as vcf with given version
* @param {String} version
* @param {String} charset
* @return {String}
*/
toString: function(version, charset) { // eslint-disable-line no-unused-vars
version = version || this.version
return vCard.format(this, version)
},
/**
* Format the card as jCard
* @param {String} version='4.0'
* @return {Array} jCard
*/
toJCard: function(version) {
version = version || '4.0'
var keys = Object.keys(this.data)
var data = [['version', {}, 'text', version]]
var prop = null
for (var i = 0; i < keys.length; i++) {
if (keys[i] === 'version') continue
prop = this.data[keys[i]]
if (Array.isArray(prop)) {
for (var k = 0; k < prop.length; k++) {
data.push(prop[k].toJSON())
}
} else {
data.push(prop.toJSON())
}
}
return ['vcard', data]
},
/**
* Format the card as jCard
* @return {Array} jCard
*/
toJSON: function() {
return this.toJCard(this.version)
},
}
// Exports
module.exports = vCard

View File

@ -44,7 +44,8 @@ class NotFound extends Component {
<h1>Shenanigans, page not found.</h1>
<p>
You might want to check the url, or{' '}
<Link to={'/'}>go back to the homepage</Link>. Or just check out some fail gifs, entirely your choice.
<Link to={'/'}>go back to the homepage</Link>. Or just check out some
fail gifs, entirely your choice.
</p>
<video className="gif" src={this.state.gif} autoPlay loop />