diff --git a/README.md b/README.md index 51836f01..820b510d 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,6 @@ ComunicWeb would not exists without the following technologies developped by the - VideoJS - jquery.hotkeys - bootstrap-wysiwyg (https://github.com/steveathon/bootstrap-wysiwyg/) -- wdt-emoji-bundle (http://ned.im/wdt-emoji-bundle) \ No newline at end of file +- wdt-emoji-bundle (http://ned.im/wdt-emoji-bundle) +- PNGLib (http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/) (BSD Licence) +- Identicon (http://github.com/stewartlord/identicon.js) (BSD Licence) \ No newline at end of file diff --git a/assets/3rdparty/identicon.js/LICENSE b/assets/3rdparty/identicon.js/LICENSE new file mode 100644 index 00000000..20890ee3 --- /dev/null +++ b/assets/3rdparty/identicon.js/LICENSE @@ -0,0 +1,8 @@ +Copyright (c) 2018, Stewart Lord +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/assets/3rdparty/identicon.js/README.md b/assets/3rdparty/identicon.js/README.md new file mode 100644 index 00000000..59deac59 --- /dev/null +++ b/assets/3rdparty/identicon.js/README.md @@ -0,0 +1,73 @@ +identicon.js +============ + +![Screenshot](/screenshot.png) + +[![CDNJS version](https://img.shields.io/cdnjs/v/identicon.js.svg)](https://cdnjs.com/libraries/identicon.js) + +GitHub-style identicons as PNGs or SVGs in JS. + +This little library will produce the same shape and (nearly) the same color as GitHub when given the same hash value. Supports PNG and SVG output formats. Note that GitHub uses an internal database identifier for the hash, so you can't simply md5 the username and get the same result. The creative visual design is borrowed from [Jason Long](http://twitter.com/jasonlong) of Git and GitHub fame. + +Demo +---- +[View Demo](https://rawgithub.com/stewartlord/identicon.js/master/demo.html) + +Installation +----- +``` +npm install identicon.js --save +``` +[![NPM Stats](https://nodei.co/npm/identicon.js.png?downloads=true)](https://npmjs.org/package/identicon.js) + +Options +---- +* **hash** - A hexadecimal string of 15+ characters that will be used to generate the image. +* **options** - [Optional] An options object used to customize the generated image. + * **size** - The size in pixels of the height and width of the generated (square) image. Defaults to 64 pixels. + * **margin** - The decimal fraction of the size to use for margin. For example, use 0.2 for a 20% margin. Defaults to 0.08 for an 8% margin. + * **foreground** - The foreground color is automatically derived from the hash value. Use this option to override that behavior and provide a rgba value array instead (e.g. [255,0,0,255] for red). + * **background** - The background color expressed as an rgba value array to use for the image background. For example, use [255,0,0,255] for red. Defaults to an opaque light gray [240,240,240,255]. + * **saturation** - The saturation of the derived foreground color as a value from 0-1. Defaults to 0.7. + * **brightness** - The brightness of the derived foreground color as a value from 0-1. Defaults to 0.5. + + +Usage +----- + +##### Simple +Generate the Identicon by supplying a hash string and size. +```js + +// create a base64 encoded PNG +var data = new Identicon('d3b07384d113edec49eaa6238ad5ff00', 420).toString(); + +// write to a data URI +document.write(''); +``` + +##### Advanced +To customize additional properties, generate the Identicon by supplying a hexadecimal string and an options object. +```js +// set up options +var hash = "c157a79031e1c40f85931829bc5fc552"; // 15+ hex chars +var options = { + foreground: [0, 0, 0, 255], // rgba black + background: [255, 255, 255, 255], // rgba white + margin: 0.2, // 20% margin + size: 420, // 420px square + format: 'svg' // use SVG instead of PNG + }; + +// create a base64 encoded SVG +var data = new Identicon(hash, options).toString(); + +// write to a data URI +document.write(''); +``` + + +[PNG output requires PNGLib](http://www.xarg.org/download/pnglib.js) + +Copyright 2018, [Stewart Lord](https://github.com/stewartlord) +Released under the [BSD license](http://www.opensource.org/licenses/bsd-license.php) diff --git a/assets/3rdparty/identicon.js/identicon.js b/assets/3rdparty/identicon.js/identicon.js new file mode 100644 index 00000000..08f68eae --- /dev/null +++ b/assets/3rdparty/identicon.js/identicon.js @@ -0,0 +1,205 @@ +/** + * Identicon.js 2.3.2 + * 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 (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/assets/3rdparty/identicon.js/package.json b/assets/3rdparty/identicon.js/package.json new file mode 100644 index 00000000..98ede396 --- /dev/null +++ b/assets/3rdparty/identicon.js/package.json @@ -0,0 +1,19 @@ +{ + "name": "identicon.js", + "version": "2.3.2", + "description": "GitHub-style identicons as PNGs or SVGs in JS.", + "main": "identicon.js", + "repository": { + "type": "git", + "url": "https://github.com/stewartlord/identicon.js" + }, + "keywords": [ + "identicon" + ], + "author": "stewardlord", + "license": "BSD", + "bugs": { + "url": "https://github.com/stewartlord/identicon.js/issues" + }, + "homepage": "https://github.com/stewartlord/identicon.js" +} diff --git a/assets/3rdparty/identicon.js/pnglib.js b/assets/3rdparty/identicon.js/pnglib.js new file mode 100644 index 00000000..17c03b60 --- /dev/null +++ b/assets/3rdparty/identicon.js/pnglib.js @@ -0,0 +1,214 @@ +/** +* A handy class to calculate color values. +* +* @version 1.0 +* @author Robert Eisele +* @copyright Copyright (c) 2010, Robert Eisele +* @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ +* @license http://www.opensource.org/licenses/bsd-license.php BSD License +* +*/ + +(function() { + + // helper functions for that ctx + function write(buffer, offs) { + for (var i = 2; i < arguments.length; i++) { + for (var j = 0; j < arguments[i].length; j++) { + buffer[offs++] = arguments[i].charAt(j); + } + } + } + + function byte2(w) { + return String.fromCharCode((w >> 8) & 255, w & 255); + } + + function byte4(w) { + return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255); + } + + function byte2lsb(w) { + return String.fromCharCode(w & 255, (w >> 8) & 255); + } + + // modified from original source to support NPM + var PNGlib = function(width,height,depth) { + + this.width = width; + this.height = height; + this.depth = depth; + + // pixel data and row filter identifier size + this.pix_size = height * (width + 1); + + // deflate header, pix_size, block headers, adler32 checksum + this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4; + + // offsets and sizes of Png chunks + this.ihdr_offs = 0; // IHDR offset and size + this.ihdr_size = 4 + 4 + 13 + 4; + this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size + this.plte_size = 4 + 4 + 3 * depth + 4; + this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size + this.trns_size = 4 + 4 + depth + 4; + this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size + this.idat_size = 4 + 4 + this.data_size + 4; + this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size + this.iend_size = 4 + 4 + 4; + this.buffer_size = this.iend_offs + this.iend_size; // total PNG size + + this.buffer = new Array(); + this.palette = new Object(); + this.pindex = 0; + + var _crc32 = new Array(); + + // initialize buffer with zero bytes + for (var i = 0; i < this.buffer_size; i++) { + this.buffer[i] = "\x00"; + } + + // initialize non-zero elements + write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03"); + write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE'); + write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS'); + write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT'); + write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND'); + + // initialize deflate header + var header = ((8 + (7 << 4)) << 8) | (3 << 6); + header+= 31 - (header % 31); + + write(this.buffer, this.idat_offs + 8, byte2(header)); + + // initialize deflate block headers + for (var i = 0; (i << 16) - 1 < this.pix_size; i++) { + var size, bits; + if (i + 0xffff < this.pix_size) { + size = 0xffff; + bits = "\x00"; + } else { + size = this.pix_size - (i << 16) - i; + bits = "\x01"; + } + write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size)); + } + + /* Create crc32 lookup table */ + for (var i = 0; i < 256; i++) { + var c = i; + for (var j = 0; j < 8; j++) { + if (c & 1) { + c = -306674912 ^ ((c >> 1) & 0x7fffffff); + } else { + c = (c >> 1) & 0x7fffffff; + } + } + _crc32[i] = c; + } + + // compute the index into a png for a given pixel + this.index = function(x,y) { + var i = y * (this.width + 1) + x + 1; + var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i; + return j; + } + + // convert a color and build up the palette + this.color = function(red, green, blue, alpha) { + + alpha = alpha >= 0 ? alpha : 255; + var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue; + + if (typeof this.palette[color] == "undefined") { + if (this.pindex == this.depth) return "\x00"; + + var ndx = this.plte_offs + 8 + 3 * this.pindex; + + this.buffer[ndx + 0] = String.fromCharCode(red); + this.buffer[ndx + 1] = String.fromCharCode(green); + this.buffer[ndx + 2] = String.fromCharCode(blue); + this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha); + + this.palette[color] = String.fromCharCode(this.pindex++); + } + return this.palette[color]; + } + + // output a PNG string, Base64 encoded + this.getBase64 = function() { + + var s = this.getDump(); + + var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var c1, c2, c3, e1, e2, e3, e4; + var l = s.length; + var i = 0; + var r = ""; + + do { + c1 = s.charCodeAt(i); + e1 = c1 >> 2; + c2 = s.charCodeAt(i+1); + e2 = ((c1 & 3) << 4) | (c2 >> 4); + c3 = s.charCodeAt(i+2); + if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); } + if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; } + r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4); + } while ((i+= 3) < l); + return r; + } + + // output a PNG string + this.getDump = function() { + + // compute adler32 of output pixels + row filter bytes + var BASE = 65521; /* largest prime smaller than 65536 */ + var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + var s1 = 1; + var s2 = 0; + var n = NMAX; + + for (var y = 0; y < this.height; y++) { + for (var x = -1; x < this.width; x++) { + s1+= this.buffer[this.index(x, y)].charCodeAt(0); + s2+= s1; + if ((n-= 1) == 0) { + s1%= BASE; + s2%= BASE; + n = NMAX; + } + } + } + s1%= BASE; + s2%= BASE; + write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1)); + + // compute crc32 of the PNG chunks + function crc32(png, offs, size) { + var crc = -1; + for (var i = 4; i < size-4; i += 1) { + crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff); + } + write(png, offs+size-4, byte4(crc ^ -1)); + } + + crc32(this.buffer, this.ihdr_offs, this.ihdr_size); + crc32(this.buffer, this.plte_offs, this.plte_size); + crc32(this.buffer, this.trns_offs, this.trns_size); + crc32(this.buffer, this.idat_offs, this.idat_size); + crc32(this.buffer, this.iend_offs, this.iend_size); + + // convert PNG to string + return "\x89PNG\r\n\x1a\n"+this.buffer.join(''); + } + } + + // modified from original source to support NPM + if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = PNGlib; + } else { + window.PNGlib = PNGlib; + } +})(); diff --git a/assets/js/common/utils.js b/assets/js/common/utils.js index d8c9f920..58fdb092 100644 --- a/assets/js/common/utils.js +++ b/assets/js/common/utils.js @@ -507,4 +507,39 @@ window.location.changed = function(e){}; } -})(jQuery); \ No newline at end of file +})(jQuery); + +/** + * Generate a new random string + * + * @param {number} length The length of the string ot generate + * @return {string} Generated string + */ +function random_string(length){ + + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst0123456789"; + + for(var i = 0; i < length; i++) + text += possible.charAt(Math.random() * possible.length); + + return text; +} + +/** + * Generate and return a new random identity image + * + * @return {string} Base64 - encoded new identity image + */ +function generateIdentImage() { + var hash = random_string(32); + var color = Math.random() * 240; + var color2 = Math.random() * 240; + var color3 = Math.random() * 240; + var options = { + foreground: [color, color2, color3, 255], + size: 130, + format: 'png' + }; + return new Identicon(hash, options).toString(); +} \ No newline at end of file diff --git a/system/config/dev.config.php b/system/config/dev.config.php index 2b1a6226..f1d6f5c9 100644 --- a/system/config/dev.config.php +++ b/system/config/dev.config.php @@ -127,6 +127,10 @@ class Dev { //Emoji picker "3rdparty/wdt-emoji/emoji.min.js", "3rdparty/wdt-emoji/wdt-emoji-bundle.js", + + //Identicon.JS + "3rdparty/identicon.js/pnglib.js", + "3rdparty/identicon.js/identicon.js", ); /**