vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

jdenticon-3.3.0.js (51424B)


      1 /**
      2  * Jdenticon 3.3.0
      3  * http://jdenticon.com
      4  *  
      5  * Built: 2024-05-10T09:48:41.921Z
      6  *
      7  * MIT License
      8  * 
      9  * Copyright (c) 2014-2024 Daniel Mester Pirttijärvi
     10  * 
     11  * Permission is hereby granted, free of charge, to any person obtaining a copy
     12  * of this software and associated documentation files (the "Software"), to deal
     13  * in the Software without restriction, including without limitation the rights
     14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     15  * copies of the Software, and to permit persons to whom the Software is
     16  * furnished to do so, subject to the following conditions:
     17  * 
     18  * The above copyright notice and this permission notice shall be included in all
     19  * copies or substantial portions of the Software.
     20  * 
     21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     27  * SOFTWARE.
     28  */
     29 
     30 (function (umdGlobal, factory) {
     31     var jdenticon = factory(umdGlobal);
     32 
     33     // Node.js
     34     if (typeof module !== "undefined" && "exports" in module) {
     35         module["exports"] = jdenticon;
     36     }
     37     // RequireJS
     38     else if (typeof define === "function" && define["amd"]) {
     39         define([], function () { return jdenticon; });
     40     }
     41     // No module loader
     42     else {
     43         umdGlobal["jdenticon"] = jdenticon;
     44     }
     45 })(typeof self !== "undefined" ? self : this, function (umdGlobal) {
     46 'use strict';
     47 
     48 /**
     49  * Parses a substring of the hash as a number.
     50  * @param {number} startPosition 
     51  * @param {number=} octets
     52  */
     53 function parseHex(hash, startPosition, octets) {
     54     return parseInt(hash.substr(startPosition, octets), 16);
     55 }
     56 
     57 function decToHex(v) {
     58     v |= 0; // Ensure integer value
     59     return v < 0 ? "00" :
     60         v < 16 ? "0" + v.toString(16) :
     61         v < 256 ? v.toString(16) :
     62         "ff";
     63 }
     64 
     65 function hueToRgb(m1, m2, h) {
     66     h = h < 0 ? h + 6 : h > 6 ? h - 6 : h;
     67     return decToHex(255 * (
     68         h < 1 ? m1 + (m2 - m1) * h :
     69         h < 3 ? m2 :
     70         h < 4 ? m1 + (m2 - m1) * (4 - h) :
     71         m1));
     72 }
     73 
     74 /**
     75  * @param {string} color  Color value to parse. Currently hexadecimal strings on the format #rgb[a] and #rrggbb[aa] are supported.
     76  * @returns {string}
     77  */
     78 function parseColor(color) {
     79     if (/^#[0-9a-f]{3,8}$/i.test(color)) {
     80         var result;
     81         var colorLength = color.length;
     82 
     83         if (colorLength < 6) {
     84             var r = color[1],
     85                   g = color[2],
     86                   b = color[3],
     87                   a = color[4] || "";
     88             result = "#" + r + r + g + g + b + b + a + a;
     89         }
     90         if (colorLength == 7 || colorLength > 8) {
     91             result = color;
     92         }
     93         
     94         return result;
     95     }
     96 }
     97 
     98 /**
     99  * Converts a hexadecimal color to a CSS3 compatible color.
    100  * @param {string} hexColor  Color on the format "#RRGGBB" or "#RRGGBBAA"
    101  * @returns {string}
    102  */
    103 function toCss3Color(hexColor) {
    104     var a = parseHex(hexColor, 7, 2);
    105     var result;
    106 
    107     if (isNaN(a)) {
    108         result = hexColor;
    109     } else {
    110         var r = parseHex(hexColor, 1, 2),
    111             g = parseHex(hexColor, 3, 2),
    112             b = parseHex(hexColor, 5, 2);
    113         result = "rgba(" + r + "," + g + "," + b + "," + (a / 255).toFixed(2) + ")";
    114     }
    115 
    116     return result;
    117 }
    118 
    119 /**
    120  * Converts an HSL color to a hexadecimal RGB color.
    121  * @param {number} hue  Hue in range [0, 1]
    122  * @param {number} saturation  Saturation in range [0, 1]
    123  * @param {number} lightness  Lightness in range [0, 1]
    124  * @returns {string}
    125  */
    126 function hsl(hue, saturation, lightness) {
    127     // Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color
    128     var result;
    129 
    130     if (saturation == 0) {
    131         var partialHex = decToHex(lightness * 255);
    132         result = partialHex + partialHex + partialHex;
    133     }
    134     else {
    135         var m2 = lightness <= 0.5 ? lightness * (saturation + 1) : lightness + saturation - lightness * saturation,
    136               m1 = lightness * 2 - m2;
    137         result =
    138             hueToRgb(m1, m2, hue * 6 + 2) +
    139             hueToRgb(m1, m2, hue * 6) +
    140             hueToRgb(m1, m2, hue * 6 - 2);
    141     }
    142 
    143     return "#" + result;
    144 }
    145 
    146 /**
    147  * Converts an HSL color to a hexadecimal RGB color. This function will correct the lightness for the "dark" hues
    148  * @param {number} hue  Hue in range [0, 1]
    149  * @param {number} saturation  Saturation in range [0, 1]
    150  * @param {number} lightness  Lightness in range [0, 1]
    151  * @returns {string}
    152  */
    153 function correctedHsl(hue, saturation, lightness) {
    154     // The corrector specifies the perceived middle lightness for each hue
    155     var correctors = [ 0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55 ],
    156           corrector = correctors[(hue * 6 + 0.5) | 0];
    157     
    158     // Adjust the input lightness relative to the corrector
    159     lightness = lightness < 0.5 ? lightness * corrector * 2 : corrector + (lightness - 0.5) * (1 - corrector) * 2;
    160     
    161     return hsl(hue, saturation, lightness);
    162 }
    163 
    164 /* global umdGlobal */
    165 
    166 // In the future we can replace `GLOBAL` with `globalThis`, but for now use the old school global detection for
    167 // backward compatibility.
    168 var GLOBAL = umdGlobal;
    169 
    170 /**
    171  * @typedef {Object} ParsedConfiguration
    172  * @property {number} colorSaturation
    173  * @property {number} grayscaleSaturation
    174  * @property {string} backColor
    175  * @property {number} iconPadding
    176  * @property {function(number):number} hue
    177  * @property {function(number):number} colorLightness
    178  * @property {function(number):number} grayscaleLightness
    179  */
    180 
    181 var CONFIG_PROPERTIES = {
    182     G/*GLOBAL*/: "jdenticon_config",
    183     n/*MODULE*/: "config",
    184 };
    185 
    186 var rootConfigurationHolder = {};
    187 
    188 /**
    189  * Defines the deprecated `config` property on the root Jdenticon object without printing a warning in the console
    190  * when it is being used.
    191  * @param {!Object} rootObject 
    192  */
    193 function defineConfigProperty(rootObject) {
    194     rootConfigurationHolder = rootObject;
    195 }
    196 
    197 /**
    198  * Sets a new icon style configuration. The new configuration is not merged with the previous one. * 
    199  * @param {Object} newConfiguration - New configuration object.
    200  */
    201 function configure(newConfiguration) {
    202     if (arguments.length) {
    203         rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/] = newConfiguration;
    204     }
    205     return rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/];
    206 }
    207 
    208 /**
    209  * Gets the normalized current Jdenticon color configuration. Missing fields have default values.
    210  * @param {Object|number|undefined} paddingOrLocalConfig - Configuration passed to the called API method. A
    211  *    local configuration overrides the global configuration in it entirety. This parameter can for backward
    212  *    compatibility also contain a padding value. A padding value only overrides the global padding, not the
    213  *    entire global configuration.
    214  * @param {number} defaultPadding - Padding used if no padding is specified in neither the configuration nor
    215  *    explicitly to the API method.
    216  * @returns {ParsedConfiguration}
    217  */
    218 function getConfiguration(paddingOrLocalConfig, defaultPadding) {
    219     var configObject = 
    220             typeof paddingOrLocalConfig == "object" && paddingOrLocalConfig ||
    221             rootConfigurationHolder[CONFIG_PROPERTIES.n/*MODULE*/] ||
    222             GLOBAL[CONFIG_PROPERTIES.G/*GLOBAL*/] ||
    223             { },
    224 
    225         lightnessConfig = configObject["lightness"] || { },
    226         
    227         // In versions < 2.1.0 there was no grayscale saturation -
    228         // saturation was the color saturation.
    229         saturation = configObject["saturation"] || { },
    230         colorSaturation = "color" in saturation ? saturation["color"] : saturation,
    231         grayscaleSaturation = saturation["grayscale"],
    232 
    233         backColor = configObject["backColor"],
    234         padding = configObject["padding"];
    235     
    236     /**
    237      * Creates a lightness range.
    238      */
    239     function lightness(configName, defaultRange) {
    240         var range = lightnessConfig[configName];
    241         
    242         // Check if the lightness range is an array-like object. This way we ensure the
    243         // array contain two values at the same time.
    244         if (!(range && range.length > 1)) {
    245             range = defaultRange;
    246         }
    247 
    248         /**
    249          * Gets a lightness relative the specified value in the specified lightness range.
    250          */
    251         return function (value) {
    252             value = range[0] + value * (range[1] - range[0]);
    253             return value < 0 ? 0 : value > 1 ? 1 : value;
    254         };
    255     }
    256 
    257     /**
    258      * Gets a hue allowed by the configured hue restriction,
    259      * provided the originally computed hue.
    260      */
    261     function hueFunction(originalHue) {
    262         var hueConfig = configObject["hues"];
    263         var hue;
    264         
    265         // Check if 'hues' is an array-like object. This way we also ensure that
    266         // the array is not empty, which would mean no hue restriction.
    267         if (hueConfig && hueConfig.length > 0) {
    268             // originalHue is in the range [0, 1]
    269             // Multiply with 0.999 to change the range to [0, 1) and then truncate the index.
    270             hue = hueConfig[0 | (0.999 * originalHue * hueConfig.length)];
    271         }
    272 
    273         return typeof hue == "number" ?
    274             
    275             // A hue was specified. We need to convert the hue from
    276             // degrees on any turn - e.g. 746° is a perfectly valid hue -
    277             // to turns in the range [0, 1).
    278             ((((hue / 360) % 1) + 1) % 1) :
    279 
    280             // No hue configured => use original hue
    281             originalHue;
    282     }
    283         
    284     return {
    285         X/*hue*/: hueFunction,
    286         p/*colorSaturation*/: typeof colorSaturation == "number" ? colorSaturation : 0.5,
    287         H/*grayscaleSaturation*/: typeof grayscaleSaturation == "number" ? grayscaleSaturation : 0,
    288         q/*colorLightness*/: lightness("color", [0.4, 0.8]),
    289         I/*grayscaleLightness*/: lightness("grayscale", [0.3, 0.9]),
    290         J/*backColor*/: parseColor(backColor),
    291         Y/*iconPadding*/: 
    292             typeof paddingOrLocalConfig == "number" ? paddingOrLocalConfig : 
    293             typeof padding == "number" ? padding : 
    294             defaultPadding
    295     }
    296 }
    297 
    298 var ICON_TYPE_SVG = 1;
    299 
    300 var ICON_TYPE_CANVAS = 2;
    301 
    302 var ATTRIBUTES = {
    303     t/*HASH*/: "data-jdenticon-hash",
    304     o/*VALUE*/: "data-jdenticon-value"
    305 };
    306 
    307 var IS_RENDERED_PROPERTY = "jdenticonRendered";
    308 
    309 var ICON_SELECTOR = "[" + ATTRIBUTES.t/*HASH*/ +"],[" + ATTRIBUTES.o/*VALUE*/ +"]";
    310 
    311 var documentQuerySelectorAll = /** @type {!Function} */ (
    312     typeof document !== "undefined" && document.querySelectorAll.bind(document));
    313 
    314 function getIdenticonType(el) {
    315     if (el) {
    316         var tagName = el["tagName"];
    317 
    318         if (/^svg$/i.test(tagName)) {
    319             return ICON_TYPE_SVG;
    320         }
    321 
    322         if (/^canvas$/i.test(tagName) && "getContext" in el) {
    323             return ICON_TYPE_CANVAS;
    324         }
    325     }
    326 }
    327 
    328 function whenDocumentIsReady(/** @type {Function} */ callback) {
    329     function loadedHandler() {
    330         document.removeEventListener("DOMContentLoaded", loadedHandler);
    331         window.removeEventListener("load", loadedHandler);
    332         setTimeout(callback, 0); // Give scripts a chance to run
    333     }
    334     
    335     if (typeof document !== "undefined" &&
    336         typeof window !== "undefined" &&
    337         typeof setTimeout !== "undefined"
    338     ) {
    339         if (document.readyState === "loading") {
    340             document.addEventListener("DOMContentLoaded", loadedHandler);
    341             window.addEventListener("load", loadedHandler);
    342         } else {
    343             // Document already loaded. The load events above likely won't be raised
    344             setTimeout(callback, 0);
    345         }
    346     }
    347 }
    348 
    349 function observer(updateCallback) {
    350     if (typeof MutationObserver != "undefined") {
    351         var mutationObserver = new MutationObserver(function onmutation(mutations) {
    352             for (var mutationIndex = 0; mutationIndex < mutations.length; mutationIndex++) {
    353                 var mutation = mutations[mutationIndex];
    354                 var addedNodes = mutation.addedNodes;
    355         
    356                 for (var addedNodeIndex = 0; addedNodes && addedNodeIndex < addedNodes.length; addedNodeIndex++) {
    357                     var addedNode = addedNodes[addedNodeIndex];
    358         
    359                     // Skip other types of nodes than element nodes, since they might not support
    360                     // the querySelectorAll method => runtime error.
    361                     if (addedNode.nodeType == 1) {
    362                         if (getIdenticonType(addedNode)) {
    363                             updateCallback(addedNode);
    364                         }
    365                         else {
    366                             var icons = /** @type {Element} */(addedNode).querySelectorAll(ICON_SELECTOR);
    367                             for (var iconIndex = 0; iconIndex < icons.length; iconIndex++) {
    368                                 updateCallback(icons[iconIndex]);
    369                             }
    370                         }
    371                     }
    372                 }
    373                 
    374                 if (mutation.type == "attributes" && getIdenticonType(mutation.target)) {
    375                     updateCallback(mutation.target);
    376                 }
    377             }
    378         });
    379 
    380         mutationObserver.observe(document.body, {
    381             "childList": true,
    382             "attributes": true,
    383             "attributeFilter": [ATTRIBUTES.o/*VALUE*/, ATTRIBUTES.t/*HASH*/, "width", "height"],
    384             "subtree": true,
    385         });
    386     }
    387 }
    388 
    389 /**
    390  * Represents a point.
    391  */
    392 function Point(x, y) {
    393     this.x = x;
    394     this.y = y;
    395 }
    396 
    397 /**
    398  * Translates and rotates a point before being passed on to the canvas context. This was previously done by the canvas context itself, 
    399  * but this caused a rendering issue in Chrome on sizes > 256 where the rotation transformation of inverted paths was not done properly.
    400  */
    401 function Transform(x, y, size, rotation) {
    402     this.u/*_x*/ = x;
    403     this.v/*_y*/ = y;
    404     this.K/*_size*/ = size;
    405     this.Z/*_rotation*/ = rotation;
    406 }
    407 
    408 /**
    409  * Transforms the specified point based on the translation and rotation specification for this Transform.
    410  * @param {number} x x-coordinate
    411  * @param {number} y y-coordinate
    412  * @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.
    413  * @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.
    414  */
    415 Transform.prototype.L/*transformIconPoint*/ = function transformIconPoint (x, y, w, h) {
    416     var right = this.u/*_x*/ + this.K/*_size*/,
    417           bottom = this.v/*_y*/ + this.K/*_size*/,
    418           rotation = this.Z/*_rotation*/;
    419     return rotation === 1 ? new Point(right - y - (h || 0), this.v/*_y*/ + x) :
    420            rotation === 2 ? new Point(right - x - (w || 0), bottom - y - (h || 0)) :
    421            rotation === 3 ? new Point(this.u/*_x*/ + y, bottom - x - (w || 0)) :
    422            new Point(this.u/*_x*/ + x, this.v/*_y*/ + y);
    423 };
    424 
    425 var NO_TRANSFORM = new Transform(0, 0, 0, 0);
    426 
    427 
    428 
    429 /**
    430  * Provides helper functions for rendering common basic shapes.
    431  */
    432 function Graphics(renderer) {
    433     /**
    434      * @type {Renderer}
    435      * @private
    436      */
    437     this.M/*_renderer*/ = renderer;
    438 
    439     /**
    440      * @type {Transform}
    441      */
    442     this.A/*currentTransform*/ = NO_TRANSFORM;
    443 }
    444 var Graphics__prototype = Graphics.prototype;
    445 
    446 /**
    447  * Adds a polygon to the underlying renderer.
    448  * @param {Array<number>} points The points of the polygon clockwise on the format [ x0, y0, x1, y1, ..., xn, yn ]
    449  * @param {boolean=} invert Specifies if the polygon will be inverted.
    450  */
    451 Graphics__prototype.g/*addPolygon*/ = function addPolygon (points, invert) {
    452         var this$1 = this;
    453 
    454     var di = invert ? -2 : 2,
    455           transformedPoints = [];
    456         
    457     for (var i = invert ? points.length - 2 : 0; i < points.length && i >= 0; i += di) {
    458         transformedPoints.push(this$1.A/*currentTransform*/.L/*transformIconPoint*/(points[i], points[i + 1]));
    459     }
    460         
    461     this.M/*_renderer*/.g/*addPolygon*/(transformedPoints);
    462 };
    463     
    464 /**
    465  * Adds a polygon to the underlying renderer.
    466  * Source: http://stackoverflow.com/a/2173084
    467  * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the entire ellipse.
    468  * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the entire ellipse.
    469  * @param {number} size The size of the ellipse.
    470  * @param {boolean=} invert Specifies if the ellipse will be inverted.
    471  */
    472 Graphics__prototype.h/*addCircle*/ = function addCircle (x, y, size, invert) {
    473     var p = this.A/*currentTransform*/.L/*transformIconPoint*/(x, y, size, size);
    474     this.M/*_renderer*/.h/*addCircle*/(p, size, invert);
    475 };
    476 
    477 /**
    478  * Adds a rectangle to the underlying renderer.
    479  * @param {number} x The x-coordinate of the upper left corner of the rectangle.
    480  * @param {number} y The y-coordinate of the upper left corner of the rectangle.
    481  * @param {number} w The width of the rectangle.
    482  * @param {number} h The height of the rectangle.
    483  * @param {boolean=} invert Specifies if the rectangle will be inverted.
    484  */
    485 Graphics__prototype.i/*addRectangle*/ = function addRectangle (x, y, w, h, invert) {
    486     this.g/*addPolygon*/([
    487         x, y, 
    488         x + w, y,
    489         x + w, y + h,
    490         x, y + h
    491     ], invert);
    492 };
    493 
    494 /**
    495  * Adds a right triangle to the underlying renderer.
    496  * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the triangle.
    497  * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the triangle.
    498  * @param {number} w The width of the triangle.
    499  * @param {number} h The height of the triangle.
    500  * @param {number} r The rotation of the triangle (clockwise). 0 = right corner of the triangle in the lower left corner of the bounding rectangle.
    501  * @param {boolean=} invert Specifies if the triangle will be inverted.
    502  */
    503 Graphics__prototype.j/*addTriangle*/ = function addTriangle (x, y, w, h, r, invert) {
    504     var points = [
    505         x + w, y, 
    506         x + w, y + h, 
    507         x, y + h,
    508         x, y
    509     ];
    510     points.splice(((r || 0) % 4) * 2, 2);
    511     this.g/*addPolygon*/(points, invert);
    512 };
    513 
    514 /**
    515  * Adds a rhombus to the underlying renderer.
    516  * @param {number} x The x-coordinate of the upper left corner of the rectangle holding the rhombus.
    517  * @param {number} y The y-coordinate of the upper left corner of the rectangle holding the rhombus.
    518  * @param {number} w The width of the rhombus.
    519  * @param {number} h The height of the rhombus.
    520  * @param {boolean=} invert Specifies if the rhombus will be inverted.
    521  */
    522 Graphics__prototype.N/*addRhombus*/ = function addRhombus (x, y, w, h, invert) {
    523     this.g/*addPolygon*/([
    524         x + w / 2, y,
    525         x + w, y + h / 2,
    526         x + w / 2, y + h,
    527         x, y + h / 2
    528     ], invert);
    529 };
    530 
    531 /**
    532  * @param {number} index
    533  * @param {Graphics} g
    534  * @param {number} cell
    535  * @param {number} positionIndex
    536  */
    537 function centerShape(index, g, cell, positionIndex) {
    538     index = index % 14;
    539 
    540     var k, m, w, h, inner, outer;
    541 
    542     !index ? (
    543         k = cell * 0.42,
    544         g.g/*addPolygon*/([
    545             0, 0,
    546             cell, 0,
    547             cell, cell - k * 2,
    548             cell - k, cell,
    549             0, cell
    550         ])) :
    551 
    552     index == 1 ? (
    553         w = 0 | (cell * 0.5), 
    554         h = 0 | (cell * 0.8),
    555 
    556         g.j/*addTriangle*/(cell - w, 0, w, h, 2)) :
    557 
    558     index == 2 ? (
    559         w = 0 | (cell / 3),
    560         g.i/*addRectangle*/(w, w, cell - w, cell - w)) :
    561 
    562     index == 3 ? (
    563         inner = cell * 0.1,
    564         // Use fixed outer border widths in small icons to ensure the border is drawn
    565         outer = 
    566             cell < 6 ? 1 :
    567             cell < 8 ? 2 :
    568             (0 | (cell * 0.25)),
    569         
    570         inner = 
    571             inner > 1 ? (0 | inner) : // large icon => truncate decimals
    572             inner > 0.5 ? 1 :         // medium size icon => fixed width
    573             inner,                    // small icon => anti-aliased border
    574 
    575         g.i/*addRectangle*/(outer, outer, cell - inner - outer, cell - inner - outer)) :
    576 
    577     index == 4 ? (
    578         m = 0 | (cell * 0.15),
    579         w = 0 | (cell * 0.5),
    580         g.h/*addCircle*/(cell - w - m, cell - w - m, w)) :
    581 
    582     index == 5 ? (
    583         inner = cell * 0.1,
    584         outer = inner * 4,
    585 
    586         // Align edge to nearest pixel in large icons
    587         outer > 3 && (outer = 0 | outer),
    588         
    589         g.i/*addRectangle*/(0, 0, cell, cell),
    590         g.g/*addPolygon*/([
    591             outer, outer,
    592             cell - inner, outer,
    593             outer + (cell - outer - inner) / 2, cell - inner
    594         ], true)) :
    595 
    596     index == 6 ? 
    597         g.g/*addPolygon*/([
    598             0, 0,
    599             cell, 0,
    600             cell, cell * 0.7,
    601             cell * 0.4, cell * 0.4,
    602             cell * 0.7, cell,
    603             0, cell
    604         ]) :
    605 
    606     index == 7 ? 
    607         g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 3) :
    608 
    609     index == 8 ? (
    610         g.i/*addRectangle*/(0, 0, cell, cell / 2),
    611         g.i/*addRectangle*/(0, cell / 2, cell / 2, cell / 2),
    612         g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 1)) :
    613 
    614     index == 9 ? (
    615         inner = cell * 0.14,
    616         // Use fixed outer border widths in small icons to ensure the border is drawn
    617         outer = 
    618             cell < 4 ? 1 :
    619             cell < 6 ? 2 :
    620             (0 | (cell * 0.35)),
    621 
    622         inner = 
    623             cell < 8 ? inner : // small icon => anti-aliased border
    624             (0 | inner),       // large icon => truncate decimals
    625 
    626         g.i/*addRectangle*/(0, 0, cell, cell),
    627         g.i/*addRectangle*/(outer, outer, cell - outer - inner, cell - outer - inner, true)) :
    628 
    629     index == 10 ? (
    630         inner = cell * 0.12,
    631         outer = inner * 3,
    632 
    633         g.i/*addRectangle*/(0, 0, cell, cell),
    634         g.h/*addCircle*/(outer, outer, cell - inner - outer, true)) :
    635 
    636     index == 11 ? 
    637         g.j/*addTriangle*/(cell / 2, cell / 2, cell / 2, cell / 2, 3) :
    638 
    639     index == 12 ? (
    640         m = cell * 0.25,
    641         g.i/*addRectangle*/(0, 0, cell, cell),
    642         g.N/*addRhombus*/(m, m, cell - m, cell - m, true)) :
    643 
    644     // 13
    645     (
    646         !positionIndex && (
    647             m = cell * 0.4, w = cell * 1.2,
    648             g.h/*addCircle*/(m, m, w)
    649         )
    650     );
    651 }
    652 
    653 /**
    654  * @param {number} index
    655  * @param {Graphics} g
    656  * @param {number} cell
    657  */
    658 function outerShape(index, g, cell) {
    659     index = index % 4;
    660 
    661     var m;
    662 
    663     !index ?
    664         g.j/*addTriangle*/(0, 0, cell, cell, 0) :
    665         
    666     index == 1 ?
    667         g.j/*addTriangle*/(0, cell / 2, cell, cell / 2, 0) :
    668 
    669     index == 2 ?
    670         g.N/*addRhombus*/(0, 0, cell, cell) :
    671 
    672     // 3
    673     (
    674         m = cell / 6,
    675         g.h/*addCircle*/(m, m, cell - 2 * m)
    676     );
    677 }
    678 
    679 /**
    680  * Gets a set of identicon color candidates for a specified hue and config.
    681  * @param {number} hue
    682  * @param {ParsedConfiguration} config
    683  */
    684 function colorTheme(hue, config) {
    685     hue = config.X/*hue*/(hue);
    686     return [
    687         // Dark gray
    688         correctedHsl(hue, config.H/*grayscaleSaturation*/, config.I/*grayscaleLightness*/(0)),
    689         // Mid color
    690         correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(0.5)),
    691         // Light gray
    692         correctedHsl(hue, config.H/*grayscaleSaturation*/, config.I/*grayscaleLightness*/(1)),
    693         // Light color
    694         correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(1)),
    695         // Dark color
    696         correctedHsl(hue, config.p/*colorSaturation*/, config.q/*colorLightness*/(0))
    697     ];
    698 }
    699 
    700 /**
    701  * Draws an identicon to a specified renderer.
    702  * @param {Renderer} renderer
    703  * @param {string} hash
    704  * @param {Object|number=} config
    705  */
    706 function iconGenerator(renderer, hash, config) {
    707     var parsedConfig = getConfiguration(config, 0.08);
    708 
    709     // Set background color
    710     if (parsedConfig.J/*backColor*/) {
    711         renderer.m/*setBackground*/(parsedConfig.J/*backColor*/);
    712     }
    713     
    714     // Calculate padding and round to nearest integer
    715     var size = renderer.k/*iconSize*/;
    716     var padding = (0.5 + size * parsedConfig.Y/*iconPadding*/) | 0;
    717     size -= padding * 2;
    718     
    719     var graphics = new Graphics(renderer);
    720     
    721     // Calculate cell size and ensure it is an integer
    722     var cell = 0 | (size / 4);
    723     
    724     // Since the cell size is integer based, the actual icon will be slightly smaller than specified => center icon
    725     var x = 0 | (padding + size / 2 - cell * 2);
    726     var y = 0 | (padding + size / 2 - cell * 2);
    727 
    728     function renderShape(colorIndex, shapes, index, rotationIndex, positions) {
    729         var shapeIndex = parseHex(hash, index, 1);
    730         var r = rotationIndex ? parseHex(hash, rotationIndex, 1) : 0;
    731         
    732         renderer.O/*beginShape*/(availableColors[selectedColorIndexes[colorIndex]]);
    733         
    734         for (var i = 0; i < positions.length; i++) {
    735             graphics.A/*currentTransform*/ = new Transform(x + positions[i][0] * cell, y + positions[i][1] * cell, cell, r++ % 4);
    736             shapes(shapeIndex, graphics, cell, i);
    737         }
    738         
    739         renderer.P/*endShape*/();
    740     }
    741 
    742     // AVAILABLE COLORS
    743     var hue = parseHex(hash, -7) / 0xfffffff,
    744     
    745           // Available colors for this icon
    746           availableColors = colorTheme(hue, parsedConfig),
    747 
    748           // The index of the selected colors
    749           selectedColorIndexes = [];
    750 
    751     var index;
    752 
    753     function isDuplicate(values) {
    754         if (values.indexOf(index) >= 0) {
    755             for (var i = 0; i < values.length; i++) {
    756                 if (selectedColorIndexes.indexOf(values[i]) >= 0) {
    757                     return true;
    758                 }
    759             }
    760         }
    761     }
    762 
    763     for (var i = 0; i < 3; i++) {
    764         index = parseHex(hash, 8 + i, 1) % availableColors.length;
    765         if (isDuplicate([0, 4]) || // Disallow dark gray and dark color combo
    766             isDuplicate([2, 3])) { // Disallow light gray and light color combo
    767             index = 1;
    768         }
    769         selectedColorIndexes.push(index);
    770     }
    771 
    772     // ACTUAL RENDERING
    773     // Sides
    774     renderShape(0, outerShape, 2, 3, [[1, 0], [2, 0], [2, 3], [1, 3], [0, 1], [3, 1], [3, 2], [0, 2]]);
    775     // Corners
    776     renderShape(1, outerShape, 4, 5, [[0, 0], [3, 0], [3, 3], [0, 3]]);
    777     // Center
    778     renderShape(2, centerShape, 1, null, [[1, 1], [2, 1], [2, 2], [1, 2]]);
    779     
    780     renderer.finish();
    781 }
    782 
    783 /**
    784  * Computes a SHA1 hash for any value and returns it as a hexadecimal string.
    785  * 
    786  * This function is optimized for minimal code size and rather short messages.
    787  * 
    788  * @param {string} message 
    789  */
    790 function sha1(message) {
    791     var HASH_SIZE_HALF_BYTES = 40;
    792     var BLOCK_SIZE_WORDS = 16;
    793 
    794     // Variables
    795     // `var` is used to be able to minimize the number of `var` keywords.
    796     var i = 0,
    797         f = 0,
    798     
    799         // Use `encodeURI` to UTF8 encode the message without any additional libraries
    800         // We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky
    801         // since `unescape` is deprecated.
    802         urlEncodedMessage = encodeURI(message) + "%80", // trailing '1' bit padding
    803         
    804         // This can be changed to a preallocated Uint32Array array for greater performance and larger code size
    805         data = [],
    806         dataSize,
    807         
    808         hashBuffer = [],
    809 
    810         a = 0x67452301,
    811         b = 0xefcdab89,
    812         c = ~a,
    813         d = ~b,
    814         e = 0xc3d2e1f0,
    815         hash = [a, b, c, d, e],
    816 
    817         blockStartIndex = 0,
    818         hexHash = "";
    819 
    820     /**
    821      * Rotates the value a specified number of bits to the left.
    822      * @param {number} value  Value to rotate
    823      * @param {number} shift  Bit count to shift.
    824      */
    825     function rotl(value, shift) {
    826         return (value << shift) | (value >>> (32 - shift));
    827     }
    828 
    829     // Message data
    830     for ( ; i < urlEncodedMessage.length; f++) {
    831         data[f >> 2] = data[f >> 2] |
    832             (
    833                 (
    834                     urlEncodedMessage[i] == "%"
    835                         // Percent encoded byte
    836                         ? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16)
    837                         // Unencoded byte
    838                         : urlEncodedMessage.charCodeAt(i++)
    839                 )
    840 
    841                 // Read bytes in reverse order (big endian words)
    842                 << ((3 - (f & 3)) * 8)
    843             );
    844     }
    845 
    846     // f is now the length of the utf8 encoded message
    847     // 7 = 8 bytes (64 bit) for message size, -1 to round down
    848     // >> 6 = integer division with block size
    849     dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS;
    850 
    851     // Message size in bits.
    852     // SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least
    853     // significant 32 bits are set. -8 is for the '1' bit padding byte.
    854     data[dataSize - 1] = f * 8 - 8;
    855     
    856     // Compute hash
    857     for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) {
    858         for (i = 0; i < 80; i++) {
    859             f = rotl(a, 5) + e + (
    860                     // Ch
    861                     i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 :
    862                     
    863                     // Parity
    864                     i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 :
    865                     
    866                     // Maj
    867                     i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc :
    868                     
    869                     // Parity
    870                     (b ^ c ^ d) + 0xca62c1d6
    871                 ) + ( 
    872                     hashBuffer[i] = i < BLOCK_SIZE_WORDS
    873                         // Bitwise OR is used to coerse `undefined` to 0
    874                         ? (data[blockStartIndex + i] | 0)
    875                         : rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1)
    876                 );
    877 
    878             e = d;
    879             d = c;
    880             c = rotl(b, 30);
    881             b = a;
    882             a = f;
    883         }
    884 
    885         hash[0] = a = ((hash[0] + a) | 0);
    886         hash[1] = b = ((hash[1] + b) | 0);
    887         hash[2] = c = ((hash[2] + c) | 0);
    888         hash[3] = d = ((hash[3] + d) | 0);
    889         hash[4] = e = ((hash[4] + e) | 0);
    890     }
    891 
    892     // Format hex hash
    893     for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) {
    894         hexHash += (
    895             (
    896                 // Get word (2^3 half-bytes per word)
    897                 hash[i >> 3] >>>
    898 
    899                 // Append half-bytes in reverse order
    900                 ((7 - (i & 7)) * 4)
    901             ) 
    902             // Clamp to half-byte
    903             & 0xf
    904         ).toString(16);
    905     }
    906 
    907     return hexHash;
    908 }
    909 
    910 /**
    911  * Inputs a value that might be a valid hash string for Jdenticon and returns it 
    912  * if it is determined valid, otherwise a falsy value is returned.
    913  */
    914 function isValidHash(hashCandidate) {
    915     return /^[0-9a-f]{11,}$/i.test(hashCandidate) && hashCandidate;
    916 }
    917 
    918 /**
    919  * Computes a hash for the specified value. Currently SHA1 is used. This function
    920  * always returns a valid hash.
    921  */
    922 function computeHash(value) {
    923     return sha1(value == null ? "" : "" + value);
    924 }
    925 
    926 
    927 
    928 /**
    929  * Renderer redirecting drawing commands to a canvas context.
    930  * @implements {Renderer}
    931  */
    932 function CanvasRenderer(ctx, iconSize) {
    933     var canvas = ctx.canvas; 
    934     var width = canvas.width;
    935     var height = canvas.height;
    936         
    937     ctx.save();
    938         
    939     if (!iconSize) {
    940         iconSize = Math.min(width, height);
    941             
    942         ctx.translate(
    943             ((width - iconSize) / 2) | 0,
    944             ((height - iconSize) / 2) | 0);
    945     }
    946 
    947     /**
    948      * @private
    949      */
    950     this.l/*_ctx*/ = ctx;
    951     this.k/*iconSize*/ = iconSize;
    952         
    953     ctx.clearRect(0, 0, iconSize, iconSize);
    954 }
    955 var CanvasRenderer__prototype = CanvasRenderer.prototype;
    956 
    957 /**
    958  * Fills the background with the specified color.
    959  * @param {string} fillColor  Fill color on the format #rrggbb[aa].
    960  */
    961 CanvasRenderer__prototype.m/*setBackground*/ = function setBackground (fillColor) {
    962     var ctx = this.l/*_ctx*/;
    963     var iconSize = this.k/*iconSize*/;
    964 
    965     ctx.fillStyle = toCss3Color(fillColor);
    966     ctx.fillRect(0, 0, iconSize, iconSize);
    967 };
    968 
    969 /**
    970  * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.
    971  * @param {string} fillColor Fill color on format #rrggbb[aa].
    972  */
    973 CanvasRenderer__prototype.O/*beginShape*/ = function beginShape (fillColor) {
    974     var ctx = this.l/*_ctx*/;
    975     ctx.fillStyle = toCss3Color(fillColor);
    976     ctx.beginPath();
    977 };
    978 
    979 /**
    980  * Marks the end of the currently drawn shape. This causes the queued paths to be rendered on the canvas.
    981  */
    982 CanvasRenderer__prototype.P/*endShape*/ = function endShape () {
    983     this.l/*_ctx*/.fill();
    984 };
    985 
    986 /**
    987  * Adds a polygon to the rendering queue.
    988  * @param points An array of Point objects.
    989  */
    990 CanvasRenderer__prototype.g/*addPolygon*/ = function addPolygon (points) {
    991     var ctx = this.l/*_ctx*/;
    992     ctx.moveTo(points[0].x, points[0].y);
    993     for (var i = 1; i < points.length; i++) {
    994         ctx.lineTo(points[i].x, points[i].y);
    995     }
    996     ctx.closePath();
    997 };
    998 
    999 /**
   1000  * Adds a circle to the rendering queue.
   1001  * @param {Point} point The upper left corner of the circle bounding box.
   1002  * @param {number} diameter The diameter of the circle.
   1003  * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).
   1004  */
   1005 CanvasRenderer__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) {
   1006     var ctx = this.l/*_ctx*/,
   1007           radius = diameter / 2;
   1008     ctx.moveTo(point.x + radius, point.y + radius);
   1009     ctx.arc(point.x + radius, point.y + radius, radius, 0, Math.PI * 2, counterClockwise);
   1010     ctx.closePath();
   1011 };
   1012 
   1013 /**
   1014  * Called when the icon has been completely drawn.
   1015  */
   1016 CanvasRenderer__prototype.finish = function finish () {
   1017     this.l/*_ctx*/.restore();
   1018 };
   1019 
   1020 /**
   1021  * Draws an identicon to a context.
   1022  * @param {CanvasRenderingContext2D} ctx - Canvas context on which the icon will be drawn at location (0, 0).
   1023  * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.
   1024  * @param {number} size - Icon size in pixels.
   1025  * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any
   1026  *    global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be
   1027  *    specified in place of a configuration object.
   1028  */
   1029 function drawIcon(ctx, hashOrValue, size, config) {
   1030     if (!ctx) {
   1031         throw new Error("No canvas specified.");
   1032     }
   1033     
   1034     iconGenerator(new CanvasRenderer(ctx, size), 
   1035         isValidHash(hashOrValue) || computeHash(hashOrValue), 
   1036         config);
   1037 
   1038     var canvas = ctx.canvas;
   1039     if (canvas) {
   1040         canvas[IS_RENDERED_PROPERTY] = true;
   1041     }
   1042 }
   1043 
   1044 /**
   1045  * Prepares a measure to be used as a measure in an SVG path, by
   1046  * rounding the measure to a single decimal. This reduces the file
   1047  * size of the generated SVG with more than 50% in some cases.
   1048  */
   1049 function svgValue(value) {
   1050     return ((value * 10 + 0.5) | 0) / 10;
   1051 }
   1052 
   1053 /**
   1054  * Represents an SVG path element.
   1055  */
   1056 function SvgPath() {
   1057     /**
   1058      * This property holds the data string (path.d) of the SVG path.
   1059      * @type {string}
   1060      */
   1061     this.B/*dataString*/ = "";
   1062 }
   1063 var SvgPath__prototype = SvgPath.prototype;
   1064 
   1065 /**
   1066  * Adds a polygon with the current fill color to the SVG path.
   1067  * @param points An array of Point objects.
   1068  */
   1069 SvgPath__prototype.g/*addPolygon*/ = function addPolygon (points) {
   1070     var dataString = "";
   1071     for (var i = 0; i < points.length; i++) {
   1072         dataString += (i ? "L" : "M") + svgValue(points[i].x) + " " + svgValue(points[i].y);
   1073     }
   1074     this.B/*dataString*/ += dataString + "Z";
   1075 };
   1076 
   1077 /**
   1078  * Adds a circle with the current fill color to the SVG path.
   1079  * @param {Point} point The upper left corner of the circle bounding box.
   1080  * @param {number} diameter The diameter of the circle.
   1081  * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).
   1082  */
   1083 SvgPath__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) {
   1084     var sweepFlag = counterClockwise ? 0 : 1,
   1085           svgRadius = svgValue(diameter / 2),
   1086           svgDiameter = svgValue(diameter),
   1087           svgArc = "a" + svgRadius + "," + svgRadius + " 0 1," + sweepFlag + " ";
   1088             
   1089     this.B/*dataString*/ += 
   1090         "M" + svgValue(point.x) + " " + svgValue(point.y + diameter / 2) +
   1091         svgArc + svgDiameter + ",0" + 
   1092         svgArc + (-svgDiameter) + ",0";
   1093 };
   1094 
   1095 
   1096 
   1097 /**
   1098  * Renderer producing SVG output.
   1099  * @implements {Renderer}
   1100  */
   1101 function SvgRenderer(target) {
   1102     /**
   1103      * @type {SvgPath}
   1104      * @private
   1105      */
   1106     this.C/*_path*/;
   1107 
   1108     /**
   1109      * @type {Object.<string,SvgPath>}
   1110      * @private
   1111      */
   1112     this.D/*_pathsByColor*/ = { };
   1113 
   1114     /**
   1115      * @type {SvgElement|SvgWriter}
   1116      * @private
   1117      */
   1118     this.R/*_target*/ = target;
   1119 
   1120     /**
   1121      * @type {number}
   1122      */
   1123     this.k/*iconSize*/ = target.k/*iconSize*/;
   1124 }
   1125 var SvgRenderer__prototype = SvgRenderer.prototype;
   1126 
   1127 /**
   1128  * Fills the background with the specified color.
   1129  * @param {string} fillColor  Fill color on the format #rrggbb[aa].
   1130  */
   1131 SvgRenderer__prototype.m/*setBackground*/ = function setBackground (fillColor) {
   1132     var match = /^(#......)(..)?/.exec(fillColor),
   1133           opacity = match[2] ? parseHex(match[2], 0) / 255 : 1;
   1134     this.R/*_target*/.m/*setBackground*/(match[1], opacity);
   1135 };
   1136 
   1137 /**
   1138  * Marks the beginning of a new shape of the specified color. Should be ended with a call to endShape.
   1139  * @param {string} color Fill color on format #xxxxxx.
   1140  */
   1141 SvgRenderer__prototype.O/*beginShape*/ = function beginShape (color) {
   1142     this.C/*_path*/ = this.D/*_pathsByColor*/[color] || (this.D/*_pathsByColor*/[color] = new SvgPath());
   1143 };
   1144 
   1145 /**
   1146  * Marks the end of the currently drawn shape.
   1147  */
   1148 SvgRenderer__prototype.P/*endShape*/ = function endShape () { };
   1149 
   1150 /**
   1151  * Adds a polygon with the current fill color to the SVG.
   1152  * @param points An array of Point objects.
   1153  */
   1154 SvgRenderer__prototype.g/*addPolygon*/ = function addPolygon (points) {
   1155     this.C/*_path*/.g/*addPolygon*/(points);
   1156 };
   1157 
   1158 /**
   1159  * Adds a circle with the current fill color to the SVG.
   1160  * @param {Point} point The upper left corner of the circle bounding box.
   1161  * @param {number} diameter The diameter of the circle.
   1162  * @param {boolean} counterClockwise True if the circle is drawn counter-clockwise (will result in a hole if rendered on a clockwise path).
   1163  */
   1164 SvgRenderer__prototype.h/*addCircle*/ = function addCircle (point, diameter, counterClockwise) {
   1165     this.C/*_path*/.h/*addCircle*/(point, diameter, counterClockwise);
   1166 };
   1167 
   1168 /**
   1169  * Called when the icon has been completely drawn.
   1170  */
   1171 SvgRenderer__prototype.finish = function finish () {
   1172         var this$1 = this;
   1173  
   1174     var pathsByColor = this.D/*_pathsByColor*/;
   1175     for (var color in pathsByColor) {
   1176         // hasOwnProperty cannot be shadowed in pathsByColor
   1177         // eslint-disable-next-line no-prototype-builtins
   1178         if (pathsByColor.hasOwnProperty(color)) {
   1179             this$1.R/*_target*/.S/*appendPath*/(color, pathsByColor[color].B/*dataString*/);
   1180         }
   1181     }
   1182 };
   1183 
   1184 var SVG_CONSTANTS = {
   1185     T/*XMLNS*/: "http://www.w3.org/2000/svg",
   1186     U/*WIDTH*/: "width",
   1187     V/*HEIGHT*/: "height",
   1188 };
   1189 
   1190 /**
   1191  * Renderer producing SVG output.
   1192  */
   1193 function SvgWriter(iconSize) {
   1194     /**
   1195      * @type {number}
   1196      */
   1197     this.k/*iconSize*/ = iconSize;
   1198 
   1199     /**
   1200      * @type {string}
   1201      * @private
   1202      */
   1203     this.F/*_s*/ =
   1204         '<svg xmlns="' + SVG_CONSTANTS.T/*XMLNS*/ + '" width="' + 
   1205         iconSize + '" height="' + iconSize + '" viewBox="0 0 ' + 
   1206         iconSize + ' ' + iconSize + '">';
   1207 }
   1208 var SvgWriter__prototype = SvgWriter.prototype;
   1209 
   1210 /**
   1211  * Fills the background with the specified color.
   1212  * @param {string} fillColor  Fill color on the format #rrggbb.
   1213  * @param {number} opacity  Opacity in the range [0.0, 1.0].
   1214  */
   1215 SvgWriter__prototype.m/*setBackground*/ = function setBackground (fillColor, opacity) {
   1216     if (opacity) {
   1217         this.F/*_s*/ += '<rect width="100%" height="100%" fill="' + 
   1218             fillColor + '" opacity="' + opacity.toFixed(2) + '"/>';
   1219     }
   1220 };
   1221 
   1222 /**
   1223  * Writes a path to the SVG string.
   1224  * @param {string} color Fill color on format #rrggbb.
   1225  * @param {string} dataString The SVG path data string.
   1226  */
   1227 SvgWriter__prototype.S/*appendPath*/ = function appendPath (color, dataString) {
   1228     this.F/*_s*/ += '<path fill="' + color + '" d="' + dataString + '"/>';
   1229 };
   1230 
   1231 /**
   1232  * Gets the rendered image as an SVG string.
   1233  */
   1234 SvgWriter__prototype.toString = function toString () {
   1235     return this.F/*_s*/ + "</svg>";
   1236 };
   1237 
   1238 /**
   1239  * Draws an identicon as an SVG string.
   1240  * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon.
   1241  * @param {number} size - Icon size in pixels.
   1242  * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any
   1243  *    global configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be
   1244  *    specified in place of a configuration object.
   1245  * @returns {string} SVG string
   1246  */
   1247 function toSvg(hashOrValue, size, config) {
   1248     var writer = new SvgWriter(size);
   1249     iconGenerator(new SvgRenderer(writer), 
   1250         isValidHash(hashOrValue) || computeHash(hashOrValue),
   1251         config);
   1252     return writer.toString();
   1253 }
   1254 
   1255 /**
   1256  * Creates a new element and adds it to the specified parent.
   1257  * @param {Element} parentNode
   1258  * @param {string} name
   1259  * @param {...(string|number)} keyValuePairs
   1260  */
   1261 function SvgElement_append(parentNode, name) {
   1262     var keyValuePairs = [], len = arguments.length - 2;
   1263     while ( len-- > 0 ) keyValuePairs[ len ] = arguments[ len + 2 ];
   1264 
   1265     var el = document.createElementNS(SVG_CONSTANTS.T/*XMLNS*/, name);
   1266     
   1267     for (var i = 0; i + 1 < keyValuePairs.length; i += 2) {
   1268         el.setAttribute(
   1269             /** @type {string} */(keyValuePairs[i]),
   1270             /** @type {string} */(keyValuePairs[i + 1])
   1271             );
   1272     }
   1273 
   1274     parentNode.appendChild(el);
   1275 }
   1276 
   1277 
   1278 /**
   1279  * Renderer producing SVG output.
   1280  */
   1281 function SvgElement(element) {
   1282     // Don't use the clientWidth and clientHeight properties on SVG elements
   1283     // since Firefox won't serve a proper value of these properties on SVG
   1284     // elements (https://bugzilla.mozilla.org/show_bug.cgi?id=874811)
   1285     // Instead use 100px as a hardcoded size (the svg viewBox will rescale 
   1286     // the icon to the correct dimensions)
   1287     var iconSize = this.k/*iconSize*/ = Math.min(
   1288         (Number(element.getAttribute(SVG_CONSTANTS.U/*WIDTH*/)) || 100),
   1289         (Number(element.getAttribute(SVG_CONSTANTS.V/*HEIGHT*/)) || 100)
   1290         );
   1291         
   1292     /**
   1293      * @type {Element}
   1294      * @private
   1295      */
   1296     this.W/*_el*/ = element;
   1297         
   1298     // Clear current SVG child elements
   1299     while (element.firstChild) {
   1300         element.removeChild(element.firstChild);
   1301     }
   1302         
   1303     // Set viewBox attribute to ensure the svg scales nicely.
   1304     element.setAttribute("viewBox", "0 0 " + iconSize + " " + iconSize);
   1305     element.setAttribute("preserveAspectRatio", "xMidYMid meet");
   1306 }
   1307 var SvgElement__prototype = SvgElement.prototype;
   1308 
   1309 /**
   1310  * Fills the background with the specified color.
   1311  * @param {string} fillColor  Fill color on the format #rrggbb.
   1312  * @param {number} opacity  Opacity in the range [0.0, 1.0].
   1313  */
   1314 SvgElement__prototype.m/*setBackground*/ = function setBackground (fillColor, opacity) {
   1315     if (opacity) {
   1316         SvgElement_append(this.W/*_el*/, "rect",
   1317             SVG_CONSTANTS.U/*WIDTH*/, "100%",
   1318             SVG_CONSTANTS.V/*HEIGHT*/, "100%",
   1319             "fill", fillColor,
   1320             "opacity", opacity);
   1321     }
   1322 };
   1323 
   1324 /**
   1325  * Appends a path to the SVG element.
   1326  * @param {string} color Fill color on format #xxxxxx.
   1327  * @param {string} dataString The SVG path data string.
   1328  */
   1329 SvgElement__prototype.S/*appendPath*/ = function appendPath (color, dataString) {
   1330     SvgElement_append(this.W/*_el*/, "path",
   1331         "fill", color,
   1332         "d", dataString);
   1333 };
   1334 
   1335 /**
   1336  * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute.
   1337  */
   1338 function updateAll() {
   1339     if (documentQuerySelectorAll) {
   1340         update(ICON_SELECTOR);
   1341     }
   1342 }
   1343 
   1344 /**
   1345  * Updates all canvas elements with the `data-jdenticon-hash` or `data-jdenticon-value` attribute that have not already
   1346  * been rendered.
   1347  */
   1348 function updateAllConditional() {
   1349     if (documentQuerySelectorAll) {
   1350         /** @type {NodeListOf<HTMLElement>} */
   1351         var elements = documentQuerySelectorAll(ICON_SELECTOR);
   1352         
   1353         for (var i = 0; i < elements.length; i++) {
   1354             var el = elements[i];
   1355             if (!el[IS_RENDERED_PROPERTY]) {
   1356                 update(el);
   1357             }
   1358         }
   1359     }
   1360 }
   1361 
   1362 /**
   1363  * Updates the identicon in the specified `<canvas>` or `<svg>` elements.
   1364  * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type
   1365  *    `<svg>` or `<canvas>`, or a CSS selector to such an element.
   1366  * @param {*=} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or
   1367  *    `data-jdenticon-value` attribute will be evaluated.
   1368  * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any
   1369  *    global configuration in its entirety. For backward compability a padding value in the range [0.0, 0.5) can be
   1370  *    specified in place of a configuration object.
   1371  */
   1372 function update(el, hashOrValue, config) {
   1373     renderDomElement(el, hashOrValue, config, function (el, iconType) {
   1374         if (iconType) {
   1375             return iconType == ICON_TYPE_SVG ? 
   1376                 new SvgRenderer(new SvgElement(el)) : 
   1377                 new CanvasRenderer(/** @type {HTMLCanvasElement} */(el).getContext("2d"));
   1378         }
   1379     });
   1380 }
   1381 
   1382 /**
   1383  * Updates the identicon in the specified canvas or svg elements.
   1384  * @param {(string|Element)} el - Specifies the container in which the icon is rendered as a DOM element of the type
   1385  *    `<svg>` or `<canvas>`, or a CSS selector to such an element.
   1386  * @param {*} hashOrValue - Optional hash or value to be rendered. If not specified, the `data-jdenticon-hash` or
   1387  *    `data-jdenticon-value` attribute will be evaluated.
   1388  * @param {Object|number|undefined} config
   1389  * @param {function(Element,number):Renderer} rendererFactory - Factory function for creating an icon renderer.
   1390  */
   1391 function renderDomElement(el, hashOrValue, config, rendererFactory) {
   1392     if (typeof el === "string") {
   1393         if (documentQuerySelectorAll) {
   1394             var elements = documentQuerySelectorAll(el);
   1395             for (var i = 0; i < elements.length; i++) {
   1396                 renderDomElement(elements[i], hashOrValue, config, rendererFactory);
   1397             }
   1398         }
   1399         return;
   1400     }
   1401     
   1402     // Hash selection. The result from getValidHash or computeHash is 
   1403     // accepted as a valid hash.
   1404     var hash = 
   1405         // 1. Explicit valid hash
   1406         isValidHash(hashOrValue) ||
   1407         
   1408         // 2. Explicit value (`!= null` catches both null and undefined)
   1409         hashOrValue != null && computeHash(hashOrValue) ||
   1410         
   1411         // 3. `data-jdenticon-hash` attribute
   1412         isValidHash(el.getAttribute(ATTRIBUTES.t/*HASH*/)) ||
   1413         
   1414         // 4. `data-jdenticon-value` attribute. 
   1415         // We want to treat an empty attribute as an empty value. 
   1416         // Some browsers return empty string even if the attribute 
   1417         // is not specified, so use hasAttribute to determine if 
   1418         // the attribute is specified.
   1419         el.hasAttribute(ATTRIBUTES.o/*VALUE*/) && computeHash(el.getAttribute(ATTRIBUTES.o/*VALUE*/));
   1420     
   1421     if (!hash) {
   1422         // No hash specified. Don't render an icon.
   1423         return;
   1424     }
   1425     
   1426     var renderer = rendererFactory(el, getIdenticonType(el));
   1427     if (renderer) {
   1428         // Draw icon
   1429         iconGenerator(renderer, hash, config);
   1430         el[IS_RENDERED_PROPERTY] = true;
   1431     }
   1432 }
   1433 
   1434 /**
   1435  * Renders an identicon for all matching supported elements.
   1436  * 
   1437  * @param {*} hashOrValue - A hexadecimal hash string or any value that will be hashed by Jdenticon. If not 
   1438  * specified the `data-jdenticon-hash` and `data-jdenticon-value` attributes of each element will be
   1439  * evaluated.
   1440  * @param {Object|number=} config - Optional configuration. If specified, this configuration object overrides any global
   1441  * configuration in its entirety. For backward compatibility a padding value in the range [0.0, 0.5) can be
   1442  * specified in place of a configuration object.
   1443  */
   1444 function jdenticonJqueryPlugin(hashOrValue, config) {
   1445     this["each"](function (index, el) {
   1446         update(el, hashOrValue, config);
   1447     });
   1448     return this;
   1449 }
   1450 
   1451 // This file is compiled to dist/jdenticon.js and dist/jdenticon.min.js
   1452 
   1453 var jdenticon = updateAll;
   1454 
   1455 defineConfigProperty(jdenticon);
   1456 
   1457 // Export public API
   1458 jdenticon["configure"] = configure;
   1459 jdenticon["drawIcon"] = drawIcon;
   1460 jdenticon["toSvg"] = toSvg;
   1461 jdenticon["update"] = update;
   1462 jdenticon["updateCanvas"] = update;
   1463 jdenticon["updateSvg"] = update;
   1464 
   1465 /**
   1466  * Specifies the version of the Jdenticon package in use.
   1467  * @type {string}
   1468  */
   1469 jdenticon["version"] = "3.3.0";
   1470 
   1471 /**
   1472  * Specifies which bundle of Jdenticon that is used.
   1473  * @type {string}
   1474  */
   1475 jdenticon["bundle"] = "browser-umd";
   1476 
   1477 // Basic jQuery plugin
   1478 var jQuery = GLOBAL["jQuery"];
   1479 if (jQuery) {
   1480     jQuery["fn"]["jdenticon"] = jdenticonJqueryPlugin;
   1481 }
   1482 
   1483 /**
   1484  * This function is called once upon page load.
   1485  */
   1486 function jdenticonStartup() {
   1487     var replaceMode = (
   1488         jdenticon[CONFIG_PROPERTIES.n/*MODULE*/] ||
   1489         GLOBAL[CONFIG_PROPERTIES.G/*GLOBAL*/] ||
   1490         { }
   1491     )["replaceMode"];
   1492     
   1493     if (replaceMode != "never") {
   1494         updateAllConditional();
   1495         
   1496         if (replaceMode == "observe") {
   1497             observer(update);
   1498         }
   1499     }
   1500 }
   1501 
   1502 // Schedule to render all identicons on the page once it has been loaded.
   1503 whenDocumentIsReady(jdenticonStartup);
   1504 
   1505 return jdenticon;
   1506 
   1507 });