/**
 * Tag cloud plugin for jQuery, showing bigger tags in the center
 * @version    1.2.0
 * @release    2021-04-07
 * @repository https://github.com/peterthoeny/jquery.tagcloud
 * @author     Peter Thoeny, https://twiki.org/ & https://github.com/peterthoeny
 * @copyright  2021 Peter Thoeny, https://github.com/peterthoeny
 * @license    MIT, https://opensource.org/licenses/mit-license
 */
(function($) {

    'use strict';

    let debug = false;

    function debugLog(msg) {
        if(debug) {
           console.log('- tagCloud: ' + msg);
        }
    }

    function entityEncode(val) {
        val = val.toString()
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
        return val;
    }

    $.fn.tagCloud = function(options) {
        let self = $(this);
        let addLink = options && options.tag && options.tag.format ? false : true;
        options = $.extend({}, $.fn.tagCloud.defaults, options);
        if(options.debug != undefined) {
           debug = options.debug;
        }
        debugLog('options: ' + JSON.stringify(options, null, ''));
        let tagName = self.prop('tagName');
        if(tagName === 'UL') {
            if(!options.data) {
                options.data = [];
            }
            self.find('li').each(function(idx, elem) {
                let weight = $(elem).data('weight');
                if(weight == undefined) {
                    weight = $(elem).find(':first-child').data('weight');
                }
                let href = $(elem).find('a').attr('href') || '';
                let tag = $(elem).text() || '?';
                debugLog(weight +', '+href+', '+tag+', '+$(elem).html());
                if(options.data[idx]) {
                    if(weight != undefined) {
                        options.data[idx].weight = Number(weight);
                    }
                    if(href) {
                        options.data[idx].link = href;
                    }
                    options.data[idx].name = tag;
                } else {
                    options.data.push({ name: tag, link: href, weight: Number(weight) })
                }
            });
            self.hide();
            if(self.next().hasClass('jqTcContainer')) {
                self.next().remove();
            }
            self.after('<div></div>');
            self = self.next();
        }
        let css = {};
        Object.keys(options.container).forEach(function(key) {
            css[key] = (options[key] != undefined) ? options[key] : options.container[key];
        });
        self.addClass('jqTcContainer').css(css);

        /* Edited by Flaviorib-dev to allow responsiveness */
        let containerWidth = options.container.width;
        // let containerWidth = $(".jqTcContainer").parent().width();
        // let containerWidth = $(".jqTcContainer").width();
        
        
        let minWeight = 1000000000000;
        let maxWeight = -1000000000000;
        let minFontSize = options.tag.minFontSize || $.fn.tagCloud.defaults.tag.minFontSize;
        let maxFontSize = options.tag.maxFontSize || $.fn.tagCloud.defaults.tag.maxFontSize;
        let format = options.tag.format || $.fn.tagCloud.defaults.tag.format;
        let sum = 0;
        debugLog('minFontSize: '+minFontSize+', maxFontSize: '+maxFontSize);
        options.data.forEach(function(item) {
            if(item.weight < minWeight) {
                minWeight = item.weight;
            }
            if(item.weight > maxWeight) {
                maxWeight = item.weight;
            }
            sum += item.weight;
        });
        let a = (maxFontSize - minFontSize) / (maxWeight - minWeight);
        let b = minFontSize - (minWeight * a);
        debugLog('minWeight: '+minWeight+', maxWeight: '+maxWeight+', a: '+a+', b: '+b);
        let tags = options.data.sort(function(a, b) {
            if(a.weight > b.weight) {
                return -1;
            } else if(a.weight < b.weight) {
                return 1;
            }
            return 0;
        }).map(function(item, idx) {
            let html = format
                .replace(/\{tag\.name\}/g, item.name)
                .replace(/\{tag\.link\}/g, item.link)
                .replace(/\{tag\.weight(?:\.(\d))?\}/g, function(m, c1) {
                    let num = item.weight;
                    if(c1) {
                        let factor = 10 ** parseInt(c1);
                        num = Math.round(num * factor) / factor;
                    }
                    return num.toString();
                })
                .replace(/\{tag\.percent(?:\.(\d))?\}/g, function(m, c1) {
                    let num = 100 * item.weight / sum;
                    let factor = 1;
                    if(c1) {
                        factor = 10 ** parseInt(c1);
                    }
                    num = Math.round(num * factor) / factor;
                    return num.toString() + '%';
                });
            if(addLink && item.link) {
                html = '<a href="' + item.link + '" target="_blank">' + html + '</a>';
            }
            let size = parseInt((a * item.weight + b) * 10, 10) / 10;
            let attrs = [
                'class="jqTcTag"',
                'data-name="' + entityEncode(item.name) + '"',
                'data-link="' + entityEncode(item.link || '') + '"',
                'data-weight="' + item.weight + '"',
                'data-size="' + size + '"'
            ];
            let style = 'style="font-size: ' + size + 'px;';
            let bgColor = item.bgColor || item.backgroundColor || options.tag.backgroundColor ||
                $.fn.tagCloud.defaults.backgroundColors[idx] || $.fn.tagCloud.defaults.defaultTagBackgroundColor;
            if(bgColor) {
                style += ' background-color: ' + bgColor + ';';
            }
            let color = item.color || options.tag.color || $.fn.tagCloud.defaults.defaultTagColor;
            if(color != 'auto') {
                style += ' color: ' + color + ';';
            }
            bgColor
            .replace(/^#(.)(.)(.)$/, '#$1$1$2$2$3$3')
            .replace(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i, function(m, c1, c2, c3) {
                let brightness = Math.round((
                    (Number('0x' + c1) * 299) +
                    (Number('0x' + c2) * 587) +
                    (Number('0x' + c3) * 114)
                ) / 1000);
                if(brightness > 125) {
                    if(color === 'auto') {
                        style += ' color: black;';
                    }
                    if(options.tag.textShadow) {
                        style += ' text-shadow: 0px 0px 2px #dddddd;';
                    }
                } else {
                    if(color === 'auto') {
                        style += ' color: white;';
                    }
                    if(options.tag.textShadow) {
                        style += ' text-shadow: 0px 0px 2px #222222;';
                    }
                }
            });
            style += '"';
            attrs.push(style);
            if(item.tooltip) {
                attrs.push('title="' + entityEncode(item.tooltip) + '"');
            }
            html = '<span ' + attrs.join(' ') + '>' + html + '</span>';
            self.html(html); // set temporarily to get width and height
            let tagElem = self.find('span');
            item.width = tagElem.outerWidth();
            item.height = tagElem.outerHeight();
            item.html = html;
            item.ttLength = tagElem.length;
            item.ttHtml = self.text();
            return item;
        });
        debugLog('tags: ' + JSON.stringify(tags, null, ' '))
        let rows = [];
        let cells = [];
        let width = 0;
        let addRight = true;
        let addBottom = true;
        let padding = 2 * 5 + 5;
        let containerPadding = padding;
        let tagMargin = 2 * 10 + 5;
        let verticalAlign = 'middle';
        tags.forEach(function(item) {
            if(width + item.width + tagMargin >= containerWidth - containerPadding) {
                let rowHtml = '<tr><td style="vertical-align: ' + verticalAlign + ';">' + cells.join('') + '</td></tr>';
                if(addBottom) {
                    rows.push(rowHtml);
                    verticalAlign = 'bottom';
                } else {
                    rows.unshift(rowHtml);
                    verticalAlign = 'top';
                }
                addBottom = !addBottom;
                containerPadding += 1.5 * padding;
                cells = [];
                width = 0;
            }
            if(addRight) {
                cells.push(item.html);
            } else {
                cells.unshift(item.html);
            }
            addRight = !addRight;
            width = width + item.width + tagMargin;
        });
        let rowHtml = '<tr><td style="vertical-align: ' + verticalAlign + ';">' + cells.join('') + '</td></tr>';
        if(addBottom) {
            rows.push(rowHtml);
        } else {
            rows.unshift(rowHtml);
        }
        let html = '<table class="jqTcTable">' + rows.join('') + '</table>';
        let tagStyle = {};
        Object.keys(options.tag).forEach(key => {
            if(!/^(minFontSize|maxFontSize|format|color|textShadow|backgroundColor)$/.test(key)) {
                tagStyle[key] = options.tag[key];
            }
        });
        self.html(html).find('.jqTcTag').css(tagStyle);
    };

    $.fn.tagCloud.defaults = {
        container: {
            width:        500,
            height:       'auto',
            backgroundColor: '#f0f0f0',
            color:        '#666666',
            padding:      '10px 5px',
            fontFamily:   '"Helvetica Neue",Helvetica,Arial,sans-serif'
        },
        tag: {
            minFontSize:  10,     // min font size in pixels
            maxFontSize:  40,     // max font size in pixels
            format:       '{tag.name}', // also '{tag.link}', '{tag.weight}', '{tag.percent}'
            color:        'auto', // auto text color, black for light background, white for dark background
            textShadow:   false   // text shadow, enable for better visibility
        },
        backgroundColors: [
            '#db843d', '#92a8cd', '#a47d7c', '#058dc7', '#50b432', '#ed561b', '#24cbe5', '#64e572',
            '#ff9655', '#d6cb54', '#6af9c4', '#b5ca92', '#2f7ed8', '#5c40de', '#8bbc21', '#910000',
            '#1aadce', '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a', '#db843d', '#92a8cd',
            '#a47d7c', '#058dc7', '#50b432', '#ed561b', '#24cbe5', '#64e572', '#ff9655', '#d6cb54',
            '#6af9c4', '#b5ca92', '#2f7ed8', '#5c40de', '#8bbc21', '#910000', '#1aadce', '#492970',
            '#f28f43', '#77a1e5', '#c42525', '#a6c96a', '#db843d', '#92a8cd', '#a47d7c', '#058dc7',
            '#50b432', '#ed561b', '#24cbe5', '#64e572', '#ff9655', '#d6cb54', '#6af9c4', '#b5ca92',
            '#2f7ed8', '#5c40de', '#8bbc21', '#910000', '#1aadce', '#492970', '#f28f43', '#77a1e5'
        ],
        defaultTagColor:            'auto',   // black or white, based on background color
        defaultTagBackgroundColor:  '#ff9655' // default background color
    };

})(jQuery);
