diff --git a/onionr/static-data/www/shared/base32.js b/onionr/static-data/www/shared/base32.js new file mode 100644 index 00000000..29c5562b --- /dev/null +++ b/onionr/static-data/www/shared/base32.js @@ -0,0 +1,451 @@ +/* + * [hi-base32]{@link https://github.com/emn178/hi-base32} + * + * @version 0.5.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2015-2018 + * @license MIT + */ +/*jslint bitwise: true */ +(function () { + 'use strict'; + + var root = typeof window === 'object' ? window : {}; + var NODE_JS = !root.HI_BASE32_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; + if (NODE_JS) { + root = global; + } + var COMMON_JS = !root.HI_BASE32_NO_COMMON_JS && typeof module === 'object' && module.exports; + var AMD = typeof define === 'function' && define.amd; + var BASE32_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.split(''); + var BASE32_DECODE_CHAR = { + 'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, + 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, + 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, + 'Z': 25, '2': 26, '3': 27, '4': 28, '5': 29, '6': 30, '7': 31 + }; + + var blocks = [0, 0, 0, 0, 0, 0, 0, 0]; + + var throwInvalidUtf8 = function (position, partial) { + if (partial.length > 10) { + partial = '...' + partial.substr(-10); + } + var err = new Error('Decoded data is not valid UTF-8.' + + ' Maybe try base32.decode.asBytes()?' + + ' Partial data after reading ' + position + ' bytes: ' + partial + ' <-'); + err.position = position; + throw err; + }; + + var toUtf8String = function (bytes) { + var str = '', length = bytes.length, i = 0, followingChars = 0, b, c; + while (i < length) { + b = bytes[i++]; + if (b <= 0x7F) { + str += String.fromCharCode(b); + continue; + } else if (b > 0xBF && b <= 0xDF) { + c = b & 0x1F; + followingChars = 1; + } else if (b <= 0xEF) { + c = b & 0x0F; + followingChars = 2; + } else if (b <= 0xF7) { + c = b & 0x07; + followingChars = 3; + } else { + throwInvalidUtf8(i, str); + } + + for (var j = 0; j < followingChars; ++j) { + b = bytes[i++]; + if (b < 0x80 || b > 0xBF) { + throwInvalidUtf8(i, str); + } + c <<= 6; + c += b & 0x3F; + } + if (c >= 0xD800 && c <= 0xDFFF) { + throwInvalidUtf8(i, str); + } + if (c > 0x10FFFF) { + throwInvalidUtf8(i, str); + } + + if (c <= 0xFFFF) { + str += String.fromCharCode(c); + } else { + c -= 0x10000; + str += String.fromCharCode((c >> 10) + 0xD800); + str += String.fromCharCode((c & 0x3FF) + 0xDC00); + } + } + return str; + }; + + var decodeAsBytes = function (base32Str) { + if (!/^[A-Z2-7=]+$/.test(base32Str)) { + throw new Error('Invalid base32 characters'); + } + base32Str = base32Str.replace(/=/g, ''); + var v1, v2, v3, v4, v5, v6, v7, v8, bytes = [], index = 0, length = base32Str.length; + + // 4 char to 3 bytes + for (var i = 0, count = length >> 3 << 3; i < count;) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255; + bytes[index++] = (v4 << 4 | v5 >>> 1) & 255; + bytes[index++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255; + bytes[index++] = (v7 << 5 | v8) & 255; + } + + // remain bytes + var remain = length - count; + if (remain === 2) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + } else if (remain === 4) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255; + } else if (remain === 5) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255; + bytes[index++] = (v4 << 4 | v5 >>> 1) & 255; + } else if (remain === 7) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + bytes[index++] = (v1 << 3 | v2 >>> 2) & 255; + bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255; + bytes[index++] = (v4 << 4 | v5 >>> 1) & 255; + bytes[index++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255; + } + return bytes; + }; + + var encodeAscii = function (str) { + var v1, v2, v3, v4, v5, base32Str = '', length = str.length; + for (var i = 0, count = parseInt(length / 5) * 5; i < count;) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i++); + v4 = str.charCodeAt(i++); + v5 = str.charCodeAt(i++); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } + + // remain char + var remain = length - count; + if (remain === 1) { + v1 = str.charCodeAt(i); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + '======'; + } else if (remain === 2) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + '===='; + } else if (remain === 3) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + '==='; + } else if (remain === 4) { + v1 = str.charCodeAt(i++); + v2 = str.charCodeAt(i++); + v3 = str.charCodeAt(i++); + v4 = str.charCodeAt(i); + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + '='; + } + return base32Str; + }; + + var encodeUtf8 = function (str) { + var v1, v2, v3, v4, v5, code, end = false, base32Str = '', + index = 0, i, start = 0, bytes = 0, length = str.length; + do { + blocks[0] = blocks[5]; + blocks[1] = blocks[6]; + blocks[2] = blocks[7]; + for (i = start; index < length && i < 5; ++index) { + code = str.charCodeAt(index); + if (code < 0x80) { + blocks[i++] = code; + } else if (code < 0x800) { + blocks[i++] = 0xc0 | (code >> 6); + blocks[i++] = 0x80 | (code & 0x3f); + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i++] = 0xe0 | (code >> 12); + blocks[i++] = 0x80 | ((code >> 6) & 0x3f); + blocks[i++] = 0x80 | (code & 0x3f); + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(++index) & 0x3ff)); + blocks[i++] = 0xf0 | (code >> 18); + blocks[i++] = 0x80 | ((code >> 12) & 0x3f); + blocks[i++] = 0x80 | ((code >> 6) & 0x3f); + blocks[i++] = 0x80 | (code & 0x3f); + } + } + bytes += i - start; + start = i - 5; + if (index === length) { + ++index; + } + if (index > length && i < 6) { + end = true; + } + v1 = blocks[0]; + if (i > 4) { + v2 = blocks[1]; + v3 = blocks[2]; + v4 = blocks[3]; + v5 = blocks[4]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } else if (i === 1) { + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + '======'; + } else if (i === 2) { + v2 = blocks[1]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + '===='; + } else if (i === 3) { + v2 = blocks[1]; + v3 = blocks[2]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + '==='; + } else { + v2 = blocks[1]; + v3 = blocks[2]; + v4 = blocks[3]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + '='; + } + } while (!end); + return base32Str; + }; + + var encodeBytes = function (bytes) { + var v1, v2, v3, v4, v5, base32Str = '', length = bytes.length; + for (var i = 0, count = parseInt(length / 5) * 5; i < count;) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i++]; + v4 = bytes[i++]; + v5 = bytes[i++]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] + + BASE32_ENCODE_CHAR[v5 & 31]; + } + + // remain char + var remain = length - count; + if (remain === 1) { + v1 = bytes[i]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2) & 31] + + '======'; + } else if (remain === 2) { + v1 = bytes[i++]; + v2 = bytes[i]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4) & 31] + + '===='; + } else if (remain === 3) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1) & 31] + + '==='; + } else if (remain === 4) { + v1 = bytes[i++]; + v2 = bytes[i++]; + v3 = bytes[i++]; + v4 = bytes[i]; + base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] + + BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] + + BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] + + BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] + + BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] + + BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] + + BASE32_ENCODE_CHAR[(v4 << 3) & 31] + + '='; + } + return base32Str; + }; + + var encode = function (input, asciiOnly) { + var notString = typeof(input) !== 'string'; + if (notString && input.constructor === ArrayBuffer) { + input = new Uint8Array(input); + } + if (notString) { + return encodeBytes(input); + } else if (asciiOnly) { + return encodeAscii(input); + } else { + return encodeUtf8(input); + } + }; + + var decode = function (base32Str, asciiOnly) { + if (!asciiOnly) { + return toUtf8String(decodeAsBytes(base32Str)); + } + if (!/^[A-Z2-7=]+$/.test(base32Str)) { + throw new Error('Invalid base32 characters'); + } + var v1, v2, v3, v4, v5, v6, v7, v8, str = '', length = base32Str.indexOf('='); + if (length === -1) { + length = base32Str.length; + } + + // 8 char to 5 bytes + for (var i = 0, count = length >> 3 << 3; i < count;) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) + + String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) + + String.fromCharCode((v4 << 4 | v5 >>> 1) & 255) + + String.fromCharCode((v5 << 7 | v6 << 2 | v7 >>> 3) & 255) + + String.fromCharCode((v7 << 5 | v8) & 255); + } + + // remain bytes + var remain = length - count; + if (remain === 2) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255); + } else if (remain === 4) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) + + String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255); + } else if (remain === 5) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) + + String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) + + String.fromCharCode((v4 << 4 | v5 >>> 1) & 255); + } else if (remain === 7) { + v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)]; + str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) + + String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) + + String.fromCharCode((v4 << 4 | v5 >>> 1) & 255) + + String.fromCharCode((v5 << 7 | v6 << 2 | v7 >>> 3) & 255); + } + return str; + }; + + var exports = { + encode: encode, + decode: decode + }; + decode.asBytes = decodeAsBytes; + + if (COMMON_JS) { + module.exports = exports; + } else { + root.base32 = exports; + if (AMD) { + define(function() { + return exports; + }); + } + } +})(); diff --git a/onionr/static-data/www/shared/identicon.js b/onionr/static-data/www/shared/identicon.js new file mode 100644 index 00000000..cd351cce --- /dev/null +++ b/onionr/static-data/www/shared/identicon.js @@ -0,0 +1,205 @@ +/** + * Identicon.js 2.3.3 + * http://github.com/stewartlord/identicon.js + * + * PNGLib required for PNG output + * http://www.xarg.org/download/pnglib.js + * + * Copyright 2018, Stewart Lord + * Released under the BSD license + * http://www.opensource.org/licenses/bsd-license.php + */ + +(function() { + var PNGlib; + if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + PNGlib = require('./pnglib'); + } else { + PNGlib = window.PNGlib; + } + + var Identicon = function(hash, options){ + if (typeof(hash) !== 'string' || hash.length < 15) { + throw 'A hash of at least 15 characters is required.'; + } + + this.defaults = { + background: [240, 240, 240, 255], + margin: 0.08, + size: 64, + saturation: 0.7, + brightness: 0.5, + format: 'png' + }; + + this.options = typeof(options) === 'object' ? options : this.defaults; + + // backward compatibility with old constructor (hash, size, margin) + if (typeof(arguments[1]) === 'number') { this.options.size = arguments[1]; } + if (arguments[2]) { this.options.margin = arguments[2]; } + + this.hash = hash + this.background = this.options.background || this.defaults.background; + this.size = this.options.size || this.defaults.size; + this.format = this.options.format || this.defaults.format; + this.margin = this.options.margin !== undefined ? this.options.margin : this.defaults.margin; + + // foreground defaults to last 7 chars as hue at 70% saturation, 50% brightness + var hue = parseInt(this.hash.substr(-7), 16) / 0xfffffff; + var saturation = this.options.saturation || this.defaults.saturation; + var brightness = this.options.brightness || this.defaults.brightness; + this.foreground = this.options.foreground || this.hsl2rgb(hue, saturation, brightness); + }; + + Identicon.prototype = { + background: null, + foreground: null, + hash: null, + margin: null, + size: null, + format: null, + + image: function(){ + return this.isSvg() + ? new Svg(this.size, this.foreground, this.background) + : new PNGlib(this.size, this.size, 256); + }, + + render: function(){ + var image = this.image(), + size = this.size, + baseMargin = Math.floor(size * this.margin), + cell = Math.floor((size - (baseMargin * 2)) / 5), + margin = Math.floor((size - cell * 5) / 2), + bg = image.color.apply(image, this.background), + fg = image.color.apply(image, this.foreground); + + // the first 15 characters of the hash control the pixels (even/odd) + // they are drawn down the middle first, then mirrored outwards + var i, color; + for (i = 0; i < 15; i++) { + color = parseInt(this.hash.charAt(i), 16) % 2 ? bg : fg; + if (i < 5) { + this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image); + } else if (i < 10) { + this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image); + this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image); + } else if (i < 15) { + this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image); + this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image); + } + } + + return image; + }, + + rectangle: function(x, y, w, h, color, image){ + if (this.isSvg()) { + image.rectangles.push({x: x, y: y, w: w, h: h, color: color}); + } else { + var i, j; + for (i = x; i < x + w; i++) { + for (j = y; j < y + h; j++) { + image.buffer[image.index(i, j)] = color; + } + } + } + }, + + // adapted from: https://gist.github.com/aemkei/1325937 + hsl2rgb: function(h, s, b){ + h *= 6; + s = [ + b += s *= b < .5 ? b : 1 - b, + b - h % 1 * s * 2, + b -= s *= 2, + b, + b + h % 1 * s, + b + s + ]; + + return[ + s[ ~~h % 6 ] * 255, // red + s[ (h|16) % 6 ] * 255, // green + s[ (h|8) % 6 ] * 255 // blue + ]; + }, + + toString: function(raw){ + // backward compatibility with old toString, default to base64 + if (raw) { + return this.render().getDump(); + } else { + return this.render().getBase64(); + } + }, + + isSvg: function(){ + return this.format.match(/svg/i) + } + }; + + var Svg = function(size, foreground, background){ + this.size = size; + this.foreground = this.color.apply(this, foreground); + this.background = this.color.apply(this, background); + this.rectangles = []; + }; + + Svg.prototype = { + size: null, + foreground: null, + background: null, + rectangles: null, + + color: function(r, g, b, a){ + var values = [r, g, b].map(Math.round); + values.push((a >= 0) && (a <= 255) ? a/255 : 1); + return 'rgba(' + values.join(',') + ')'; + }, + + getDump: function(){ + var i, + xml, + rect, + fg = this.foreground, + bg = this.background, + stroke = this.size * 0.005; + + xml = "" + + ""; + + for (i = 0; i < this.rectangles.length; i++) { + rect = this.rectangles[i]; + if (rect.color == bg) continue; + xml += ""; + } + xml += "" + + return xml; + }, + + getBase64: function(){ + if ('function' === typeof btoa) { + return btoa(this.getDump()); + } else if (Buffer) { + return new Buffer(this.getDump(), 'binary').toString('base64'); + } else { + throw 'Cannot generate base64 output'; + } + } + }; + + if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = Identicon; + } else { + window.Identicon = Identicon; + } +})(); diff --git a/onionr/static-data/www/shared/images/anon.svg b/onionr/static-data/www/shared/images/anon.svg new file mode 100644 index 00000000..786f60ee --- /dev/null +++ b/onionr/static-data/www/shared/images/anon.svg @@ -0,0 +1,63 @@ + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + + + diff --git a/onionr/static-data/www/shared/useridenticons.js b/onionr/static-data/www/shared/useridenticons.js new file mode 100644 index 00000000..2c562578 --- /dev/null +++ b/onionr/static-data/www/shared/useridenticons.js @@ -0,0 +1,23 @@ +function toHexString(byteArray) { + // cc-by-sa-4 https://stackoverflow.com/a/44608819 by https://stackoverflow.com/users/1883624/grantpatterson + var s = '0x'; + byteArray.forEach(function(byte) { + s += ('0' + (byte & 0xFF).toString(16)).slice(-2); + }); + return s; + } + +function userIcon(pubkey, imgSize=64){ + pubkey = toHexString(base32.decode.asBytes(pubkey)) + let options = { + //foreground: [0,0,0,1], // rgba black + background: [255, 255, 255, 255], // rgba white + //margin: 0.1, + size: imgSize, + format: 'svg' // use SVG instead of PNG + }; + + // create a base64 encoded SVG + let data = new Identicon(pubkey, options).toString(); + return data +} \ No newline at end of file