// Minidenticons MIT Credit to https://github.com/laurentpayot/minidenticons
// density of 4 for the lowest probability of collision
const SQUARE_DENSITY = 4;
// 18 different colors only for easy distinction
const COLORS_NB = 18;
const DEFAULT_SATURATION = 50;
const DEFAULT_LIGHTNESS = 50;
// 32 bit FNV-1a hash parameters
const FNV_PRIME = 16777619;
const OFFSET_BASIS = 2166136261;
// based on the FNV-1a hash algorithm, modified for *signed* 32 bit integers http://www.isthe.com/chongo/tech/comp/fnv/index.html
/**
* @param str
*/
function simpleHash(str) {
return (
str
.split('')
// >>> 0 for 32 bit unsigned integer conversion https://2ality.com/2012/02/js-integers.html
.reduce(
(hash, char) => ((hash ^ char.charCodeAt(0)) >>> 0) * FNV_PRIME,
OFFSET_BASIS
)
);
}
/**
* @param username
* @param saturation
* @param lightness
*/
export function identicon(
username,
saturation = DEFAULT_SATURATION,
lightness = DEFAULT_LIGHTNESS
) {
const hash = simpleHash(username);
// dividing hash by FNV_PRIME to get last XOR result for better color randomness (will be an integer except for empty string hash)
const hue = ((hash / FNV_PRIME) % COLORS_NB) * (360 / COLORS_NB);
const rects = [...Array(username ? 25 : 0).keys()]
// 2 + ((3 * 5 - 1) - modulo) to concentrate squares at the center
.map((i) =>
hash % (16 - (i % 15)) < SQUARE_DENSITY
? `<rect x="${i > 14 ? 7 - ~~(i / 5) : ~~(i / 5)}" y="${
i % 5
}" width="1" height="1"/>`
: ''
)
.join('');
// xmlns attribute added in case of SVG file generation https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg#sect1
return `<svg viewBox="-1.5 -1.5 8 8" xmlns="http://www.w3.org/2000/svg" fill="hsl(${hue} ${saturation}% ${lightness}%)">${rects}</svg>`;
}
class IdenticonOrImageElement extends HTMLElement {
connectedCallback() {
this.identiconSvg();
}
attributeChangedCallback() {
this.identiconSvg();
}
static get observedAttributes() {
return ['username', 'saturation', 'lightness'];
}
identiconSvg() {
this.innerHTML = identicon(
this.getAttribute('username') || '',
this.getAttribute('saturation') || DEFAULT_SATURATION,
this.getAttribute('lightness') || DEFAULT_LIGHTNESS
);
}
}
customElements.define('identicon-or-image', IdenticonOrImageElement);
export { IdenticonOrImageElement };