From fa067bdf4663424f392625b7d3d24fe50970a83a Mon Sep 17 00:00:00 2001 From: daniel Date: Sat, 8 Jul 2017 16:37:07 +0200 Subject: [PATCH] Release --- dist/jdenticon.js | 895 ++++++++++++++++++++++++++++++++++++++++++ dist/jdenticon.min.js | 14 + package.json | 2 +- 3 files changed, 910 insertions(+), 1 deletion(-) create mode 100644 dist/jdenticon.js create mode 100644 dist/jdenticon.min.js diff --git a/dist/jdenticon.js b/dist/jdenticon.js new file mode 100644 index 0000000..d12cab4 --- /dev/null +++ b/dist/jdenticon.js @@ -0,0 +1,895 @@ +/** + * Jdenticon 1.6.0 + * http://jdenticon.com + * + * Built: 2017-07-08T14:36:44.057Z + * + * Copyright (c) 2014-2017 Daniel Mester Pirttijärvi + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + */ + +/*jslint bitwise: true */ + +(function (global, name, factory) { + var jQuery = global["jQuery"], + jdenticon = factory(global, jQuery); + + // Node.js + if (typeof module !== "undefined" && "exports" in module) { + module["exports"] = jdenticon; + } + // RequireJS + else if (typeof define === "function" && define["amd"]) { + define([], function () { return jdenticon; }); + } + // No module loader + else { + global[name] = jdenticon; + } +})(this, "jdenticon", function (global, jQuery) { + "use strict"; + + + + + /** + * Represents a point. + * @private + * @constructor + */ + function Point(x, y) { + this.x = x; + this.y = y; + }; + + + /** + * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, + * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly. + * @param {number} x The x-coordinate of the upper left corner of the transformed rectangle. + * @param {number} y The y-coordinate of the upper left corner of the transformed rectangle. + * @param {number} size The size of the transformed rectangle. + * @param {number} rotation Rotation specified as 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad + * @private + * @constructor + */ + function Transform(x, y, size, rotation) { + this._x = x; + this._y = y; + this._size = size; + this._rotation = rotation; + } + Transform.prototype = { + /** + * Transforms the specified point based on the translation and rotation specification for this Transform. + * @param {number} x x-coordinate + * @param {number} y y-coordinate + * @param {number=} w The width of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + * @param {number=} h The height of the transformed rectangle. If greater than 0, this will ensure the returned point is of the upper left corner of the transformed rectangle. + */ + transformPoint: function (x, y, w, h) { + var right = this._x + this._size, + bottom = this._y + this._size; + return this._rotation === 1 ? new Point(right - y - (h || 0), this._y + x) : + this._rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) : + this._rotation === 3 ? new Point(this._x + y, bottom - x - (w || 0)) : + new Point(this._x + x, this._y + y); + } + }; + Transform.noTransform = new Transform(0, 0, 0, 0); + + + + /** + * Provides helper functions for rendering common basic shapes. + * @private + * @constructor + */ + function Graphics(renderer) { + this._renderer = renderer; + this._transform = Transform.noTransform; + } + Graphics.prototype = { + /** + * Adds a polygon to the underlying renderer. + * @param {Array} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ] + * @param {boolean=} invert Specifies if the polygon will be inverted. + */ + addPolygon: function (points, invert) { + var di = invert ? -2 : 2, + transform = this._transform, + transformedPoints = [], + i; + + for (i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) { + transformedPoints.push(transform.transformPoint(points[i], points[i + 1])); + } + + this._renderer.addPolygon(transformedPoints); + }, + + /** + * Adds a polygon to the underlying renderer. + * Source: http://stackoverflow.com/a/2173084 + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse. + * @param {number} size The size of the ellipse. + * @param {boolean=} invert Specifies if the ellipse will be inverted. + */ + addCircle: function (x, y, size, invert) { + var p = this._transform.transformPoint(x, y, size, size); + this._renderer.addCircle(p, size, invert); + }, + + /** + * Adds a rectangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle. + * @param {number} w The width of the rectangle. + * @param {number} h The height of the rectangle. + * @param {boolean=} invert Specifies if the rectangle will be inverted. + */ + addRectangle: function (x, y, w, h, invert) { + this.addPolygon([ + x, y, + x + w, y, + x + w, y + h, + x, y + h + ], invert); + }, + + /** + * Adds a right triangle to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle. + * @param {number} w The width of the triangle. + * @param {number} h The height of the triangle. + * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle. + * @param {boolean=} invert Specifies if the triangle will be inverted. + */ + addTriangle: function (x, y, w, h, r, invert) { + var points = [ + x + w, y, + x + w, y + h, + x, y + h, + x, y + ]; + points.splice(((r || 0) % 4) * 2, 2); + this.addPolygon(points, invert); + }, + + /** + * Adds a rhombus to the underlying renderer. + * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus. + * @param {number} w The width of the rhombus. + * @param {number} h The height of the rhombus. + * @param {boolean=} invert Specifies if the rhombus will be inverted. + */ + addRhombus: function (x, y, w, h, invert) { + this.addPolygon([ + x + w / 2, y, + x + w, y + h / 2, + x + w / 2, y + h, + x, y + h / 2 + ], invert); + } + }; + + + + + var shapes = { + center: [ + /** @param {Graphics} g */ + function (g, cell, index) { + var k = cell * 0.42; + g.addPolygon([ + 0, 0, + cell, 0, + cell, cell - k * 2, + cell - k, cell, + 0, cell + ]); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var w = 0 | (cell * 0.5), + h = 0 | (cell * 0.8); + g.addTriangle(cell - w, 0, w, h, 2); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var s = 0 | (cell / 3); + g.addRectangle(s, s, cell - s, cell - s); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var inner = cell * 0.1, + inner = + inner > 1 ? (0 | inner) : // large icon => truncate decimals + inner > 0.5 ? 1 : // medium size icon => fixed width + inner, // small icon => anti-aliased border + + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 6 ? 1 : + cell < 8 ? 2 : + (0 | (cell * 0.25)); + + g.addRectangle(outer, outer, cell - inner - outer, cell - inner - outer); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var m = 0 | (cell * 0.15), + s = 0 | (cell * 0.5); + g.addCircle(cell - s - m, cell - s - m, s); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var inner = cell * 0.1, + outer = inner * 4; + + g.addRectangle(0, 0, cell, cell); + g.addPolygon([ + outer, outer, + cell - inner, outer, + outer + (cell - outer - inner) / 2, cell - inner + ], true); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + g.addPolygon([ + 0, 0, + cell, 0, + cell, cell * 0.7, + cell * 0.4, cell * 0.4, + cell * 0.7, cell, + 0, cell + ]); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + g.addRectangle(0, 0, cell, cell / 2); + g.addRectangle(0, cell / 2, cell / 2, cell / 2); + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 1); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var inner = cell * 0.14, + inner = + cell < 8 ? inner : // small icon => anti-aliased border + (0 | inner), // large icon => truncate decimals + + // Use fixed outer border widths in small icons to ensure the border is drawn + outer = + cell < 4 ? 1 : + cell < 6 ? 2 : + (0 | (cell * 0.35)); + + g.addRectangle(0, 0, cell, cell); + g.addRectangle(outer, outer, cell - outer - inner, cell - outer - inner, true); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var inner = cell * 0.12, + outer = inner * 3; + + g.addRectangle(0, 0, cell, cell); + g.addCircle(outer, outer, cell - inner - outer, true); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + g.addTriangle(cell / 2, cell / 2, cell / 2, cell / 2, 3); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var m = cell * 0.25; + g.addRectangle(0, 0, cell, cell); + g.addRhombus(m, m, cell - m, cell - m, true); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var m = cell * 0.4, s = cell * 1.2; + if (!index) { + g.addCircle(m, m, s); + } + } + ], + + outer: [ + /** @param {Graphics} g */ + function (g, cell, index) { + g.addTriangle(0, 0, cell, cell, 0); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + g.addTriangle(0, cell / 2, cell, cell / 2, 0); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + g.addRhombus(0, 0, cell, cell); + }, + /** @param {Graphics} g */ + function (g, cell, index) { + var m = cell / 6; + g.addCircle(m, m, cell - 2 * m); + } + ] + }; + + + + + function decToHex(v) { + v |= 0; // Ensure integer value + return v < 0 ? "00" : + v < 16 ? "0" + v.toString(16) : + v < 256 ? v.toString(16) : + "ff"; + } + + function hueToRgb(m1, m2, h) { + h = h < 0 ? h + 6 : h > 6 ? h - 6 : h; + return decToHex(255 * ( + h < 1 ? m1 + (m2 - m1) * h : + h < 3 ? m2 : + h < 4 ? m1 + (m2 - m1) * (4 - h) : + m1)); + } + + /** + * Functions for converting colors to hex-rgb representations. + * @private + */ + var color = { + /** + * @param {number} r Red channel [0, 255] + * @param {number} g Green channel [0, 255] + * @param {number} b Blue channel [0, 255] + */ + rgb: function (r, g, b) { + return "#" + decToHex(r) + decToHex(g) + decToHex(b); + }, + /** + * @param h Hue [0, 1] + * @param s Saturation [0, 1] + * @param l Lightness [0, 1] + */ + hsl: function (h, s, l) { + // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color + if (s == 0) { + var partialHex = decToHex(l * 255); + return "#" + partialHex + partialHex + partialHex; + } + else { + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s, + m1 = l * 2 - m2; + return "#" + + hueToRgb(m1, m2, h * 6 + 2) + + hueToRgb(m1, m2, h * 6) + + hueToRgb(m1, m2, h * 6 - 2); + } + }, + // This function will correct the lightness for the "dark" hues + correctedHsl: function (h, s, l) { + // The corrector specifies the perceived middle lightnesses for each hue + var correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ], + corrector = correctors[(h * 6 + 0.5) | 0]; + + // Adjust the input lightness relative to the corrector + l = l < 0.5 ? l * corrector * 2 : corrector + (l - 0.5) * (1 - corrector) * 2; + + return color.hsl(h, s, l); + } + }; + + + + + /** + * Gets a set of identicon color candidates for a specified hue and config. + */ + function colorTheme(hue, config) { + return [ + // Dark gray + color.hsl(0, 0, config.grayscaleLightness(0)), + // Mid color + color.correctedHsl(hue, config.saturation, config.colorLightness(0.5)), + // Light gray + color.hsl(0, 0, config.grayscaleLightness(1)), + // Light color + color.correctedHsl(hue, config.saturation, config.colorLightness(1)), + // Dark color + color.correctedHsl(hue, config.saturation, config.colorLightness(0)) + ]; + } + + + + + /** + * Draws an identicon to a specified renderer. + */ + function iconGenerator(renderer, hash, x, y, size, padding, config) { + var undefined; + + // Calculate padding + padding = (size * (padding === undefined ? 0.08 : padding)) | 0; + size -= padding * 2; + + if (!/^[0-9a-f]{11,}$/i.test(hash)) { + throw new Error("Invalid hash passed to Jdenticon."); + } + + var graphics = new Graphics(renderer); + + // Calculate cell size and ensure it is an integer + var cell = 0 | (size / 4); + + // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon + x += 0 | (padding + size / 2 - cell * 2); + y += 0 | (padding + size / 2 - cell * 2); + + function renderShape(colorIndex, shapes, index, rotationIndex, positions) { + var r = rotationIndex ? parseInt(hash.charAt(rotationIndex), 16) : 0, + shape = shapes[parseInt(hash.charAt(index), 16) % shapes.length], + i; + + renderer.beginShape(availableColors[selectedColorIndexes[colorIndex]]); + + for (i = 0; i < positions.length; i++) { + graphics._transform = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4); + shape(graphics, cell, i); + } + + renderer.endShape(); + } + + // AVAILABLE COLORS + var hue = parseInt(hash.substr(-7), 16) / 0xfffffff, + + // Available colors for this icon + availableColors = colorTheme(hue, config), + + // The index of the selected colors + selectedColorIndexes = [], + index; + + function isDuplicate(values) { + if (values.indexOf(index) >= 0) { + for (var i = 0; i < values.length; i++) { + if (selectedColorIndexes.indexOf(values[i]) >= 0) { + return true; + } + } + } + } + + for (var i = 0; i < 3; i++) { + index = parseInt(hash.charAt(8 + i), 16) % availableColors.length; + if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo + isDuplicate([2, 3])) { // Disallow light gray and light color combo + index = 1; + } + selectedColorIndexes.push(index); + } + + // ACTUAL RENDERING + // Sides + renderShape(0, shapes.outer, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]); + // Corners + renderShape(1, shapes.outer, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]); + // Center + renderShape(2, shapes.center, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]); + + renderer.finish(); + }; + + + + + /** + * Prepares a measure to be used as a measure in an SVG path, by + * rounding the measure to a single decimal. This reduces the file + * size of the generated SVG with more than 50% in some cases. + */ + function svgValue(value) { + return ((value * 10 + 0.5) | 0) / 10; + } + + /** + * Represents an SVG path element. + * @private + * @constructor + */ + function SvgPath() { + /** + * This property holds the data string (path.d) of the SVG path. + */ + this.dataString = ""; + } + SvgPath.prototype = { + /** + * Adds a polygon with the current fill color to the SVG path. + * @param points An array of Point objects. + */ + addPolygon: function (points) { + var dataString = "M" + svgValue(points[0].x) + " " + svgValue(points[0].y); + for (var i = 1; i < points.length; i++) { + dataString += "L" + svgValue(points[i].x) + " " + svgValue(points[i].y); + } + this.dataString += dataString + "Z"; + }, + /** + * Adds a circle with the current fill color to the SVG path. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle: function (point, diameter, counterClockwise) { + var sweepFlag = counterClockwise ? 0 : 1, + svgRadius = svgValue(diameter / 2), + svgDiameter = svgValue(diameter); + + this.dataString += + "M" + svgValue(point.x) + " " + svgValue(point.y + diameter / 2) + + "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " " + svgDiameter + ",0" + + "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " " + (-svgDiameter) + ",0"; + } + }; + + + + /** + * Renderer producing SVG output. + * @private + * @constructor + * @param {SvgElement|SvgWriter} target + */ + function SvgRenderer(target) { + this._pathsByColor = { }; + this._target = target; + this.size = target.size; + } + SvgRenderer.prototype = { + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} color Fill color on format #xxxxxx. + */ + beginShape: function (color) { + this._path = this._pathsByColor[color] || (this._pathsByColor[color] = new SvgPath()); + }, + /** + * Marks the end of the currently drawn shape. + */ + endShape: function () { }, + /** + * Adds a polygon with the current fill color to the SVG. + * @param points An array of Point objects. + */ + addPolygon: function (points) { + this._path.addPolygon(points); + }, + /** + * Adds a circle with the current fill color to the SVG. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle: function (point, diameter, counterClockwise) { + this._path.addCircle(point, diameter, counterClockwise); + }, + /** + * Called when the icon has been completely drawn. + */ + finish: function () { + for (var color in this._pathsByColor) { + this._target.append(color, this._pathsByColor[color].dataString); + } + } + }; + + + + /** + * Renderer producing SVG output. + * @private + * @constructor + */ + function SvgElement(element) { + // Don't use the clientWidth and clientHeight properties on SVG elements + // since Firefox won't serve a proper value of these properties on SVG + // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811) + // Instead use 100px as a hardcoded size (the svg viewBox will rescale + // the icon to the correct dimensions) + this.size = Math.min( + (Number(element.getAttribute("width")) || 100), + (Number(element.getAttribute("height")) || 100) + ); + this._el = element; + + // Clear current SVG child elements + while (element.firstChild) { + element.removeChild(element.firstChild); + } + + // Set viewBox attribute to ensure the svg scales nicely. + element.setAttribute("viewBox", "0 0 " + this.size + " " + this.size); + element.setAttribute("preserveAspectRatio", "xMidYMid meet"); + } + SvgElement.prototype = { + /** + * Appends a path to the SVG element. + * @param {string} color Fill color on format #xxxxxx. + * @param {string} dataString The SVG path data string. + */ + append: function (color, dataString) { + var path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute("fill", color); + path.setAttribute("d", dataString); + this._el.appendChild(path); + } + }; + + + + /** + * Renderer producing SVG output. + * @private + * @constructor + */ + function SvgWriter(size) { + this.size = size; + this._s = + ''; + } + SvgWriter.prototype = { + /** + * Writes a path to the SVG string. + * @param {string} color Fill color on format #xxxxxx. + * @param {string} dataString The SVG path data string. + */ + append: function (color, dataString) { + this._s += + ''; + }, + /** + * Gets the rendered image as an SVG string. + */ + toString: function () { + return this._s + ""; + } + }; + + + + /** + * Renderer redirecting drawing commands to a canvas context. + * @private + * @constructor + */ + function CanvasRenderer(ctx, size) { + var width = ctx.canvas.width, + height = ctx.canvas.height; + + this._ctx = ctx; + + if (size) { + this.size = size; + } + else { + this.size = Math.min(width, height); + + ctx.translate( + ((width - this.size) / 2) | 0, + ((height - this.size) / 2) | 0); + } + + ctx.clearRect(0, 0, this.size, this.size); + } + CanvasRenderer.prototype = { + /** + * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape. + * @param {string} color Fill color on format #xxxxxx. + */ + beginShape: function (color) { + this._ctx.fillStyle = color; + this._ctx.beginPath(); + }, + /** + * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas. + */ + endShape: function () { + this._ctx.fill(); + }, + /** + * Adds a polygon to the rendering queue. + * @param points An array of Point objects. + */ + addPolygon: function (points) { + var ctx = this._ctx, i; + ctx.moveTo(points[0].x, points[0].y); + for (i = 1; i < points.length; i++) { + ctx.lineTo(points[i].x, points[i].y); + } + ctx.closePath(); + }, + /** + * Adds a circle to the rendering queue. + * @param {Point} point The upper left corner of the circle bounding box. + * @param {number} diameter The diameter of the circle. + * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path). + */ + addCircle: function (point, diameter, counterClockwise) { + var ctx = this._ctx, + radius = diameter / 2; + ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise); + ctx.closePath(); + }, + /** + * Called when the icon has been completely drawn. + */ + finish: function () { } + }; + + + + + + + var /** @const */ + HASH_ATTRIBUTE = "data-jdenticon-hash", + supportsQuerySelectorAll = typeof document !== "undefined" && "querySelectorAll" in document; + + /** + * Gets the normalized current Jdenticon color configuration. Missing fields have default values. + */ + function getCurrentConfig() { + var configObject = jdenticon["config"] || global["jdenticon_config"] || { }, + lightnessConfig = configObject["lightness"] || { }, + saturation = configObject["saturation"]; + + /** + * Creates a lightness range. + */ + function lightness(configName, defaultMin, defaultMax) { + var range = lightnessConfig[configName] instanceof Array ? lightnessConfig[configName] : [defaultMin, defaultMax]; + + /** + * Gets a lightness relative the specified value in the specified lightness range. + */ + return function (value) { + value = range[0] + value * (range[1] - range[0]); + return value < 0 ? 0 : value > 1 ? 1 : value; + }; + } + + return { + saturation: typeof saturation == "number" ? saturation : 0.5, + colorLightness: lightness("color", 0.4, 0.8), + grayscaleLightness: lightness("grayscale", 0.3, 0.9) + } + } + + /** + * Updates the identicon in the specified canvas or svg elements. + * @param {string=} hash Optional hash to be rendered. If not specified, the hash specified by the data-jdenticon-hash is used. + * @param {number=} padding Optional padding in percents. Extra padding might be added to center the rendered identicon. + */ + function update(el, hash, padding) { + if (typeof(el) === "string") { + if (supportsQuerySelectorAll) { + var elements = document.querySelectorAll(el); + for (var i = 0; i < elements.length; i++) { + update(elements[i], hash, padding); + } + } + return; + } + if (!el || !el["tagName"]) { + // No element found + return; + } + hash = hash || el.getAttribute(HASH_ATTRIBUTE); + if (!hash) { + // No hash specified + return; + } + + var isSvg = el["tagName"].toLowerCase() == "svg", + isCanvas = el["tagName"].toLowerCase() == "canvas"; + + // Ensure we have a supported element + if (!isSvg && !(isCanvas && "getContext" in el)) { + return; + } + + var renderer = isSvg ? + new SvgRenderer(new SvgElement(el)) : + new CanvasRenderer(el.getContext("2d")); + + // Draw icon + iconGenerator(renderer, hash, 0, 0, renderer.size, padding, getCurrentConfig()); + } + + /** + * Draws an identicon to a context. + */ + function drawIcon(ctx, hash, size) { + if (!ctx) { + throw new Error("No canvas specified."); + } + + var renderer = new CanvasRenderer(ctx, size); + iconGenerator(renderer, hash, 0, 0, size, 0, getCurrentConfig()); + } + + /** + * Draws an identicon to a context. + * @param {number=} padding Optional padding in percents. Extra padding might be added to center the rendered identicon. + */ + function toSvg(hash, size, padding) { + var writer = new SvgWriter(size); + var renderer = new SvgRenderer(writer); + iconGenerator(renderer, hash, 0, 0, size, padding, getCurrentConfig()); + return writer.toString(); + } + + /** + * Updates all canvas elements with the data-jdenticon-hash attribute. + */ + function jdenticon() { + if (supportsQuerySelectorAll) { + update("svg[" + HASH_ATTRIBUTE + "],canvas[" + HASH_ATTRIBUTE + "]"); + } + } + + // Public API + jdenticon["drawIcon"] = drawIcon; + jdenticon["toSvg"] = toSvg; + jdenticon["update"] = update; + jdenticon["version"] = "1.6.0"; + + // Basic jQuery plugin + if (jQuery) { + jQuery["fn"]["jdenticon"] = function (hash, padding) { + this["each"](function (index, el) { + update(el, hash, padding); + }); + return this; + }; + } + + // Schedule to render all identicons on the page once it has been loaded. + if (typeof setTimeout === "function") { + setTimeout(jdenticon, 0); + } + + return jdenticon; + +}); \ No newline at end of file diff --git a/dist/jdenticon.min.js b/dist/jdenticon.min.js new file mode 100644 index 0000000..d5c4261 --- /dev/null +++ b/dist/jdenticon.min.js @@ -0,0 +1,14 @@ +// Jdenticon 1.6.0 | jdenticon.com | zlib licensed | (c) 2014-2017 Daniel Mester Pirttijärvi +(function(p,q,l){var m=l(p,p.jQuery);"undefined"!==typeof module&&"exports"in module?module.exports=m:"function"===typeof define&&define.amd?define([],function(){return m}):p[q]=m})(this,"jdenticon",function(p,q){function l(b,a){this.x=b;this.y=a}function m(b,a,c,d){this.o=b;this.s=a;this.D=c;this.j=d}function A(b){this.C=b;this.m=m.O}function n(b){b|=0;return 0>b?"00":16>b?"0"+b.toString(16):256>b?b.toString(16):"ff"}function u(b,a,c){c=0>c?c+6:6c?b+(a-b)*c:3>c?a:4>c?b+(a- +b)*(4-c):b))}function F(b,a){return[h.w(0,0,a.I(0)),h.v(b,a.A,a.u(.5)),h.w(0,0,a.I(1)),h.v(b,a.A,a.u(1)),h.v(b,a.A,a.u(0))]}function v(b,a,c,d,e){var f=0,k=0;function r(c,d,e,g,r){g=g?parseInt(a.charAt(g),16):0;d=d[parseInt(a.charAt(e),16)%d.length];b.G(p[n[c]]);for(c=0;cc;c++){var q=parseInt(a.charAt(8+c),16)%p.length;if(g([0,4])||g([2,3]))q=1;n.push(q)}r(0,w.J,2,3,[[1,0],[2,0],[2,3],[1,3],[0,1],[3,1],[3,2],[0,2]]);r(1,w.J,4,5,[[0,0],[3,0],[3,3],[0,3]]);r(2,w.N,1,null,[[1,1],[2,1],[2,2],[1,2]]);b.finish()}function k(b){return(10*b+.5|0)/10}function B(){this.h=""}function x(b){this.i={};this.M=b;this.size=b.size}function C(b){this.size=Math.min(Number(b.getAttribute("width"))|| +100,Number(b.getAttribute("height"))||100);for(this.L=b;b.firstChild;)b.removeChild(b.firstChild);b.setAttribute("viewBox","0 0 "+this.size+" "+this.size);b.setAttribute("preserveAspectRatio","xMidYMid meet")}function D(b){this.size=b;this.l=''}function y(b,a){var c=b.canvas.width,d=b.canvas.height;this.g=b;a?this.size=a:(this.size=Math.min(c,d),b.translate((c-this.size)/ +2|0,(d-this.size)/2|0));b.clearRect(0,0,this.size,this.size)}function z(){function b(a,b,f){var d=c[a]instanceof Array?c[a]:[b,f];return function(a){a=d[0]+a*(d[1]-d[0]);return 0>a?0:1a?1:8>a?2:0|.25*a;b.c(d,d,a-c-d,a-c-d)},function(b,a){var c=0|.15*a,d=0|.5*a;b.b(a-d-c,a-d-c,d)},function(b,a){var c=.1*a,d=4*c;b.c(0,0,a,a);b.a([d,d,a-c,d,d+(a-d-c)/2,a-c],!0)},function(b,a){b.a([0,0,a,0,a,.7*a,.4*a,.4*a,.7*a,a,0,a])},function(b,a){b.f(a/2,a/2,a/2,a/2,3)},function(b,a){b.c(0,0,a,a/2);b.c(0,a/2,a/2,a/2);b.f(a/2,a/2,a/2,a/2,1)},function(b,a){var c=.14*a,c=8>a?c:0|c,d=4>a?1:6>a?2:0|.35*a;b.c(0,0,a,a);b.c(d, +d,a-d-c,a-d-c,!0)},function(b,a){var c=.12*a,d=3*c;b.c(0,0,a,a);b.b(d,d,a-c-d,!0)},function(b,a){b.f(a/2,a/2,a/2,a/2,3)},function(b,a){var c=.25*a;b.c(0,0,a,a);b.F(c,c,a-c,a-c,!0)},function(b,a,c){var d=.4*a;c||b.b(d,d,1.2*a)}],J:[function(b,a){b.f(0,0,a,a,0)},function(b,a){b.f(0,a/2,a,a/2,0)},function(b,a){b.F(0,0,a,a)},function(b,a){var c=a/6;b.b(c,c,a-2*c)}]},h={P:function(b,a,c){return"#"+n(b)+n(a)+n(c)},w:function(b,a,c){if(0==a)return b=n(255*c),"#"+b+b+b;a=.5>=c?c*(a+1):c+a-c*a;c=2*c-a;return"#"+ +u(c,a,6*b+2)+u(c,a,6*b)+u(c,a,6*b-2)},v:function(b,a,c){var d=[.55,.5,.5,.46,.6,.55,.55][6*b+.5|0];return h.w(b,a,.5>c?c*d*2:d+(c-.5)*(1-d)*2)}};B.prototype={a:function(b){for(var a="M"+k(b[0].x)+" "+k(b[0].y),c=1;c'},toString:function(){return this.l+""}};y.prototype={G:function(b){this.g.fillStyle=b;this.g.beginPath()},H:function(){this.g.fill()},a:function(b){var a= +this.g,c;a.moveTo(b[0].x,b[0].y);for(c=1;c