identifi-lib
Version:
Basic tools for reading and writing Identifi messages and identities.
339 lines (289 loc) • 12.9 kB
JavaScript
;
exports.__esModule = true;
var _identicon = require('identicon.js');
var _identicon2 = _interopRequireDefault(_identicon);
var _attribute = require('./attribute');
var _attribute2 = _interopRequireDefault(_attribute);
var _util = require('./util');
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* An Identifi identity profile. Usually you don't create them yourself, but get them
* from Index methods such as search().
*/
var Identity = function () {
/**
* @param {Object} gun node where the Identity data lives
*/
function Identity(gun, linkTo) {
_classCallCheck(this, Identity);
this.gun = gun;
this.linkTo = linkTo;
}
Identity.create = async function create(gun, data) {
if (!data.linkTo && !data.attrs) {
throw new Error('You must specify either data.linkTo or data.attrs');
}
if (data.linkTo && !data.attrs) {
var linkTo = new _attribute2.default(data.linkTo);
data.attrs = {};
if (!data.attrs.hasOwnProperty(linkTo.uri())) {
data.attrs[linkTo.uri()] = linkTo;
}
} else {
data.linkTo = Identity.getLinkTo(data.attrs);
}
await gun.put(data);
return new Identity(gun, data.linkTo);
};
Identity.getLinkTo = function getLinkTo(attrs) {
var mva = Identity.getMostVerifiedAttributes(attrs);
var keys = Object.keys(mva);
var linkTo = void 0;
for (var i = 0; i < keys.length; i++) {
if (keys[i] === 'keyID') {
linkTo = mva[keys[i]].attribute;
break;
} else if (_attribute2.default.isUniqueType(keys[i])) {
linkTo = mva[keys[i]].attribute;
}
}
return linkTo;
};
Identity.getMostVerifiedAttributes = function getMostVerifiedAttributes(attrs) {
var mostVerifiedAttributes = {};
Object.keys(attrs).forEach(function (k) {
var a = attrs[k];
var keyExists = Object.keys(mostVerifiedAttributes).indexOf(a.type) > -1;
a.conf = isNaN(a.conf) ? 1 : a.conf;
a.ref = isNaN(a.ref) ? 0 : a.ref;
if (a.conf * 2 > a.ref * 3 && (!keyExists || a.conf - a.ref > mostVerifiedAttributes[a.type].verificationScore)) {
mostVerifiedAttributes[a.type] = {
attribute: a,
verificationScore: a.conf - a.ref
};
if (a.verified) {
mostVerifiedAttributes[a.type].verified = true;
}
}
});
return mostVerifiedAttributes;
};
/**
* @param {string} attribute attribute type
* @returns {string} most verified value of the param type
*/
Identity.prototype.verified = async function verified(attribute) {
var attrs = await this.gun.get('attrs').then();
var mva = Identity.getMostVerifiedAttributes(attrs);
return mva.hasOwnProperty(attribute) ? mva[attribute].attribute.value : undefined;
};
/**
* @param {Object} ipfs (optional) an IPFS instance that is used to fetch images
* @returns {HTMLElement} profile card html element describing the identity
*/
Identity.prototype.profileCard = function profileCard(ipfs) {
var _this = this;
var card = document.createElement('div');
card.className = 'identifi-card';
var identicon = this.identicon(60, null, null, ipfs);
identicon.style.order = 1;
identicon.style.flexShrink = 0;
identicon.style.marginRight = '15px';
var details = document.createElement('div');
details.style.padding = '5px';
details.style.order = 2;
details.style.flexGrow = 1;
var linkEl = document.createElement('span');
var links = document.createElement('small');
card.appendChild(identicon);
card.appendChild(details);
details.appendChild(linkEl);
details.appendChild(links);
this.gun.on(async function (data) {
if (!data) {
return;
}
var attrs = await new Promise(function (resolve) {
_this.gun.get('attrs').load(function (r) {
return resolve(r);
});
});
var linkTo = await _this.gun.get('linkTo').then();
var link = 'https://identi.fi/#/identities/' + linkTo.type + '/' + linkTo.value;
var mva = Identity.getMostVerifiedAttributes(attrs);
linkEl.innerHTML = '<a href="' + link + '">' + (mva.type && mva.type.attribute.value || mva.nickname && mva.nickname.attribute.value || linkTo.type + ':' + linkTo.value) + '</a><br>';
linkEl.innerHTML += '<small>Received: <span class="identifi-pos">+' + (data.receivedPositive || 0) + '</span> / <span class="identifi-neg">-' + (data.receivedNegative || 0) + '</span></small><br>';
links.innerHTML = '';
Object.keys(attrs).forEach(function (k) {
var a = attrs[k];
if (a.link) {
links.innerHTML += a.type + ': <a href="' + a.link + '">' + a.value + '</a> ';
}
});
});
/*
const template = ```
<tr ng-repeat="result in ids.list" id="result{$index}" ng-hide="!result.linkTo" ui-sref="identities.show({ type: result.linkTo.type, value: result.linkTo.value })" class="search-result-row" ng-class="{active: result.active}">
<td class="gravatar-col"><identicon id="result" border="3" width="46" positive-score="result.pos" negative-score="result.neg"></identicon></td>
<td>
<span ng-if="result.distance == 0" class="label label-default pull-right">viewpoint</span>
<span ng-if="result.distance > 0" ng-bind="result.distance | ordinal" class="label label-default pull-right"></span>
<a ng-bind-html="result.name|highlight:query.term" ui-sref="identities.show({ type: result.linkTo.type, value: result.linkTo.value })"></a>
<small ng-if="!result.name" class="list-group-item-text">
<span ng-bind-html="result[0][0]|highlight:query.term"></span>
</small><br>
<small>
<span ng-if="result.nickname && result.name != result.nickname" ng-bind-html="result.nickname|highlight:query.term" class="mar-right10"></span>
<span ng-if="result.email" class="mar-right10">
<span class="glyphicon glyphicon-envelope"></span> <span ng-bind-html="result.email|highlight:query.term"></span>
</span>
<span ng-if="result.facebook" class="mar-right10">
<span class="fa fa-facebook"></span> <span ng-bind-html="result.facebook|highlight:query.term"></span>
</span>
<span ng-if="result.twitter" class="mar-right10">
<span class="fa fa-twitter"></span> <span ng-bind-html="result.twitter|highlight:query.term"></span>
</span>
<span ng-if="result.googlePlus" class="mar-right10">
<span class="fa fa-google-plus"></span> <span ng-bind-html="result.googlePlus|highlight:query.term"></span>
</span>
<span ng-if="result.bitcoin" class="mar-right10">
<span class="fa fa-bitcoin"></span> <span ng-bind-html="result.bitcoin|highlight:query.term"></span>
</span>
</small>
</td>
</tr>
```;*/
return card;
};
/**
* Appends a search widget to the given HTMLElement
* @param {HTMLElement} parentElement element where the search widget is added and event listener attached
* @param {Index} index index root to use for search
*/
Identity.appendSearchWidget = function appendSearchWidget(parentElement, index) {
var form = document.createElement('form');
var input = document.createElement('input');
input.type = 'text';
input.placeholder = 'Search';
input.id = 'identifiSearchInput';
form.innerHTML += '<div id="identifiSearchResults"></div>';
var searchResults = document.createElement('div');
parentElement.appendChild(form);
form.appendChild(input);
form.appendChild(searchResults);
input.addEventListener('keyup', async function () {
var r = await index.search(input.value);
searchResults.innerHTML = '';
r.sort(function (a, b) {
return a.trustDistance - b.trustDistance;
});
r.forEach(function (i) {
searchResults.appendChild(i.profileCard());
});
});
};
Identity._ordinal = function _ordinal(n) {
var s = ['th', 'st', 'nd', 'rd'];
var v = n % 100;
return n + (s[(v - 20) % 10] || s[v] || s[0]);
};
/**
* @param {number} width of the identicon
* @param {number} border identicon border (aura) width
* @param {boolean} showDistance whether to show web of trust distance ordinal
* @param {Object} ipfs (optional) an IPFS instance that is used to fetch images
* @returns {HTMLElement} identicon element that can be appended to DOM
*/
Identity.prototype.identicon = function identicon(width) {
var border = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 4;
var showDistance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var ipfs = arguments[3];
_util2.default.injectCss(); // some other way that is not called on each identicon generation?
var identicon = document.createElement('div');
identicon.className = 'identifi-identicon';
identicon.style.width = width + 'px';
identicon.style.height = width + 'px';
var pie = document.createElement('div');
pie.className = 'identifi-pie';
pie.style.width = width + 'px';
var img = document.createElement('img');
img.alt = '';
img.width = width;
img.height = width;
img.style.borderWidth = border + 'px';
var distance = void 0;
if (showDistance) {
distance = document.createElement('span');
distance.className = 'identifi-distance';
distance.style.fontSize = width > 50 ? width / 4 + 'px' : '10px';
identicon.appendChild(distance);
}
identicon.appendChild(pie);
identicon.appendChild(img);
function setPie(data) {
if (!data) {
return;
}
// Define colors etc
var bgColor = 'rgba(0,0,0,0.2)';
var bgImage = 'none';
var transform = '';
var boxShadow = '0px 0px 0px 0px #82FF84';
if (data.receivedPositive > data.receivedNegative * 20) {
boxShadow = '0px 0px ' + border * data.receivedPositive / 50 + 'px 0px #82FF84';
} else if (data.receivedPositive < data.receivedNegative * 3) {
boxShadow = '0px 0px ' + border * data.receivedNegative / 10 + 'px 0px #BF0400';
}
if (data.receivedPositive + data.receivedNegative > 0) {
if (data.receivedPositive > data.receivedNegative) {
transform = 'rotate(' + (-data.receivedPositive / (data.receivedPositive + data.receivedNegative) * 360 - 180) / 2 + 'deg)';
bgColor = '#A94442';
bgImage = 'linear-gradient(' + data.receivedPositive / (data.receivedPositive + data.receivedNegative) * 360 + 'deg, transparent 50%, #3C763D 50%), linear-gradient(0deg, #3C763D 50%, transparent 50%)';
} else {
transform = 'rotate(' + ((-data.receivedNegative / (data.receivedPositive + data.receivedNegative) * 360 - 180) / 2 + 180) + 'deg)';
bgColor = '#3C763D';
bgImage = 'linear-gradient(' + data.receivedNegative / (data.receivedPositive + data.receivedNegative) * 360 + 'deg, transparent 50%, #A94442 50%), linear-gradient(0deg, #A94442 50%, transparent 50%)';
}
}
pie.style.backgroundColor = bgColor;
pie.style.backgroundImage = bgImage;
pie.style.boxShadow = boxShadow;
pie.style.transform = transform;
pie.style.opacity = (data.receivedPositive + data.receivedNegative) / 10 * 0.5 + 0.35;
if (showDistance) {
distance.textContent = typeof data.trustDistance === 'number' ? Identity._ordinal(data.trustDistance) : '\u2715';
}
}
function setIdenticonImg(data) {
var hash = _util2.default.getHash(encodeURIComponent(data.type) + ':' + encodeURIComponent(data.value), 'hex');
var identiconImg = new _identicon2.default(hash, { width: width, format: 'svg' });
img.src = img.src || 'data:image/svg+xml;base64,' + identiconImg.toString();
}
if (this.linkTo) {
setIdenticonImg(this.linkTo);
} else {
this.gun.get('linkTo').on(setIdenticonImg);
}
this.gun.on(setPie);
if (ipfs) {
this.gun.get('attrs').open(function (attrs) {
var mva = Identity.getMostVerifiedAttributes(attrs);
if (mva.profilePhoto) {
var go = function go() {
ipfs.cat(mva.profilePhoto.attribute.value).then(function (file) {
var f = ipfs.types.Buffer.from(file).toString('base64');
img.src = 'data:image;base64,' + f;
});
};
ipfs.isOnline() ? go() : ipfs.on('ready', go);
}
});
}
return identicon;
};
return Identity;
}();
exports.default = Identity;
module.exports = exports['default'];