// version: 2017-11-25
    /**
    * o--------------------------------------------------------------------------------o
    * | This file is part of the RGraph package - you can learn more at:               |
    * |                                                                                |
    * |                          http://www.rgraph.net                                 |
    * |                                                                                |
    * | RGraph is licensed under the Open Source MIT license. That means that it's     |
    * | totally free to use and there are no restrictions on what you can do with it!  |
    * o--------------------------------------------------------------------------------o
    */

    RGraph      = window.RGraph || {isRGraph: true};
    RGraph.HTML = RGraph.HTML || {};

// Module pattern
(function (win, doc, undefined)
{
    var RG = RGraph,
        ua = navigator.userAgent,
        ma = Math;




    /**
    * Draws the graph key (used by various graphs)
    * 
    * @param object obj The graph object
    * @param array  key An array of the texts to be listed in the key
    * @param colors An array of the colors to be used
    */
    RG.drawKey =
    RG.DrawKey = function (obj, key, colors)
    {
        if (!key) {
            return;
        }

        var ca   = obj.canvas,
            co   = obj.context,
            prop = obj.properties,

            // Key positioned in the gutter
            keypos   = prop['chart.key.position'],
            textsize = prop['chart.text.size'],
            key_non_null    = [],
            colors_non_null = [];

        co.lineWidth = 1;

        co.beginPath();

        /**
        * Change the older chart.key.vpos to chart.key.position.y
        */
        if (typeof(prop['chart.key.vpos']) == 'number') {
            obj.Set('chart.key.position.y', prop['chart.key.vpos'] * prop['chart.gutter.top']);
        }

        /**
        * Account for null values in the key
        */
        for (var i=0; i<key.length; ++i) {
            if (key[i] != null) {
                colors_non_null.push(colors[i]);
                key_non_null.push(key[i]);
            }
        }
        
        key    = key_non_null;
        colors = colors_non_null;



























        /**
        * This does the actual drawing of the key when it's in the graph
        * 
        * @param object obj The graph object
        * @param array  key The key items to draw
        * @param array colors An aray of colors that the key will use
        */
        function DrawKey_graph (obj, key, colors)
        {
            var text_size   = typeof(prop['chart.key.text.size']) == 'number' ? prop['chart.key.text.size'] : prop['chart.text.size'],
                text_italic = prop['chart.key.text.italic'] ?  true : false,
                text_bold   = prop['chart.key.text.bold'] ?  true : false,
                text_font   = prop['chart.key.text.font'] || prop['chart.key.font'] || prop['chart.text.font'],
                gutterLeft   = obj.gutterLeft,
                gutterRight  = obj.gutterRight,
                gutterTop    = obj.gutterTop,
                gutterBottom = obj.gutterBottom,
                hpos         = prop['chart.yaxispos'] == 'right' ? gutterLeft + 10 : ca.width - gutterRight - 10,
                vpos         = gutterTop + 10,
                title        = prop['chart.title'],
                blob_size    = text_size, // The blob of color
                hmargin      = 8, // This is the size of the gaps between the blob of color and the text
                vmargin      = 4, // This is the vertical margin of the key
                fillstyle    = prop['chart.key.background'],
                text_color   = prop['chart.key.text.color'],
                strokestyle  = '#333',
                height       = 0,
                width        = 0;
    
            if (!obj.coords) obj.coords = {};
            obj.coords.key = [];
    
    
            // Need to set this so that measuring the text works out OK
            co.font = text_size + 'pt ' + prop['chart.text.font'];
    
            // Work out the longest bit of text
            for (i=0; i<key.length; ++i) {
                width = Math.max(width, co.measureText(key[i]).width);
            }
    
            width += 5;
            width += blob_size;
            width += 5;
            width += 5;
            width += 5;
    
            /**
            * Now we know the width, we can move the key left more accurately
            */
            if (   prop['chart.yaxispos'] == 'left'
                || (obj.type === 'pie' && !prop['chart.yaxispos'])
                || (obj.type === 'hbar' && !prop['chart.yaxispos'])
                || (obj.type === 'hbar' && prop['chart.yaxispos'] === 'center')
                || (obj.type === 'hbar' && prop['chart.yaxispos'] === 'right')
                || (obj.type === 'rscatter' && !prop['chart.yaxispos'])
                || (obj.type === 'radar' && !prop['chart.yaxispos'])
                || (obj.type === 'rose' && !prop['chart.yaxispos'])
                || (obj.type === 'funnel' && !prop['chart.yaxispos'])
                || (obj.type === 'vprogress' && !prop['chart.yaxispos'])
                || (obj.type === 'hprogress' && !prop['chart.yaxispos'])
               ) {

                hpos -= width;
            }

            /**
            * Horizontal alignment
            */
            if (typeof(prop['chart.key.halign']) == 'string') {
                if (prop['chart.key.halign'] == 'left') {
                    hpos = gutterLeft + 10;
                } else if (prop['chart.key.halign'] == 'right') {
                    hpos = ca.width - gutterRight  - width;
                }
            }
    
            /**
            * Specific location coordinates
            */
            if (typeof(prop['chart.key.position.x']) == 'number') {
                hpos = prop['chart.key.position.x'];
            }
            
            if (typeof(prop['chart.key.position.y']) == 'number') {
                vpos = prop['chart.key.position.y'];
            }
    
    
            // Stipulate the shadow for the key box
            if (prop['chart.key.shadow']) {
                co.shadowColor   = prop['chart.key.shadow.color'];
                co.shadowBlur    = prop['chart.key.shadow.blur'];
                co.shadowOffsetX = prop['chart.key.shadow.offsetx'];
                co.shadowOffsetY = prop['chart.key.shadow.offsety'];
            }
    
    
    
    
            // Draw the box that the key resides in
            co.beginPath();
                co.fillStyle   = prop['chart.key.background'];
                co.strokeStyle = 'black';
    
            if (typeof(prop['chart.key.position.graph.boxed']) == 'undefined' || (typeof(prop['chart.key.position.graph.boxed']) == 'boolean' && prop['chart.key.position.graph.boxed']) ) {
                if (arguments[3] != false) {
        
                    co.lineWidth = typeof(prop['chart.key.linewidth']) == 'number' ? prop['chart.key.linewidth'] : 1;
    
                    // The older square rectangled key
                    if (prop['chart.key.rounded'] == true) {
                        co.beginPath();
                            co.strokeStyle = strokestyle;
                            RG.strokedCurvyRect(co, Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)),4);
                        co.stroke();
                        co.fill();
    
                        RG.NoShadow(obj);
                
                    } else {
                        co.strokeRect(Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)));
                        co.fillRect(Math.round(hpos), Math.round(vpos), width - 5, 5 + ( (text_size + 5) * RG.getKeyLength(key)));
                    }
                }
            }
    
            RG.NoShadow(obj);
    
            co.beginPath();
    
                /**
                * Custom colors for the key
                */
                if (prop['chart.key.colors']) {
                    colors = prop['chart.key.colors'];
                }
    
    
    
                ////////////////////////////////////////////////////////////////////////////////////////////
    
    

                // Draw the labels given
                for (var i=key.length - 1; i>=0; i--) {

                    var j = Number(i) + 1;

                    /**
                    * Draw the blob of color
                    */
                    // An array element, string
                    if (typeof prop['chart.key.color.shape'] === 'object' && typeof prop['chart.key.color.shape'][i] === 'string') {
                        var blob_shape = prop['chart.key.color.shape'][i];
                    
                    // An array element, function
                    } else if (typeof prop['chart.key.color.shape'] === 'object' && typeof prop['chart.key.color.shape'][i] === 'function') {
                        var blob_shape = prop['chart.key.color.shape'][i];
                    
                    // No array - just a string
                    } else if (typeof prop['chart.key.color.shape'] === 'string') {
                        var blob_shape = prop['chart.key.color.shape'];
                    
                    // No array - just a function
                    } else if (typeof prop['chart.key.color.shape'] === 'function') {
                        var blob_shape = prop['chart.key.color.shape'];

                    // Unknown
                    } else {
                        var blob_shape = 'rect';
                    }

                    if (blob_shape == 'circle') {
                        co.beginPath();
                            co.fillStyle = colors[i];
                            co.arc(hpos + 5 + (blob_size / 2), vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2), blob_size / 2, 0, 6.26, 0);
                        co.fill();
                    
                    } else if (blob_shape == 'line') {
                        co.beginPath();
                            co.strokeStyle = colors[i];
                            co.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
                            co.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + (blob_size / 2));
                        co.stroke();
                    
                    } else if (blob_shape == 'triangle') {
                        co.beginPath();
                            co.strokeStyle = colors[i];
                            co.moveTo(hpos + 5, vpos + (5 * j) + (text_size * j) - text_size + blob_size);
                            co.lineTo(hpos + (blob_size / 2) + 5, vpos + (5 * j) + (text_size * j) - text_size );
                            co.lineTo(hpos + blob_size + 5, vpos + (5 * j) + (text_size * j) - text_size + blob_size);
                        co.closePath();
                        co.fillStyle =  colors[i];
                        co.fill();

                    } else if (typeof blob_shape === 'function') {

                        blob_shape({
                            object: obj,
                            color: colors[i],
                            x: hpos + 5,
                            y: vpos + (5 * j) + (text_size * j) - text_size,
                            width: text_size,
                            height: text_size + 1
                        });
                    } else {
                        co.fillStyle =  colors[i];
                        co.fillRect(
                            hpos + 5,
                            vpos + (5 * j) + (text_size * j) - text_size,
                            text_size,
                            text_size + 1
                        );
                    }
                    
                    ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    
    
                    co.beginPath();
                    co.fillStyle = typeof text_color == 'object' ? text_color[i] : text_color;
                
                    ret = RG.Text2(obj, {
                        'font': text_font,
                        'size': text_size,
                        'bold': text_bold,
                        'italic': text_italic,
                        'x': hpos + blob_size + 5 + 5,
                        'y': vpos + (5 * j) + (text_size * j) + 3,
                        'text': key[i],
                        'accessible': !obj.properties['chart.key.interactive']
                    });

                    obj.coords.key[i] = [
                        ret.x,
                        ret.y,
                        ret.width,
                        ret.height,
                        key[i],
                        colors[i],
                        obj
                    ];
                }
            co.fill();
        }























        /**
        * This does the actual drawing of the key when it's in the gutter
        * 
        * @param object obj The graph object
        * @param array  key The key items to draw
        * @param array colors An aray of colors that the key will use
        */
        function DrawKey_gutter (obj, key, colors)
        {
            var text_size    = typeof(prop['chart.key.text.size']) == 'number' ? prop['chart.key.text.size'] : prop['chart.text.size'],
                text_bold    = prop['chart.key.text.bold'],
                text_italic  = prop['chart.key.text.italic'],
                text_font    = prop['chart.key.text.font'] || prop['chart.key.font'] || prop['chart.text.font'],
                text_color   = prop['chart.key.text.color'],
                gutterLeft   = obj.gutterLeft,
                gutterRight  = obj.gutterRight,
                gutterTop    = obj.gutterTop,
                gutterBottom = obj.gutterBottom,
                hpos         = ((ca.width - gutterLeft - gutterRight) / 2) + obj.gutterLeft,
                vpos         = gutterTop - text_size - 5,
                title        = prop['chart.title'],
                blob_size    = text_size, // The blob of color
                hmargin      = 8, // This is the size of the gaps between the blob of color and the text
                vmargin      = 4, // This is the vertical margin of the key
                fillstyle    = prop['chart.key.background'],
                strokestyle  = '#999',
                length       = 0;

            if (!obj.coords) obj.coords = {};
            obj.coords.key = [];
    
    
    
            // Need to work out the length of the key first
            co.font = (obj.properties['chart.key.text.italic'] ? 'italic ' : '') + (obj.properties['chart.key.text.bold'] ? 'bold ' : '') + text_size + 'pt ' + text_font;

            for (i=0; i<key.length; ++i) {
                length += hmargin;
                length += blob_size;
                length += hmargin;
                length += co.measureText(key[i]).width;
            }
            length += hmargin;
    
    
    
    
            /**
            * Work out hpos since in the Pie it isn't necessarily dead center
            */
            if (obj.type == 'pie') {
                if (prop['chart.align'] == 'left') {
                    var hpos = obj.radius + gutterLeft;
                    
                } else if (prop['chart.align'] == 'right') {
                    var hpos = ca.width - obj.radius - gutterRight;
    
                } else {
                    hpos = ca.width / 2;
                }
            }
    
    
    
    
    
            /**
            * This makes the key centered
            */  
            hpos -= (length / 2);
    
    
            /**
            * Override the horizontal/vertical positioning
            */
            if (typeof(prop['chart.key.position.x']) == 'number') {
                hpos = prop['chart.key.position.x'];
            }
            if (typeof(prop['chart.key.position.y']) == 'number') {
                vpos = prop['chart.key.position.y'];
            }
    
    
    
            /**
            * Draw the box that the key sits in
            */
            if (obj.Get('chart.key.position.gutter.boxed')) {
    
                if (prop['chart.key.shadow']) {
                    co.shadowColor   = prop['chart.key.shadow.color'];
                    co.shadowBlur    = prop['chart.key.shadow.blur'];
                    co.shadowOffsetX = prop['chart.key.shadow.offsetx'];
                    co.shadowOffsetY = prop['chart.key.shadow.offsety'];
                }
    
                
                co.beginPath();
                    co.fillStyle = fillstyle;
                    co.strokeStyle = strokestyle;
    
                    if (prop['chart.key.rounded']) {
                        RG.strokedCurvyRect(co, hpos, vpos - vmargin, length, text_size + vmargin + vmargin)
                        // Odd... RG.filledCurvyRect(co, hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
                    } else {
                        co.rect(hpos, vpos - vmargin, length, text_size + vmargin + vmargin);
                    }
                    
                co.stroke();
                co.fill();
    
    
                RG.NoShadow(obj);
            }
    
    
            /**
            * Draw the blobs of color and the text
            */
    
            // Custom colors for the key
            if (prop['chart.key.colors']) {
                colors = prop['chart.key.colors'];
            }
    
            for (var i=0, pos=hpos; i<key.length; ++i) {
                pos += hmargin;
    
    
    
                //////////////////////////////////////////////////////////////////////////////////////////////////////
    
    
    
                // Draw the blob of color
                if (typeof prop['chart.key.color.shape'] === 'object' && typeof prop['chart.key.color.shape'][i] === 'string') {
                    var blob_shape = prop['chart.key.color.shape'][i];
                
                } else if (typeof prop['chart.key.color.shape'] === 'object' && typeof prop['chart.key.color.shape'][i] === 'function') {
                    var blob_shape = prop['chart.key.color.shape'][i];
                
                // No array - just a function
                } else if (typeof prop['chart.key.color.shape'] === 'function') {
                    var blob_shape = prop['chart.key.color.shape'];
                
                } else if (typeof(prop['chart.key.color.shape']) == 'string') {
                    var blob_shape = prop['chart.key.color.shape'];
                
                } else {
                    var blob_shape = 'square';
                }
    
    
                /**
                * Draw the blob of color - line
                */
                if (blob_shape =='line') {
                    
                    co.beginPath();
                        co.strokeStyle = colors[i];
                        co.moveTo(pos, vpos + (blob_size / 2));
                        co.lineTo(pos + blob_size, vpos + (blob_size / 2));
                    co.stroke();
                    
                // Circle
                } else if (blob_shape == 'circle') {
                    
                    co.beginPath();
                        co.fillStyle = colors[i];
                        co.moveTo(pos, vpos + (blob_size / 2));
                        co.arc(pos + (blob_size / 2), vpos + (blob_size / 2), (blob_size / 2), 0, 6.28, 0);
                    co.fill();
                
                } else if (blob_shape == 'triangle') {
                
                    co.fillStyle = colors[i];
                    co.beginPath();
                        co.strokeStyle = colors[i];
                        co.moveTo(pos, vpos + blob_size);
                        co.lineTo(pos + (blob_size / 2), vpos);
                        co.lineTo(pos + blob_size, vpos + blob_size);
                    co.closePath();
                    co.fill();

                } else if (typeof blob_shape === 'function') {

                    blob_shape({
                        object: obj,
                        color: colors[i],
                        x: pos,
                        y: vpos,
                        width: blob_size,
                        height: blob_size
                    });

                } else {
                
                    co.beginPath();
                        co.fillStyle = colors[i];
                        co.rect(pos, vpos, blob_size, blob_size);
                    co.fill();
                }
    
    
    
                //////////////////////////////////////////////////////////////////////////////////////////////////////
    
    
    
    
                pos += blob_size;
                
                pos += hmargin;
    
                co.beginPath();
                    co.fillStyle = (typeof text_color === 'object') ? text_color[i] : text_color;

                    var ret = RG.Text2(obj, {
                        'font':text_font,
                        'bold':text_bold,
                        'size':text_size,
                        'italic': text_italic,
                        'x':pos,
                        'y':vpos + text_size + 3,
                        'text': key[i],
                        accessible: !obj.properties['chart.key.interactive']
                    });
                co.fill();
                pos += co.measureText(key[i]).width;
            
                obj.coords.key[i] = [
                    ret.x,
                    ret.y,
                    ret.width,
                    ret.height,
                    key[i],
                    colors[i],
                    obj
                ];
            }
        }




        if (keypos && keypos == 'gutter') {
            DrawKey_gutter(obj, key, colors);
        } else if (keypos && keypos == 'graph') {
            DrawKey_graph(obj, key, colors);
        } else {
            alert('[COMMON] (' + obj.id + ') Unknown key position: ' + keypos);
        }






        if (prop['chart.key.interactive']) {

            if (!RGraph.Drawing || !RGraph.Drawing.Rect) {
                alert('[INTERACTIVE KEY] The drawing API Rect library does not appear to have been included (which the interactive key uses)');
            }



            /**
            * Check that the RGraph.common.dynamic.js file has been included
            */
            if (!RGraph.InstallWindowMousedownListener) {
                alert('[INTERACTIVE KEY] The dynamic library does not appear to have been included');
            }



            // Determine the maximum width of the labels
            for (var i=0,len=obj.coords.key.length,maxlen=0; i<len; i+=1) {
                maxlen = Math.max(maxlen, obj.coords.key[i][2]);
            }
    

            //obj.coords.key.forEach(function (value, index, arr)
            //{
            for (var i=0,len=obj.coords.key.length; i<len; i+=1) {
            
                // Because the loop would have finished when the i variable is needed - put
                // the onclick function inside a new context so that the value of the i
                // variable is what we expect when the key has been clicked
                (function (idx)
                {
                    var arr   = obj.coords.key;
                    var value = obj.coords.key[idx];
                    var index = idx;
    

                    var rect = new RGraph.Drawing.Rect(obj.id,value[0], value[1], prop['chart.key.position'] == 'gutter' ? value[2] : maxlen, value[3])
                        .Set('fillstyle', 'rgba(0,0,0,0)')
                        .Draw();
                    
                    rect.onclick = function (e, shape)
                    {
                        var co  = rect.context;
    
                        co.fillStyle = prop['chart.key.interactive.highlight.label'];
                        co.fillRect(shape.x, shape.y, shape.width, shape.height);
    
                        if (typeof obj.interactiveKeyHighlight == 'function') {
                            
                            obj.Set('chart.key.interactive.index', idx);

                            RG.FireCustomEvent(obj, 'onbeforeinteractivekey');
                            obj.interactiveKeyHighlight(index);
                            RG.FireCustomEvent(obj, 'onafterinteractivekey');
                        }
                    }
                    
                    rect.onmousemove = function (e, shape)
                    {
                        return true;
                    }
                })(i);
            }
        }
    };




    /**
    * Returns the key length, but accounts for null values
    * 
    * @param array key The key elements
    */
    RG.getKeyLength = function (key)
    {
        var length = 0;

        for (var i=0,len=key.length; i<len; i+=1) {
            if (key[i] != null) {
                ++length;
            }
        }

        return length;
    };




    /**
    * Create a TABLE based HTML key. There's lots of options so it's
    * suggested that you consult the documentation page
    * 
    * @param mixed id   This should be a string consisting of the ID of the container
    * @param object prop An object map of the various properties that you can use to
    *                    configure the key. See the documentation page for a list.
    */
    RGraph.HTML.key =
    RGraph.HTML.Key = function (id, prop)
    {
        var div = doc.getElementById(id);
        var uid = RG.createUID();

        
        /**
        * Create the table that becomes the key
        */
        var str = '<table border="0" cellspacing="0" cellpadding="0" id="rgraph_key_' + uid + '" style="display: inline;' + (function ()
            {
                var style = ''
                for (i in prop.tableCss) {
                    if (typeof i === 'string') {
                        style = style + i + ': ' + prop.tableCss[i] + ';';
                    }
                }
                return style;
            })() + '" ' + (prop.tableClass ? 'class="' + prop.tableClass + '"' : '') + '>';



        /**
        * Add the individual key elements
        */
        for (var i=0; i<prop.labels.length; i+=1) {
            str += '<tr><td><div style="' + (function ()
            {
                var style = '';

                for (var j in prop.blobCss) {
                    if (typeof j === 'string') {
                        style = style + j + ': ' + prop.blobCss[j] + ';';
                    }
                }

                return style;
            })() + 'display: inline-block; margin-right: 5px; margin-top: 4px; width: 15px; height: 15px; background-color: ' + prop.colors[i] + '"' + (prop.blobClass ? 'class="' + prop.blobClass + '"' : '') + '>&nbsp;</div><td>' + (prop.links && prop.links[i] ? '<a href="' + prop.links[i] + '">' : '') + '<span ' + (prop.labelClass ? 'class="' + prop.labelClass + '"' : '') + '" style="' + (function ()
            {
                var style = '';

                for (var j in prop.labelCss) {
                    if (typeof j === 'string') {
                        style = style + j + ': ' + prop.labelCss[j] + ';';
                    }
                }

                return style;
            })() + ' ' + (function ()
            {
                var style = '';

                if (prop['labelCss_' + i]) {

                    for (var j in prop['labelCss_' + i]) {
                        style = style + j + ': ' + prop['labelCss_' + i][j] + ';';
                    }
                }

                return style ? style + '"' : '"';
            })() + '>' + prop.labels[i] + '</span>' + (prop.links && prop.links[i] ? '</a>' : '') + '</td></tr>';
        }

        div.innerHTML += (str + '</table>');

        // Return the TABLE object that is the HTML key
        return doc.getElementById('rgraph_key_' + uid);
    };




// End module pattern
})(window, document);