(function () {
    'use strict';

    // 2014-20 - ntop.org
    Date.prototype.format = function (format) { //author: meizz
        var o = {
            "M+": this.getMonth() + 1, //month
            "d+": this.getDate(),    //day
            "h+": this.getHours(),   //hour
            "m+": this.getMinutes(), //minute
            "s+": this.getSeconds(), //second
            "q+": Math.floor((this.getMonth() + 3) / 3),  //quarter
            "S": this.getMilliseconds() //millisecond
        };

        if (/(y+)/.test(format)) format = format.replace(RegExp.$1,
            (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        for (var k in o) if (new RegExp("(" + k + ")").test(format))
            format = format.replace(RegExp.$1,
                RegExp.$1.length == 1 ? o[k] :
                    ("00" + o[k]).substr(("" + o[k]).length));
        return format;
    };

    // Extended disable function 
    jQuery.fn.extend({
        disable: function (state) {
            return this.each(function () {
                var $this = $(this);
                if ($this.is('input, button, textarea, select'))
                    this.disabled = state;
                else
                    $this.toggleClass('disabled', state);
            });
        }
    });

    const NTOPNG_MIN_VISUAL_VALUE = 0.005;

    const REGEXES = {
        ipv4: String.raw`^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`,
        ipv6: String.raw`^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*)$`,
        domainName: String.raw`^([a-zA-Z]([a-zA-Z]|[0-9])?\.[a-zA-Z]{2,13}|[a-zA-Z0-9]([\-_.a-zA-Z0-9]{1,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,13}|[a-zA-Z0-9]([\-_.a-zA-Z0-9]{1,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,30}\.[a-zA-Z]{2,3})$`,
        port: String.raw`^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$`,
        latency: String.raw`^([0-9]*[.])?[0-9]+$`,
        url: String.raw`^(https?\:\/\/[^\/\s]+(\/.*)?)$`,
        emailUrl: String.raw`^smtps?:\/\/[\-a-zA-Z0-9:.]{1,256}$`,
        macAddress: String.raw`^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$`,
        hostname: String.raw`^(?!\s*$)[a-zA-Z0-9._: \-\/]{1,250}|^[a-zA-Z0-9._: \-\/]{1,250}@[0-9]{0,5}`,
        username: String.raw`^[a-zA-Z0-9._@!-?]{3,30}$`,
        singleword: String.raw`^(?=[a-zA-Z0-9._:\-]{3,253}$)(?!.*[_.:\-]{2})[^_.:\-].*[^_.:\-]$`,
        multiword: String.raw`^([a-zA-Z0-9._:\-\s]{3,253})$`,
        email: String.raw`^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[a-zA-Z]{2,})$`,
        emailCommaList: String.raw`^((?:[a-zA-Z0-9.!#$%&'*+\-\/=?^_\`\|~]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*)|([a-zA-Z\d.!#$%&'*+\-\/=?^_\`\|~]{1,128}))(?:,((?:[a-zA-Z0-9.!#$%&'*+\-\/=?^_\`\|~]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*)|([a-zA-Z\d.!#$%&'*+\-\/=?^_\`\|~]{1,128})))*$`,
        https: String.raw`^https?:\/\/.+$`,
        token: String.raw`^[0-9a-f]{32}`,
        score: String.raw`^[0-9]{1,5}`,
        telegram_channel: String.raw`^[0-9\-]{1,15}`,
        password: String.raw`^[\w\/$!\/()=?^*@_-]{5,31}$`,
        tls_certificate: String.raw`^[^=,]+=[^=,]+(,\s[^=,]+=[^=,]+)*$`,
        domain_name_not_strict: String.raw`^[a-zA-Z0-9\-_~]+((\.[a-zA-Z0-9\-_~]+)+)$`,
        non_quoted_text: String.raw`^[a-zA-Z0-9.-_]+$`,
    };

    class NtopUtils$1 {

        /* Show an overlay to hide loading */
        static toggleOverlays(time = 500) {
            $(`.overlay`).toggle(time);
        }

        static showOverlays(time = 500) {
            $(`.overlay`).fadeIn(time);
        }

        static hideOverlays(time = 500) {
            $(`.overlay`).fadeOut(time);
        }

        static get REGEXES() {
            return REGEXES;
        }

        static getIPv4RegexWithCIDR() {
            const length = REGEXES.ipv4.length;
            return `${REGEXES.ipv4.substring(0, length - 1)}(\\/?)(\\b([0-9]|[12][0-9]|3[0-2])?\\b)$`;
        }

        static getIPv6RegexWithCIDR() {
            const length = REGEXES.ipv6.length;
            return `${REGEXES.ipv6.substring(0, length - 1)}(\\/?)\\b([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8])?\\b$`;
        }

        /**
         * Resolve a hostname by doing a DNS Resolve.
         * @param {string} hostname The hostname to resolve
         */
        static async resolveDNS(hostname = "ntop.org") {

            // resolve the hostname by doing a fetch request to the backend
            try {
                const request = await fetch(`${http_prefix}/lua/rest/v2/get/dns/resolve.lua?hostname=${hostname}`);
                const response = await request.json();
                return response;
            }
            catch (err) {
                // prints out the error if the request fails
                console.error(`Something went wrong when resolving hostname: ${err}`);
            }

            // if the request has failed return a placeholder response
            // indicating the failure
            return { rc: -1, rc_str: "FAILED_HTTP_REQUEST" };
        }

        /**
         * Replace the inputs which contain the [data-pattern] attribute
         * with the [pattern] attribute.
         */
        static initDataPatterns() {
            // for each input with the data-pattern attribute
            // substitute the data-pattern with the right regexes
            $(`input[data-pattern]`).each(function () {

                // if the pattern is empty then print a warn inside the console
                const dataPattern = $(this).data('pattern');
                if (!dataPattern) {
                    console.warn(`An empty data-pattern on an input was found!`, this);
                    return;
                }

                // build the regexp pattern for the input
                const pattern = dataPattern.split('|').map(p => REGEXES[p].toString()).join('|');
                // load the pattern
                $(this).attr('pattern', pattern);
                // remove the data-pattern from the input
                $(this).removeAttr('data-pattern');
            });
        }

        static is_good_ipv4(ipv4) {
            return new RegExp(REGEXES.ipv4).test(ipv4);
        }

        static is_good_ipv6(ipv6) {
            return new RegExp(REGEXES.ipv6).test(ipv6);
        }

        static is_mac_address(mac) {
            return new RegExp(REGEXES.macAddress).test(mac);
        }

        static isNumeric(value) {
            return /^\d+$/.test(value);
        }

        static is_network_mask(what, optional_mask) {
            var elems = what.split("/");
            var mask = null;
            var ip_addr;

            if (elems.length != 2) {
                if (!optional_mask)
                    return null;
                else
                    ip_addr = what;
            } else {
                ip_addr = elems[0];

                if (!NtopUtils$1.isNumeric(elems[1]))
                    return null;

                mask = parseInt(elems[1]);

                if (mask < 0)
                    return null;
            }

            if (NtopUtils$1.is_good_ipv4(ip_addr)) {
                if (mask === null)
                    mask = 32;
                else if (mask > 32)
                    return null;

                return {
                    type: "ipv4",
                    address: ip_addr,
                    mask: mask
                };
            } else if (NtopUtils$1.is_good_ipv6(elems[0])) {
                if (mask === null)
                    mask = 128;
                else if (mask > 128)
                    return (false);

                return {
                    type: "ipv6",
                    address: ip_addr,
                    mask: mask
                };
            }

            return null;
        }

        static fbits(bits) {
            const sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps'];

            if (typeof (bits) === "undefined")
                return "-";

            if (bits == 0) return '0';
            if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' bps');
            var bits_log1000 = Math.log(bits) / Math.log(1000);
            var i = parseInt(Math.floor(bits_log1000));
            if (i < 0 || isNaN(i)) {
                i = 0;
            } else if (i >= sizes.length) { // prevents overflows
                return "> " + sizes[sizes.length - 1]
            }

            if (i <= 1) {
                return Math.round(bits / Math.pow(1000, i) * 100) / 100 + ' ' + sizes[i]
            }
            else {
                var ret = parseFloat(bits / Math.pow(1000, i)).toFixed(2);
                if (ret % 1 == 0)
                    ret = Math.round(ret);
                return ret + ' ' + sizes[i]
            }
        }

        static export_rate(eps) {
            if (typeof (eps) === "undefined")
                return "-";

            var sizes = ['exp/s', 'Kexp/s'];
            if (eps == 0) return '0';
            if ((eps > 0) && (eps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' exps/s');
            var res = NtopUtils$1.scaleValue(eps, sizes, 1000);

            // Round to two decimal digits
            return Math.round(res[0] * 100) / 100 + ' ' + res[1];
        }

        static exports_format(exports) {
            if (typeof (exports) === "undefined")
                return "-";

            var exports_label = i18n_ext.exports.toLowerCase();

            var sizes = [exports_label, 'K ' + exports_label];
            if (exports == 0) return '0';
            if ((exports > 0) && (exports < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' exps/s');
            var res = NtopUtils$1.scaleValue(exports, sizes, 1000);

            // Round to two decimal digits
            return Math.round(res[0] * 100) / 100 + ' ' + res[1];
        }

        static fbits_from_bytes(bytes) {
            if (typeof (bytes) === "undefined")
                return "-";
            return (NtopUtils$1.fbits(bytes * 8));
        }

        static fpackets(pps) {
            if (typeof (pps) === "undefined")
                return "-";

            var sizes = ['pps', 'Kpps', 'Mpps', 'Gpps', 'Tpps'];
            if (pps == 0) return '0';
            if ((pps > 0) && (pps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' pps');
            var res = NtopUtils$1.scaleValue(pps, sizes, 1000);

            // Round to two decimal digits
            return Math.round(res[0] * 100) / 100 + ' ' + res[1];
        }

        static fpoints(pps) {
            if (typeof (pps) === "undefined")
                return "-";

            var sizes = ['pt/s', 'Kpt/s', 'Mpt/s', 'Gpt/s', 'Tpt/s'];
            if (pps == 0) return '0';
            if ((pps > 0) && (pps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' pt/s');
            var res = NtopUtils$1.scaleValue(pps, sizes, 1000);

            // Round to two decimal digits
            return Math.round(res[0] * 100) / 100 + ' ' + res[1];
        }

        static fflows(fps) {
            if (typeof (fps) === "undefined")
                return "-";

            var sizes = ['fps', 'Kfps', 'Mfps', 'Gfps', 'Tfps'];
            if (fps == 0) return '0';
            if ((fps > 0) && (fps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' fps');
            var res = NtopUtils$1.scaleValue(fps, sizes, 1000);

            // Round to two decimal digits
            return Math.round(res[0] * 100) / 100 + ' ' + res[1];
        }

        static fmsgs(mps) {
            if (typeof (mps) === "undefined")
                return "-";

            var sizes = ['msg/s', 'Kmsg/s', 'Msg/s', 'Gmsg/s', 'Tmsg/s'];
            if (mps == 0) return '0';
            if ((mps > 0) && (mps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' mps');
            var res = NtopUtils$1.scaleValue(mps, sizes, 1000);

            // Round to two decimal digits
            return Math.round(res[0] * 100) / 100 + ' ' + res[1];
        }

        static fmillis(ms) {

            if (ms === undefined) return '-';
            const sizes = ['ms'];
            const res = NtopUtils$1.scaleValue(ms, sizes, 1000);
            return Math.round(res[0] * 100) / 100 + ' ' + res[1];
        }

        static fnone(val) {

            if (val === undefined) return '-';
            return Math.round(val * 100) / 100;
        }

        static falerts(aps) {
            if (typeof (aps) === "undefined")
                return "-";

            // Round to two decimal digits
            return Math.round(aps * 100) / 100 + ' alerts/s';
        }

        static fint(value) {
            if (typeof (value) === "undefined")
                return "-";

            var x = Math.round(value);
            return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        }

        static ffloat(value) {
            if (typeof (value) === "undefined")
                return "-";

            var x = Math.round(value * 100) / 100.;
            return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        }

        static fpercent(value) {
            if (typeof (value) === "undefined")
                return "-";

            return Math.round(value * 100) / 100 + " %";
        }

        static percentage(value, total) {
            if (total > 0) {
                var pctg = Math.round((value * 10000) / total);

                if (pctg > 0) {
                    /* Two decimals */
                    return (" [ " + (pctg / 100) + " % ] ")
                }
            }

            return ("")
        }

        static fdate(when) {
            var epoch = when * 1000;
            var d = new Date(epoch);

            return (d);
        }

        static capitaliseFirstLetter(string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        }

        static get_trend(actual, before) {
            if ((actual === undefined) || (before === undefined) || (actual == before)) {
                return ("<i class=\"fas fa-minus\"></i>");
            } else if (actual > before) {
                return ("<i class=\"fas fa-arrow-up\"></i>");
            } else {
                return ("<i class=\"fas fa-arrow-down\"></i>");
            }
        }

        static abbreviateString(str, len) {
            if (!str)
                return "";
            if (str.length < len)
                return str;
            return str.substring(0, len) + "...";
        }

        static toFixed2(num) {
            if (!num) return "";
            return num.toFixed(2);
        }

        // Convert bytes to human readable format
        static bytesToSize(bytes) {
            if (typeof (bytes) === "undefined")
                return "-";

            var precision = 2;
            var kilobyte = 1024;
            var megabyte = kilobyte * 1024;
            var gigabyte = megabyte * 1024;
            var terabyte = gigabyte * 1024;

            if ((bytes >= 0) && (bytes < kilobyte))
                if (bytes != 0)
                    return parseFloat(bytes.toFixed(precision)) + " Bytes";
                else
                    return parseFloat(bytes) + " Bytes";

            else if ((bytes >= kilobyte) && (bytes < megabyte))
                return parseFloat((bytes / kilobyte).toFixed(precision)) + ' KB';
            else if ((bytes >= megabyte) && (bytes < gigabyte))
                return parseFloat((bytes / megabyte).toFixed(precision)) + ' MB';
            else if ((bytes >= gigabyte) && (bytes < terabyte))
                return parseFloat((bytes / gigabyte).toFixed(precision)) + ' GB';
            else if (bytes >= terabyte)
                return parseFloat((bytes / terabyte).toFixed(precision)) + ' TB';
            else
                return parseFloat(bytes.toFixed(precision)) + ' Bytes';
        }

        static drawTrend(current, last, withColor) {
            if (current == last) {
                return ("<i class=\"fas fa-minus\"></i>");
            } else if (current > last) {
                return ("<i class=\"fas fa-arrow-up\"" + withColor + "></i>");
            } else {
                return ("<i class=\"fas fa-arrow-down\"></i>");
            }
        }

        static toggleAllTabs(enabled) {
            if (enabled === true)
                $("#historical-tabs-container").find("li").removeClass("disabled").find("a").attr("data-toggle", "tab");
            else
                $("#historical-tabs-container").find("li").addClass("disabled").find("a").removeAttr("data-toggle");
        }

        static disableAllDropdownsAndTabs() {
            $("select").each(function () {
                $(this).prop("disabled", true);
            });
            NtopUtils$1.toggleAllTabs(false);
        }

        static enableAllDropdownsAndTabs() {
            $("select").each(function () {
                $(this).prop("disabled", false);
            });
            NtopUtils$1.toggleAllTabs(true);
        }

        static capitalize(s) {
            return s && s[0].toUpperCase() + s.slice(1);
        }

        static addCommas(nStr) {
            nStr += '';
            var x = nStr.split('.');
            var x1 = x[0];
            var x2 = x.length > 1 ? '.' + x[1] : '';
            var rgx = /(\d+)(\d{3})/;
            while (rgx.test(x1)) {
                x1 = x1.replace(rgx, '$1' + ',' + '$2');
            }
            return x1 + x2;
        }

        static scaleValue(val, sizes, scale, decimals) {
            if (val == 0) return [0, sizes[0]];
            let factor = decimals ? (10 * decimals) : 10;

            var i = parseInt(Math.floor(Math.log(val) / Math.log(scale)));
            if (i < 0 || isNaN(i)) {
                i = 0;
            } else if (i >= sizes.length) {
                i = sizes.length - 1;
            }

            return [Math.round((val / Math.pow(scale, i)) * factor) / factor, sizes[i]];
        }

        static formatValue(val, decimals) {
            var sizes = ['', 'K', 'M', 'G', 'T'];
            if (val == 0) return '0';
            if ((val > 0) && (val < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE);
            if (decimals == undefined) decimals = 0;
            var res = NtopUtils$1.scaleValue(val, sizes, 1000, decimals);

            return res[0] + res[1];
        }

        static formatPackets(n) {
            return (NtopUtils$1.addCommas(n.toFixed(0)) + " Pkts");
        }

        static bytesToVolume(bytes) {
            var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
            if (bytes == 0) return '0 Bytes';
            if ((bytes > 0) && (bytes < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " Bytes");
            var res = NtopUtils$1.scaleValue(bytes, sizes, 1024);

            return parseFloat(res[0]) + " " + res[1];
        };

        static bytesToVolumeAndLabel(bytes) {
            var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
            if (bytes == 0) return '0 Bytes';
            var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
            return [(bytes / Math.pow(1024, i)).toFixed(2), sizes[i]];
        };

        static bitsToSize(bits, factor) {
            factor = factor || 1000;
            var sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps'];
            if (bits == 0) return '0 bps';
            if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " bps");
            var res = NtopUtils$1.scaleValue(bits, sizes, factor);

            return res[0].toFixed(2) + " " + res[1];
        };

        static bitsToSize_no_comma(bits, factor) {
            factor = factor || 1000;
            var sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps'];
            if (bits == 0) return '0 bps';
            if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " bps");
            var res = NtopUtils$1.scaleValue(bits, sizes, factor);

            return res[0] + " " + res[1];
        };

        static secondsToTime(seconds) {

            if (seconds < 1) {
                return ("< 1 sec")
            }

            let days = Math.floor(seconds / 86400);
            let hours = Math.floor((seconds / 3600) - (days * 24));
            let minutes = Math.floor((seconds / 60) - (days * 1440) - (hours * 60));
            let sec = seconds % 60;
            let msg = "", msg_array = [];

            if (days > 0) {
                let years = Math.floor(days / 365);

                if (years > 0) {
                    days = days % 365;

                    msg = years + " year";
                    if (years > 1) {
                        msg += "s";
                    }

                    msg_array.push(msg);
                    msg = "";
                }
                msg = days + " day";
                if (days > 1) { msg += "s"; }
                msg_array.push(msg);
                msg = "";
            }

            if (hours > 0) {
                if (hours < 10) { msg = "0"; }
                msg += hours + ":";
            }

            if (minutes < 10) { msg += "0"; }
            msg += minutes + ":";
            if (sec < 10) { msg += "0"; }
            msg += sec;
            msg_array.push(msg);

            return msg_array.join(", ")
        }

        static msecToTime(msec) {
            if (msec >= 1000) {
                return NtopUtils$1.secondsToTime(msec / 1000);
            } else {
                var x = Math.round(msec * 1000) / 1000.;
                return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " ms";
            }
        }

        static epoch2Seen(epoch) {
            /* 08/01/13 15:12:37 [18 min, 13 sec ago] */
            var d = new Date(epoch * 1000);
            var tdiff = Math.floor(((new Date()).getTime() / 1000) - epoch);

            return (d.format("dd/MM/yyyy hh:mm:ss") + " [" + NtopUtils$1.secondsToTime(tdiff) + " ago]");
        }

        /* ticks for graph x axis */
        static graphGetXAxisTicksFormat(diff_epoch) {
            var tickFormat;

            if (diff_epoch <= 86400) {
                tickFormat = "%H:%M:%S";
            } else if (diff_epoch <= 2 * 86400) {
                tickFormat = "%b %e, %H:%M:%S";
            } else {
                tickFormat = "%b %e";
            }

            return (tickFormat);
        }

        static paramsExtend(defaults, override) {
            return $.extend({}, defaults, override);
        }

        static paramsToForm(form, params) {
            form = $(form);

            for (var k in params) {
                if (params.hasOwnProperty(k)) {
                    var input = $('<input type="hidden" name="' + k + '" value="' + params[k] + '">');
                    input.appendTo(form);
                }
            }

            return form;
        }

        /*
         * This function creates a javascript object where each k->v pair of the input object
         * translates into two pairs in the output object: a key_[i]->k and a val_[i]->v, where
         * i is an incremental index.
         *
         * The output object can then be serialized to an URL. This conversion is required for
         * handling special characters: since ntopng strips special characters in _GET keys,
         * _GET values must be used.
         *
         * This function performs the inverse conversion of lua paramsPairsDecode.
         *
         */
        static paramsPairsEncode(params) {
            var i = 0;
            var res = {};

            for (var k in params) {
                res["key_" + i] = k;
                res["val_" + i] = params[k];
                i = i + 1;
            }

            return res;
        }

        static hostkey2hostInfo(host_key) {
            var info;

            host_key = host_key.replace(/____/g, ":");
            host_key = host_key.replace(/___/g, "/");
            host_key = host_key.replace(/__/g, ".");

            info = host_key.split("@");
            return (info);
        }

        static handle_tab_state(nav_object, default_tab) {
            $('a', nav_object).click(function (e) {
                e.preventDefault();
            });

            // store the currently selected tab in the hash value
            $(" > li > a", nav_object).on("shown.bs.tab", function (e) {
                var id = $(e.target).attr("href").substr(1);
                if (history.replaceState) {
                    // this will prevent the 'jump' to the hash
                    history.replaceState(null, null, "#" + id);
                } else {
                    // fallback
                    window.location.hash = id;
                }
            });

            // on load of the page: switch to the currently selected tab
            var hash = window.location.hash;
            if (!hash) hash = "#" + default_tab;
            $('a[href="' + hash + '"]', nav_object).tab('show');
        }

        static _add_find_host_link(form, name, data) {
            $('<input>').attr({
                type: 'hidden',
                id: name,
                name: name,
                value: data,
            }).appendTo(form);
        }

        /* Used while searching hosts a and macs with typeahead */
        static makeFindHostBeforeSubmitCallback(http_prefix) {
            return function (form, data) {
                if (data.context && data.context == "historical") {
                    form.attr("action", http_prefix + "/lua/pro/db_search.lua");
                    if (data.type == "ip") {
                        NtopUtils$1._add_find_host_link(form, "ip", data.ip);
                    } else if (data.type == "mac") {
                        NtopUtils$1._add_find_host_link(form, "mac", data.mac);
                    } else if (data.type == "community_id") {
                        NtopUtils$1._add_find_host_link(form, "community_id", data.community_id);
                    } else if (data.type == "ja3_client") {
                        NtopUtils$1._add_find_host_link(form, "ja3_client", data.ja3_client);
                    } else if (data.type == "ja3_server") {
                        NtopUtils$1._add_find_host_link(form, "ja3_server", data.ja3_server);
                    } else /* "hostname" */ {
                        NtopUtils$1._add_find_host_link(form, "name", data.hostname ? data.hostname : data.name);
                    }
                } else {
                    if (data.type == "mac") {
                        form.attr("action", http_prefix + "/lua/mac_details.lua");
                    } else if (data.type == "network") {
                        form.attr("action", http_prefix + "/lua/hosts_stats.lua");
                        NtopUtils$1._add_find_host_link(form, "network", data.network);
                    } else if (data.type == "snmp") {
                        form.attr("action", http_prefix + "/lua/pro/enterprise/snmp_interface_details.lua");
                        NtopUtils$1._add_find_host_link(form, "snmp_port_idx", data.snmp_port_idx);
                    } else if (data.type == "snmp_device") {
                        form.attr("action", http_prefix + "/lua/pro/enterprise/snmp_device_details.lua");
                    } else if (data.type == "asn") {
                        form.attr("action", http_prefix + "/lua/hosts_stats.lua");
                        NtopUtils$1._add_find_host_link(form, "asn", data.asn);
                    } else {
                        form.attr("action", http_prefix + "/lua/host_details.lua");
                        NtopUtils$1._add_find_host_link(form, "mode", "restore");
                    }
                }

                return true;
            }
        }

        static tstampToDateString(html_tag, format, tdiff) {
            tdiff = tdiff || 0;
            var timestamp = parseInt(html_tag.html()) + tdiff;
            var localized = d3.time.format(format)(new Date(timestamp * 1000));
            html_tag.html(localized).removeClass("hidden");
            return localized;
        }

        static noHtml(s) {
            return s.replace(/<[^>]+>/g, '');
        }

        static cleanCustomHostUrl(host) {
            /* Remove starting http(s). */
            return host
                .replace(/^http:\/\//gi, '')
                .replace(/^https:\/\//gi, '')
                /* Remove starting www. */
                .replace(/^www\./gi, '')
                /* Remove non-allowed characters */
                .replace(/[^0-9a-zA-Z\.:\/_-]/gi, '');
        }

        /* https://stackoverflow.com/questions/2090551/parse-query-string-in-javascript */
        static parseQuery(queryString) {
            var query = {};
            var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
            for (var i = 0; i < pairs.length; i++) {
                var pair = pairs[i].split('=');
                query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
            }
            return query;
        }

        static getHistoryParameters(params) {
            var cur_params = NtopUtils$1.parseQuery(window.location.search);
            var new_params = $.extend(cur_params, params);
            var new_query = "?" + $.param(new_params, true);
            var baseUrl = [location.protocol, '//', location.host, location.pathname].join('');

            return baseUrl + new_query;
        }

        // return true if the status code is different from 200
        static check_status_code(status_code, status_text, $error_label) {

            const is_different = status_code != 200;

            if (is_different && $error_label != null) {

                let message = i18n_ext.request_failed_message;
                if (status_code && status_text) {
                    message += `: ${status_code} - ${status_text}`;
                }

                $error_label.text(message).show();
            }
            else if (is_different && $error_label == null) {
                alert(`${i18n_ext.request_failed_message}: ${status_code} - ${status_text}`);
            }

            return is_different;
        }

        // To be used in conjunction with httpdocs/templates/config_list_components/import_modal.html
        static importModalHelper(params) {

            if (!params.loadConfigXHR) { throw ("importModalHelper:: Missing 'loadConfigXHR' param"); }

            $(`input#import-input`).on('change', function () {
                $(`#btn-confirm-import`).removeAttr("disabled");
            });

            $(`#import-modal`).on('hidden.bs.modal', function () {
                $(`#import-input`).val('');
                $("#import-error").hide().removeClass('text-warning').addClass('invalid-feedback');
                $(`#btn-confirm-import`).attr("disabled", "disabled");
            });

            $("#import-modal").on("submit", "form", function (e) {

                e.preventDefault();

                const $button = $('#btn-confirm-import');
                $button.attr("disabled", "");

                // read configuration file
                const file = $('#import-input')[0].files[0];

                if (!file) {
                    $("#import-error").text(`${i18n_ext.no_file}`).show();
                    $button.removeAttr("disabled");
                    return;
                }

                const reader = new FileReader();
                reader.readAsText(file, "UTF-8");

                reader.onload = function (responseJSON) {
                    // Client-side configuration file format check
                    let jsonConfiguration = null;
                    try {
                        jsonConfiguration = JSON.parse(reader.result);
                    } catch (e) {

                        ToastUtils.showToast({
                            id: 'import-error-toast',
                            level: 'error',
                            title: i18n("error"),
                            body: e,
                            delay: 3000
                        });
                        return;
                    }

                    if (!jsonConfiguration) {
                        $("#import-error").text(i18n_ext.rest_consts[responseJSON.rc_str] || 'Not Implemented Yet').show();
                        $button.removeAttr("disabled");
                        return;
                    }

                    // Submit configuration file
                    params.loadConfigXHR(reader.result)
                        .done((response, status, xhr) => {
                            if (response.rc < 0) {
                                $("#import-error").text(response.rc_str).show();
                                return;
                            }

                            // if the operation was successful call the successCallback
                            if (params.successCallback) {
                                params.successCallback(response);
                            }

                            const key = $(`input[name='configuration']:checked`).val();

                            const body = (key == 'all')
                                ? i18n("manage_configurations.messages.import_all_success")
                                : i18n("manage_configurations.messages.import_success");

                            // show a success alert message
                            ToastUtils.showToast({
                                id: 'import-configuration-alert',
                                level: 'success',
                                title: i18n("success"),
                                body: body,
                                delay: 2000
                            });

                            $("#import-modal").modal('hide');

                        })
                        .fail(({ responseJSON }) => {
                            const PARTIAL_IMPORT_RC = -28;

                            if (params.failureCallback) {
                                params.failureCallback(responseJSON);
                            }

                            if (responseJSON && responseJSON.rc > 0) return;
                            if (responseJSON.rc == PARTIAL_IMPORT_RC) {
                                $(`#import-error`).removeClass('invalid-feedback').addClass('text-warning');
                            }

                            $("#import-error").text(responseJSON.rc_str_hr || i18n_ext.rest_consts[responseJSON.rc_str] || i18n_ext.FAILED_HTTP_REQUEST).show();

                        })
                        .always(() => {
                            $button.removeAttr("disabled");
                        });
                };
            });
        }

        static serializeFormArray(serializedArray) {
            const serialized = {};
            serializedArray.forEach((obj) => {
                /* if the object is an array  */
                if (obj.name.includes('[]')) {
                    return;
                }
                else {

                    // clean the string
                    if (typeof obj.value === "string") {
                        obj.value = obj.value.trim();
                    }
                    serialized[obj.name] = obj.value;
                }
            });
            return serialized;
        }

        static cleanForm(formSelector) {
            /* remove validation fields and tracks */
            $(formSelector).find('input,select,textarea').each(function (i, input) {
                $(this).removeClass(`is-valid`).removeClass(`is-invalid`);
            });
            /* reset all the values */
            $(formSelector)[0].reset();
        }

        /**
         * Make a fetch call with a timeout option
         */
        static fetchWithTimeout(uri, options = {}, time = 5000) {

            const controller = new AbortController();
            const config = { ...options, signal: controller.signal };

            return fetch(uri, config)
                .then((response) => {
                    if (!response.ok) {
                        throw new Error(`${response.status}: ${response.statusText}`)
                    }
                    return response
                })
                .catch((error) => {
                    if (error.name === 'AbortError') {
                        throw new Error('Response timed out')
                    }
                })
        }

        static setPref(action, csrf, success, failure) {

            if (action == undefined) {
                console.warn("An action key must be defined to set a preference!");
                return;
            }

            const empty = () => { };
            const request = $.post(`${http_prefix}/lua/update_prefs.lua`, { action: action, csrf: csrf });
            request.done(success || empty);
            request.fail(failure || empty);
        }

        /**
         * Glue strings contained in array separated by a comma.
         * @param {array} array The array of strings. I.e. ["Hello", "World"]
         * @param {number} limit How many words the string contains
         *
         * @return {string} A string built by array's elements. i.e: "Hello, World"
         */
        static arrayToListString(array, limit) {

            if (array == undefined) return "";

            if (array.length > limit) {
                return array.slice(0, limit).join(", ") + ` ${i18n_ext.and_x_more.replace('$num', array.length - limit)}`;
            }

            return array.slice(0, limit).join(", ");
        }

        static buildURL(location, params = {}, hasReferer = false, refererParams = {}) {

            const url = new URL(location, window.location);

            for (const [name, value] of Object.entries(params)) {
                if (value || value === 0)
                    url.searchParams.set(name, value);
                continue;
            }

            if (hasReferer) {

                const refUrl = new URL(window.location.href);
                for (const [name, value] of Object.entries(refererParams)) {
                    if (!value) continue;
                    refUrl.searchParams.set(name, value);
                }

                url.searchParams.set('referer', refUrl.toString());
            }

            return url.toString();
        }

        static getEditPoolLink(href, poolId) {
            const url = new URL(href, window.location);
            url.searchParams.set('pool_id', poolId);
            return url.toString();
        }

        static getPoolLink(poolType, poolId = 0) {
            return `${http_prefix}/lua/rest/v2/get/${poolType}/pools.lua?pool=${poolId}`;
        }

        static async getPool(poolType, id = 0) {

            if (poolType === null) throw 'A pool type must be defined!';

            try {

                const request = await fetch(NtopUtils$1.getPoolLink(poolType, id));
                const pool = await request.json();

                if (pool.rc < 0) {
                    return [false, {}];
                }

                return [true, pool.rsp[0]];
            }
            catch (err) {
                return [false, {}];
            }
        }

        /**
         * Save the scale of element inside the local storage
         * @param {object} $element 
         * @param {object} scale
         */
        static saveElementScale($element, scale = { width: 0, height: 0 }) {

            const key = NtopUtils$1.generateScaleElementKey($element);
            localStorage.setItem(key, JSON.stringify(scale));
        }

        static generateScaleElementKey($element) {
            let identificator;
            const page = location.pathname;
            const elementId = $element.attr('id');

            if (elementId !== "") {
                identificator = elementId;
            }
            else {
                const className = $element.attr('class');
                identificator = className;
            }

            const key = `${identificator}-${page}-scale`;
            return key;
        }

        /**
         * Load the old scale value ofx element from the local storage
         * @param {object} $element 
         */
        static loadElementScale($element) {

            const key = NtopUtils$1.generateScaleElementKey($element);
            const currentValue = localStorage.getItem(key);
            if (currentValue == null) return undefined;

            return JSON.parse(currentValue);
        }

        static fillFieldIfValid($field, value) {

            if (value === undefined) {
                $field.val('');
            }
            else {
                $field.val(value);
            }

        }

        static copyToClipboard(text, item) {
            const el = document.createElement('textarea');
            el.value = text;
            el.setAttribute('readonly', '');
            el.style.position = 'absolute';
            el.style.left = '-9999px';
            document.body.appendChild(el);
            el.select();
            document.execCommand('copy');
            document.body.removeChild(el);
            $(item).attr("title", "Copied!").tooltip("dispose").tooltip().tooltip("show");
            $(item).removeAttr("data-bs-original-title");
            $(item).attr("title", text);
        }

        static stripTags(html) {
            let t = document.createElement("div");
            t.innerHTML = html;
            return t.textContent || t.innerText || "";
        }

        static shortenLabel(label, len, last_char) {
            let shortened_label = label;
            if (label.length > len + 5) {
                if (last_char) {
                    let last_index = label.lastIndexOf(last_char);
                    const requested_label = label.slice(last_index);
                    if (len > last_index)
                        len = last_index;
                    shortened_label = label.slice(0, len) + "... " + requested_label;
                } else {
                    shortened_label = label.slice(0, len) + "...";
                }
            }

            return shortened_label
        }

        static sortAlphabetically(a, b) {
            const nameA = a.label?.toUpperCase(); // ignore upper and lowercase
            const nameB = b.label?.toUpperCase(); // ignore upper and lowercase
            if (nameA < nameB) { return -1; }
            if (nameA > nameB) { return 1; }
            return 0;
        }

        /* This function, given a name and a value, return a string
         * formatted in the following way:
         * name [value]
              * If max_name_len is different from 0, then it's going to cut the name string
              * to max_name_len
         */
        static formatNameValue(name, value, max_name_len) {
            let label = name;
            if (name != value) {
                if (max_name_len && typeof (max_name_len) == 'number')
                    label = this.shortenLabel(label, max_name_len, '.');

                label = `${label} [${value}]`;
            }
            return label
        }

        /* This function, remove from a string the VLAN 0
         * name@0 -> name
         */
        static removeVlan(name) {
            let label = name;
            const vlan_index = label.lastIndexOf('@');
            if (vlan_index != -1) {
                const vlan = label.slice(vlan_index + 1);
                if (vlan == 0) {
                    label = label.slice(0, vlan_index);
                }
            }

            return label
        }

        /* Format an object with label and value from a column row */
        static formatGenericObj(obj, row) {
            let label = obj.label ? obj.label : obj.value;
            obj.value;
            return label;
        }

        /* Format a country from a column object */
        static formatCountry(obj, row) {
            let country_code = obj.value;
            let label = obj.label ? obj.label : obj.value;
            return `${label} <img src="/dist/images/blank.gif" class="flag flag-${country_code.toLowerCase()}">`;
        }

        /* Format an host from a column object */
        static formatHost(obj, row, is_client) {
            let label = "";

            if (!obj) {
                return label;
            }

            /* Link */
            let host_key = obj.ip;
            if (row.vlan_id && row.vlan_id.value)
                host_key = host_key + '@' + row.vlan_id.value;

            /* Label */
            label = obj.label ? obj.label : obj.value;
            if (row.vlan_id && row.vlan_id.label)
                label += `@${row.vlan_id.label}`;

            const url = NtopUtils$1.buildURL(`${http_prefix}/lua/host_details.lua`, { host: host_key });
            label = `<a href="${url}">${label}</a>`;

            /* Country */
            let country_obj = is_client ? row.cli_country : row.srv_country;
            if (!country_obj && row.country) country_obj = row.country;
            if (country_obj && country_obj.value)
                label += ` <img src="${http_prefix}/dist/images/blank.gif" class="flag flag-${country_obj.value.toLowerCase()}" title="${country_obj.title}"></a>`;

            return label;
        }

        /* Format a network from a column object */
        static formatNetwork(obj, row) {
            let label = "";

            if (!obj) {
                return label;
            }

            /* Link */
            let network_key = obj.value;

            /* Label */
            label = obj.label ? obj.label : obj.value;
            if (row.vlan_id && row.vlan_id.label)
                label += `@${row.vlan_id.label}`;

            const url = NtopUtils$1.buildURL(`${http_prefix}/lua/hosts_stats.lua`, { network: network_key });
            label = `<a href="${url}">${label}</a>`;

            return label;
        }

        /* This function converts a mac address to a string*/
        static convertMACAddress(a) {
            return a.toLowerCase().replace(/[^a-f0-9]/g, '');
        }
        /* This function converts an ip to a number equale to the ip but without . or :: in case of ipv6
         * this is needed in case of ordering
         */
        static convertIPAddress(a) {
            var i, item;
            var m, n, t;
            var x, xa;

            if (!a) {
                return 0;
            }

            a = a.replace(/<[\s\S]*?>/g, "");
            //IPv4:Port
            t = a.split(":");
            if (t.length == 2) {
                m = t[0].split(".");
            }
            else {
                m = a.split(".");
            }
            n = a.split(":");
            x = "";
            xa = "";

            if (m.length == 4) {
                // IPV4
                for (i = 0; i < m.length; i++) {
                    item = m[i];

                    if (item.length == 1) {
                        x += "00" + item;
                    }
                    else if (item.length == 2) {
                        x += "0" + item;
                    }
                    else {
                        x += item;
                    }
                }
            }
            else if (n.length > 0) {
                // IPV6
                var count = 0;
                for (i = 0; i < n.length; i++) {
                    item = n[i];

                    if (i > 0) {
                        xa += ":";
                    }

                    if (item.length === 0) {
                        count += 0;
                    }
                    else if (item.length == 1) {
                        xa += "000" + item;
                        count += 4;
                    }
                    else if (item.length == 2) {
                        xa += "00" + item;
                        count += 4;
                    }
                    else if (item.length == 3) {
                        xa += "0" + item;
                        count += 4;
                    }
                    else {
                        xa += item;
                        count += 4;
                    }
                }

                // Padding the ::
                n = xa.split(":");
                var paddDone = 0;

                for (i = 0; i < n.length; i++) {
                    item = n[i];

                    if (item.length === 0 && paddDone === 0) {
                        for (var padding = 0; padding < (32 - count); padding++) {
                            x += "0";
                            paddDone = 1;
                        }
                    }
                    else {
                        x += item;
                    }
                }
            }

            return x;
        }

        /* Format an AS from a column object */
        static formatASN(obj, row) {
            let label = "";

            if (!obj) {
                return label;
            }

            /* Link */
            let asn_key = obj.value;

            /* Label */
            label = obj.label ? obj.label : obj.value;

            const url = NtopUtils$1.buildURL(`${http_prefix}/lua/hosts_stats.lua`, { asn: asn_key });
            label = `<a href="${url}">${label}</a>`;

            return label;
        }

        static createProgressBar(percentage) {
            return `<div class="d-flex flex-row align-items-center">
              <div class="col-9 progress">
                <div class="progress-bar bg-warning" aria-valuenow="${percentage}" aria-valuemin="0" aria-valuemax="100" style="width: ${percentage}%;">
                </div>
              </div>
              <div class="col">&nbsp;${percentage} %</div>
            </div>`
        }

        static createBreakdown(percentage_1, percentage_2, label_1, label_2) {
            if (percentage_1 == 0 && percentage_2 == 0) {
                return `<div class="d-flex flex-row align-items-center progress">
            <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" 
                 data-bs-toggle="tooltip" data-bs-placement="top" title="No data available"></div>
        </div>`
            }

            let progressBars = '';

            if (percentage_1 > 0) {
                progressBars += `<div class="progress-bar bg-warning" aria-valuenow="${percentage_1}" aria-valuemin="0" aria-valuemax="100" 
                 style="width: ${percentage_1}%;" 
                 data-bs-toggle="tooltip" data-bs-placement="top" title="${label_1}: ${Math.floor(percentage_1)}%">${label_1}</div>`;
            }

            if (percentage_2 > 0) {
                progressBars += `<div class="progress-bar bg-success" aria-valuenow="${percentage_2}" aria-valuemin="0" aria-valuemax="100" 
                 style="width: ${percentage_2}%;" 
                 data-bs-toggle="tooltip" data-bs-placement="top" title="${label_2}: ${Math.floor(percentage_2)}%">${label_2}</div>`;
            }

            return `<div class="d-flex flex-row align-items-center progress">${progressBars}</div>`
        }

        /* Return the number of rows available in a table */
        static getNumTableRows() {
            return [10, 20, 50, 100];
        }

        static formatApexChartLabelFromXandName({ series, seriesIndex, dataPointIndex, w }) {
            const serie = w.config.series[seriesIndex]["data"][dataPointIndex];
            const name = serie["name"];
            const y_value = serie["y"];
            const host_name = serie["meta"]["label"];

            const x_axis_title = w.config.xaxis.title.text;
            const y_axis_title = w.config.yaxis[0].title.text;

            return (`
    <div class='apexcharts-theme-light apexcharts-active' id='test'>
        <div class='apexcharts-tooltip-title' style='font-family: Helvetica, Arial, sans-serif; font-size: 12px;'>
            ${host_name}
        </div>
        <div class='apexcharts-tooltip-series-group apexcharts-active d-block'>
            <div class='apexcharts-tooltip-text text-left'>
                <b>${x_axis_title}</b>: ${name}
            </div>
            <div class='apexcharts-tooltip-text text-left'>
                <b>${y_axis_title}</b>: ${y_value}
            </div>
        </div>
    </div>
    `)
        }

        static apexChartJumpToAlerts(event, chartContext, config) {
            const { seriesIndex, dataPointIndex } = config;
            const { series } = config.config;
            if (seriesIndex === -1) return;
            if (series === undefined) return;

            const serie = series[seriesIndex];
            const base_url = serie.base_url || series[0]['base_url'];
            const default_url = serie.start_url || series[0]['start_url'];
            if (base_url != null && default_url != null) {
                const search = serie.data[dataPointIndex].meta.url_query;
                location.href = `${base_url}?${default_url}${search}`;
            }
        }


        static apexChartJumpToHostDetails(event, chartContext, config) {
            const { seriesIndex, dataPointIndex } = config;
            const { series } = config.config;
            if (seriesIndex === -1) return;
            if (series === undefined) return;

            const serie = series[seriesIndex];

            const base_url = serie.base_url || series[0]['base_url'];

            if (base_url != null) {
                const url = `${base_url}?${serie.data[dataPointIndex].meta.url_query}`;
                ntopng_url_manager.go_to_url(url);
            }
        }


        static formatApexChartLabelFromXandY({ series, seriesIndex, dataPointIndex, w }) {
            const serie = w.config.series[seriesIndex]["data"][dataPointIndex];

            const x_value = serie["x"];
            const y_value = serie["y"];
            const host_name = serie["meta"]["label"];

            const x_axis_title = w.config.xaxis.title.text;
            const y_axis_title = w.config.yaxis[0].title.text;

            return (`
      <div class='apexcharts-theme-light apexcharts-active' id='test'>
          <div class='apexcharts-tooltip-title' style='font-family: Helvetica, Arial, sans-serif; font-size: 12px;'>
              ${host_name}
          </div>
          <div class='apexcharts-tooltip-series-group apexcharts-active d-block'>
              <div class='apexcharts-tooltip-text text-left'>
                  <b>${x_axis_title}</b>: ${x_value}
              </div>
              <div class='apexcharts-tooltip-text text-left'>
                  <b>${y_axis_title}</b>: ${y_value}
              </div>
          </div>
      </div>
    `)
        }

        static loadBSTooltips() {
            var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
            tooltipTriggerList.map(function (tooltipTriggerEl) {
                return new bootstrap.Tooltip(tooltipTriggerEl)
            });
        }

        static disposeBSTooltips() {
            document.querySelectorAll('.tooltip').forEach(tooltip => {
                const tooltipInstance = bootstrap.Tooltip.getInstance(tooltip);
                if (tooltipInstance) {
                    tooltipInstance.dispose();
                }
                tooltip.remove();
            });
        }

        static reloadBSTooltips() {
            this.disposeBSTooltips();
            this.loadBSTooltips();
        }
    }

    $(function () {
        // if there are inputs with 'pattern' data attribute
        // then initialize them
        NtopUtils$1.initDataPatterns();
    });

    // 2016-19 - ntop.org

    function datatableRemoveEmptyRow(table) {
      $("tbody tr.emptyRow", $(table)).remove();
    }

    function datatableAddEmptyRow(table, empty_str) {
      var columns = $("thead th", $(table)).filter(function() {
       return $(this).css('display') != 'none';
      }).length;
      $("tbody", $(table)).html('<tr class="emptyRow"><td colspan="' + columns + '"><i>' + empty_str + '</i></td></tr>');
    }

    function datatableGetNumDisplayedItems(table) {
       return $("tr:not(.emptyRow)", $(table)).length - 1;
    }

    function datatableIsEmpty(table) {
      return datatableGetNumDisplayedItems(table) == 0;
    }

    function datatableGetByForm(form) {
      return $("table", $("#dt-top-details", $(form)).parent())
    }

    function datatableUndoAddRow(new_row, empty_str, bt_to_enable, callback_str) {
      if (bt_to_enable)
         $(bt_to_enable).removeAttr("disabled").removeClass("disabled");

      var form = $(new_row).closest("form");
      $(new_row).remove();
      aysUpdateForm(form);
      var dt = datatableGetByForm(form);

      if (datatableIsEmpty(dt))
         datatableAddEmptyRow(dt, empty_str);

       if (callback_str)
          // invoke
          window[callback_str](new_row);
    }

    function datatableForEachRow(table, callbacks) {
       $("tr:not(:first)", table).each(function(row_i) {
          if(typeof callbacks === 'function') {
             callbacks.bind(this)(row_i);
          } else {
             var i;
             for (i=0; i<callbacks.length; i++)
                callbacks[i].bind(this)(row_i);
          }
       });
    }

    function datatableAddButtonCallback(td_idx, label, bs_class, callback_str, link, visible = true, title = '') {
       if ($("td:nth-child("+td_idx+")", $(this)).find('div.d-flex').length == 0) {
          $("td:nth-child("+td_idx+")", $(this)).empty();
          $("td:nth-child("+td_idx+")", $(this)).append($("<div class='d-flex justify-content-center'></div>"));
       }
       $("td:nth-child("+td_idx+")", $(this)).find('.d-flex').append('<a href="' + link + `" title='${title}' data-placement="bottom" class="btn btn-sm mx-1 ${bs_class} ${!visible ? 'disabled' : ''}" onclick="` + callback_str + '" role="button">' + label + '</a>');
    }

    function datatableAddDeleteButtonCallback(td_idx, callback_str, label) {
        datatableAddButtonCallback.bind(this)(td_idx, label, "btn-danger", callback_str, "javascript:void(0)", true, 'Delete');
    }

    function datatableAddActionButtonCallback(td_idx, callback_str, label, visible = true, title = '') {
        datatableAddButtonCallback.bind(this)(td_idx, label, "btn-info", callback_str, "javascript:void(0)", visible, title);
    }
        
    function datatableAddFilterButtonCallback(td_idx, callback_str, label, title = '', visible = true) {
        datatableAddButtonCallback.bind(this)(td_idx, label, "btn-warning", callback_str, "javascript:void(0)", visible, title);
    }

    function datatableAddLinkButtonCallback(td_idx, link, label, title = '') {
       datatableAddButtonCallback.bind(this)(td_idx, label, "btn-primary", "", link, true, title);
    }

    function datatableMakeSelectUnique(tr_obj, added_rows_prefix, options) {
       options = NtopUtils.paramsExtend({
          on_change: $.noop,                     /* A callback to be called when the select input changes */
          selector_fn: function(obj) {           /* A callback which receives a tr object and returns a single select input */
             return obj.find("select").first();
          },
       }, options);

       function datatableForeachSelectOtherThan(this_select, added_rows_prefix, selector_fn, callback) {
          $("[id^=" + added_rows_prefix + "]").each(function(){
             var other = selector_fn($(this));
             if (other[0] != this_select[0])
                callback(other);
          });
       }

       function datatableOptionChangeStatus(option_obj, enable) {
          if (enable) {
             option_obj.removeAttr("disabled");
          } else {
             var select_obj = option_obj.closest("select");
             var should_reset = (select_obj.val() == option_obj.val());
             option_obj.attr("disabled", "disabled");

             if(should_reset) {
                var new_val = select_obj.find("option:not([disabled])").first().val();
                select_obj.val(new_val);
                select_obj.attr("data-old-val", new_val);
             }
          }
       }

       function datatableOnSelectEntryChange(added_rows_prefix, selector_fn, change_callback) {
          var old_value = $(this).attr("data-old-val") || "";
          var new_value = $(this).val() || "";
          var others = [];

          if (old_value == new_value)
             old_value = "";

          datatableForeachSelectOtherThan($(this), added_rows_prefix, selector_fn, function(other) {
             datatableOptionChangeStatus(other.find("option[value='" + old_value + "']"), true);
             datatableOptionChangeStatus(other.find("option[value='" + new_value + "']"), false);
             others.push(other);
          });

          change_callback($(this), old_value, new_value, others, datatableOptionChangeStatus);

          $(this).attr("data-old-val", new_value);
       }

       function datatableOnAddSelectEntry(select_obj, added_rows_prefix, selector_fn) {
          select_obj.val("");

          // Trigger an update on other inputs in order to disable entries on the select_obj
          datatableForeachSelectOtherThan(select_obj, added_rows_prefix, selector_fn, function(other) {
             //datatableOptionChangeStatus(select_obj.find("option[value='" + other.val() + "']"), false);
             other.trigger("change");
          });

          // select first available entry
          var new_sel = select_obj.find("option:not([disabled])").first();
          var new_val = new_sel.val();

          // trigger change event to update other entries
          select_obj.val(new_val);
          select_obj.trigger("change");
       }

       var select = options.selector_fn(tr_obj);
       select.on("change", function() { datatableOnSelectEntryChange.bind(this)(added_rows_prefix, options.selector_fn, options.on_change); });
       select.on("remove", function() {$(this).val("").trigger("change");});
       datatableOnAddSelectEntry(select, added_rows_prefix, options.selector_fn);
    }

    function datatableIsLastPage(table) {
       var lastpage = $("#dt-bottom-details .pagination li:nth-last-child(3)", $(table));
       return !((lastpage.length == 1) && (lastpage.hasClass("active") == false));
    }

    function datatableGetColumn(table, id_key, id_value) {
       var res = table.data("datatable").resultset.data.filter(function(item) {
          return item[id_key] === id_value;
       });

       if(res) return res[0];
    }

    function datatableGetColumnIndex(table, column_key) {
       var index = table.data("datatable").options.columns.findIndex(function(item) {
          return item.field === column_key;
       });

       return(index);
    }

    /*
     * Helper function to add refreshable datatables rows.
     *
     * table: the datatable div jquery object
     * column_id: the field key used to indentify the rows
     * refresh_interval: milliseconds refresh interval for this table
     * trend_columns: (optional) a map <field -> formatter_fn> which indicates the numeric columns
     * which should be shown with up/down arrows upon refresh.
     *
     * Returns true on success, false otherwise.
     *
     * Example usage:
     *   $("#table-redis-stats").datatable({
     *     ...
     *     tableCallback: function() {
     *       // The table rows will be identified by the "column_key",
     *       // refreshed every 5 seconds, with up/down arrows on the "column_hits"
     *       datatableInitRefreshRows($("#table-redis-stats"), "column_key", 5000, {"column_hits": addCommas});
     *     }
     *   });
     */
    function datatableInitRefreshRows(table, column_id, refresh_interval, trend_columns) {
      var $dt = table.data("datatable");
      var rows = $dt.resultset.data;
      var old_timer = table.data("dt-rr-timer");
      var old_req = table.data("dt-rr-ajax");
      trend_columns = trend_columns || {};

      if(old_timer) {
        // Remove the previously set timer to avoid double scheduling
        clearInterval(old_timer);
        table.removeData("dt-rr-timer");
      }

      if(old_req) {
        // Abort the previous request if any
        old_req.abort();
        table.removeData("dt-rr-ajax");
      }

      var ids = [];
      var id_to_row = {};

      for(var row in rows) {
        var data = rows[row];

        if(data[column_id]) {
          var data_id = data[column_id];
          id_to_row[data_id] = row;
          ids.push(data_id);
        }
      }

      // These parameters will be passed to the refresh endpoint
      // the custom_hosts parameter will be passed in the AJAX request and
      // will contain the IDs to refresh. It should be used by the receiving
      // Lua script as a filter
      var params = {
        "custom_hosts": ids.join(",")
      };
      var url = $dt.options.url;
      var first_load = true;

      var _process_result = function(result) {
        if(typeof(result) === "string")
          result = JSON.parse(result);

        if(!result) {
          console.error("Bad JSON result");
          return;
        }

        for(var row in result.data) {
           var data = result.data[row];
           var data_id = data[column_id];

           if(data_id && id_to_row[data_id]) {
              var row_idx = id_to_row[data_id];
              var row_html = $dt.rows[row_idx];
              var row_tds = $("td", row_html);

              /* Try to update all the fields for the current row (row_html) */
              for(var key in data) {
                 var col_idx = datatableGetColumnIndex(table, key);
                 var cell = row_tds[col_idx];
                 var $cell = $(cell);

                 var old_val = $cell.data("dt-rr-cur-val") || $(cell).html();
                 var trend_value_formatter = trend_columns[key];
                 var new_val = data[key];
                 var arrows = "";

                 if(trend_value_formatter) {
                  if(parseFloat(new_val) != new_val)
                    console.warn("Invalid number: " + new_val);

                  if(!first_load)
                    arrows = " " + NtopUtils.drawTrend(parseFloat(new_val), parseFloat(old_val));

                  // This value will be neede in the next refresh
                  $cell.data("dt-rr-cur-val", new_val);

                  new_val = trend_value_formatter(new_val);
                }

                 $(cell).html((new_val != 0) ? (new_val + arrows) : "");
              }
           }
        }

        first_load = false;
        table.removeData("dt-rr-ajax");
     };

      // Save the timer into "dt-rr-timer" to be able to stop it if
      // datatableInitRefreshRows is called again
      table.data("dt-rr-timer", setInterval(function() {
        // Double check that a request is not pending
        var old_req = table.data("dt-rr-ajax");

        if(old_req)
          return;

        // Save the ajax request to possibly abort it if
        // datatableInitRefreshRows is called again
        table.data("dt-rr-ajax", $.ajax({
           type: 'GET',
           url: url,
           data: params,
           cache: false,
           success: _process_result,
        }));
      }, refresh_interval));

      // First update
      _process_result($dt.resultset);
    }

    function draw_processes_graph(http_prefix, graph_div_id, host) {
        var links;
        var nodes = {};

        var url = http_prefix + '/lua/get_processes_graph_data.lua?host=' + host;

        d3.json(url, function(error, json) {
    	if(error)
    	    return console.warn(error);

    	links = json;
    	var _link;

    	// Compute the distinct nodes from the links.
    	links.forEach(function(link) {
    	    if(link.source_pid == -1) {
    		/* IP Address -> PID */
    		_link = http_prefix + "/lua/host_details.lua?host=" + link.source;
    	    } else {
    		/* PID -> IP Address */
    		_link = http_prefix + "/lua/process_details.lua?pid=" + link.source_pid + "&pid_name=" + link.source_name + "&host=" + host + "&page=flows";
    	    }

    	    link.source = nodes[link.source]
    		|| (nodes[link.source] = {
    		    name: link.source_name, num:link.source,
    		    link: _link, type: link.source_type, pid: link.source_pid
    		});

    	    if(link.target_pid == -1) {
    		/* IP Address -> PID */
    		_link = http_prefix + "/lua/host_details.lua?host=" + link.target;
    	    } else {
    		/* PID -> IP Address */
    		_link = http_prefix + "/lua/process_details.lua?pid=" + link.target_pid + "&pid_name=" + link.target_name + "&host=" + host + "&page=flows";
    	    }

    	    link.target = nodes[link.target]
    		|| (nodes[link.target] = {
    		    name: link.target_name, num: link.target,
    		    link: _link, type: link.target_type, pid: link.target_pid
    		});
    	});

    	var width = 960, height = 500, arrow_size = 6;
    	var color = d3.scale.category10();

    	/* Same colors as those used in the flow_details.lua page to represent hosts and processes */
    	color["proc"] = "red";
    	color["host"] = "lightsteelblue";

    	var force = d3.layout.force()
    	    .nodes(d3.values(nodes))
    	    .links(links)
    	    .size([width, height])
    	    .linkDistance(120) // Arc length
    	    .charge(-400)
    	    .on("tick", tick)
    	    .start();

    	var svg = d3.select("#" + graph_div_id).append("svg")
    	    .attr("id", "ebpf_graph")
    	    .attr("width", width)
    	    .attr("height", height);

    	// Per-type markers, as they don't inherit styles.
    	svg.append("defs").selectAll("marker")
    	    .data(["proc2proc", "proc2host", "host2proc", "host2host"])
    	    .enter().append("marker")
    	    .attr("id", function(d) { return d; })
    	    .attr("viewBox", "0 -5 20 20")
    	    .attr("refX", 15)
    	    .attr("refY", -1.5)
    	    .attr("markerWidth", arrow_size).attr("markerHeight", arrow_size)
    	    .attr("orient", "auto")
    	    .append("path")
    	    .attr("d", "M0,-5L10,0L0,5");

    	var path = svg.append("g").selectAll("path")
    	    .data(force.links())
    	    .enter().append("path")
    	    .attr("class", function(d) { return "link " + d.type; })
    	    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });


    	var circle = svg.append("g").selectAll("circle")
    	    .data(force.nodes())
    	    .enter().append("circle")
    	    .attr("class", "ebpf_circle")
    	    .attr("r", 15) /* Radius */
    	    .style("fill", function(d) { return color[d.type]; })
    	    .call(force.drag)
    	    .on("dblclick", function(d) {
    		window.location.href = d.link;
    	    } );

    	// Circle label
    	var text = svg.append("g").selectAll("text")
    	    .data(force.nodes())
    	    .enter().append("text")
    	    .attr("class", "ebpf_text")
    	    .attr("x", 12)
    	    .attr("y", "0.5rem")
          .style("font-size", "1rem")
    	    .text(function(d) {
    		if(d.pid >= 0) // Process
    		    return(d.name + " [pid: "+d.pid+"]");
    		else { // Host
    		    return(d.name);
    		}
    	    });

    	// Use elliptical arc path segments to doubly-encode directionality.
    	function tick() {
    	    path.attr("d", linkArc);
    	    circle.attr("transform", transform);
    	    text.attr("transform", transform);
    	}

    	function linkArc(d) {
    	    var dx = d.target.x - d.source.x,
    		dy = d.target.y - d.source.y,
    		dr = Math.sqrt(dx * dx + dy * dy);
    	    return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
    	}

    	function transform(d) {
    	    return "translate(" + d.x + "," + d.y + ")";
    	}
        });
    }

    var ebpfUtils = /*#__PURE__*/Object.freeze({
        __proto__: null,
        draw_processes_graph: draw_processes_graph
    });

    /* Use with:
      *
      * $('#edit-recipient-modal form').modalHandler({ ... })
      */
    class ModalHandler {

        constructor(form, options) {

            if (typeof options.csrf === "undefined") {
                throw new Error("ModalHandler::Missing CSRF token!");
            }

            this.element = form;
            this.dialog = $(form).closest(".modal");

            this.options = options;
            this.csrf = options.csrf;
            this.dontDisableSubmit = options.dontDisableSubmit;

            this.observer = new MutationObserver((list) => {
                this.bindFormValidation();
                this.toggleFormSubmission();
                this.initDataPatterns();
            });

            this.observer.observe(this.element[0], {
                childList: true,
                subtree: true
            });

            this.initialState = null;
            this.currentState = null;
            this.firstCloseAttempt = false;
            this.isSubmitting = false;

            const submitButton = $(this.element).find(`[type='submit']`);
            if (submitButton.length == 0) {
                throw new Error("ModalHandler::The submit button was not found inside the form!");
            }

            this.toggleFormSubmission();

        }

        initDataPatterns() {
            NtopUtils.initDataPatterns();
        }

        /**
         * Create a form's snapshot to save a form state
         */
        createFormSnapshot() {

            const snapshot = {
                inputs: {},
                hidden: []
            };

            $(this.element).find('textarea,select,input[type!="radio"]').each(function () {

                const type = $(this).prop('nodeName').toLowerCase();
                const name = $(this).attr('name');
                snapshot.inputs[`${type}[name='${name}']`] = $(this).val();
            });

            $(this.element).find(`[style='display: none;'], span.invalid-feedback`).each(function () {
                snapshot.hidden.push($(this));
            });

            return snapshot;
        }

        compareFormSnaphsot(s1, s2) {

            if (s1 == null || s2 == null) return true;

            for (let [key, value] of Object.entries(s1.inputs)) {
                if (s2.inputs[key] != value) return false;
            }

            return true;
        }

        delegateModalClosing() {

            const self = this;

            $(this.dialog).find('button.cancel').off('click').click(function () {

                self.firstCloseAttempt = false;
                $(self.element)[0].reportValidity();
                $(self.dialog).find('.confirm-closing').fadeOut(100, function () {
                    $(self.dialog).find('button.btn-close').fadeIn(100);
                });
            });

            $(this.dialog).off('hide.bs.modal').on('hide.bs.modal', function (event) {
                
                if (self.isSubmitting) {
                    event.preventDefault();
                    return;
                }

                // if the form state hasn't changed then don't show the message
                if (self.compareFormSnaphsot(self.currentState, self.initialState)) {
                    return;
                }

                if (self.firstCloseAttempt) return;
                // abort the modal closing event
                event.preventDefault();

                // flag a close attempt has been invoked
                self.firstCloseAttempt = true;
                
                // show an alert to inform the user
                $(self.dialog).find('button.btn-close').fadeOut(100, function () {
                    $(self.dialog).find('.confirm-closing').fadeIn(100);
                });

                return;

            });

            $(this.dialog).off('hidden.bs.modal').on('hidden.bs.modal', function (event) {

                // for each input inside the form restore the initial value
                // from the snapshot taken at init
                for (const [selector, value] of Object.entries(self.initialState.inputs)) {
                    $(self.dialog).find(selector).val(value);
                    $(self.dialog).find(selector).removeClass('is-invalid');
                }

                // hide the shwon elements
                self.initialState.hidden.forEach(($hidden) => {
                    $hidden.hide();
                });

                self.element.find(`[type='submit']`).attr("disabled", "disabled");
                self.currentState = null;
                self.firstCloseAttempt = false;

                $(self.dialog).find('.confirm-closing').fadeOut(100, function () {
                    $(self.dialog).find('button.btn-close').fadeIn(100);
                });

                // clean the form when the modal is closed
                // to prevent the fields flickering
                self.cleanForm();
            });
        }

        fillFormModal() {
            return this.options.loadFormData();
        }

        invokeModalInit(data = {}) {

            const self = this;

            // reset form values when the modal closes
            this.delegateModalClosing();
            this.data = data || this.fillFormModal();
            this.options.onModalInit(this.data, this);

            $(this.element).parents('.modal').on('show.bs.modal', function () {
                self.options.onModalShow();
            });

            // create a initial form snapshot to restore elements on closing
            this.initialState = this.createFormSnapshot();
            this.currentState = null;

            this.delegateResetButton();   
        }

        delegateSubmit() {

            this.bindFormValidation();

            const self = this;

            this.submitHandler = function (e) {
                if (!self.options.isSyncRequest) {
                    e.preventDefault();
                    e.stopPropagation();
                    self.makeRequest();
                }
            };

            $(this.element).on('submit', this.submitHandler);
        }

        bindFormValidation() {

            const self = this;

            // handle input validation
            $(this.element).find(`input,select,textarea`).each(async function (i, input) {

                // jQuery object of the current input
                const $input = $(this);
                // id to handle the current timeout set to show errors
                let timeoutId = -1;

                const validHostname = async () => {

                    // show the spinner to the user and set the input to readonly
                    const $spinner = $input.parent().find('.spinner-border');
                    $input.attr("readonly", true);
                    $spinner.show();

                    const response = await NtopUtils.resolveDNS($(input).val());

                    // hide the spinner and renable write to the input
                    $input.removeAttr("readonly");
                    $spinner.hide();

                    // if the response was negative then alert the user
                    if (response.rc < 0) {
                        input.setCustomValidity(response.rc_str);
                        return [false, response.rc_str_hr];
                    }

                    // return success for valid resolved hostnmae
                    input.setCustomValidity("");

                    return [true, "Success"];
                };

                const validInput = async (validation) => {
                    // if the input require to validate host name then perform a DNS resolve
                    if (validation.data.resolveDNS && $input.val().match(NtopUtils.REGEXES.domainName)) {
                        return await validHostname();
                    }

                    if (validation.data.cannotBeEmpty && validation.isInputEmpty) {
                        // trigger input validation flag
                        input.setCustomValidity("Please fill the input.");
                        return [false, validation.data.validationEmptyMessage || i18n_ext.missing_field];
                    }

                    if (input.validity.patternMismatch) {
                        input.setCustomValidity("Pattern mismatch.");
                        return [false, validation.data.validationMessage || i18n_ext.invalid_field];
                    }

                    if (input.validity.rangeOverflow) {
                        input.setCustomValidity("Value exceed the maximum value.");
                        return [false, validation.data.rangeOverflowMessage || i18n_ext.invalid_field];
                    }

                    if (input.validity.rangeUnderflow) {
                        input.setCustomValidity("Value is under the minimum value.");
                        return [false, validation.data.rangeUnderflowMessage || i18n_ext.invalid_field];
                    }

                    if (input.validity.typeMismatch) {
                        input.setCustomValidity("Pattern mismatch.");
                        return [false, validation.data.validationMessage || i18n_ext.invalid_field];
                    }

                    // set validation to true
                    input.setCustomValidity("");
                    return [true, "Success"];
                };

                const checkValidation = async () => {
                    const validation = {
                        data: {
                            validationMessage: $input.data('validationMessage'),
                            validationEmptyMessage: $input.data('validationEmptyMessage'),
                            cannotBeEmpty: ($input.attr('required') === "required") || ($input.data("validationNotEmpty") == true),
                            resolveDNS: $input.data('validationResolvedns'),
                            rangeOverflowMessage: $input.data('validationRangeOverflowMessage'),
                            rangeUnderflowMessage: $input.data('validationUnderflowOverflowMessage'),
                        },
                        isInputEmpty: (typeof($input.val()) === "string" ? $input.val().trim() == "" : false)
                    };

                    const [isValid, messageToShow] = await validInput(validation);
                    let $error = $input.parent().find(`.invalid-feedback`);

                    // if the error element doesn't exist then create a new one
                    if ($error.length == 0) {
                        $error = $(`<span class='invalid-feedback'></span>`);
                    }

                    // display the errors and color the input box
                    if (!isValid) {
                        $input.addClass('is-invalid');
                        $input.parent().append($error);
                        $error.text(messageToShow);
                    }
                    else {
                        // clean the validation message and remove the error
                        $input.removeClass('is-invalid');
                        $error.fadeOut(500, function () { $(this).remove(); });
                    }
                };

                $(this).off('input').on('input', function (e) {

                    self.currentState = self.createFormSnapshot();

                    // if exists already a Timeout then clear it
                    if (timeoutId != -1) clearTimeout(timeoutId);

                    if (!$input.attr("formnovalidate")) {
                        // trigger input validation after 300msec
                        timeoutId = setTimeout(() => {
                            checkValidation();
                            // trigger form validation to enable the submit button
                            self.toggleFormSubmission();
                        }, 300);
                        // the user has changed the input, we can abort the first close attempt
                        self.firstCloseAttempt = false;
                    }
                });

                $(this).off('invalid').on('invalid', function (e) {
                    e.preventDefault();
                    if (!$input.attr("formnovalidate")) {
                        checkValidation();
                    }
                });
            });

        }

        getModalID() {
            return $(this.element).parents('.modal').attr('id');
        }

        toggleFormSubmission() {

            let isValid = true;

            // if each input is marked as valid then enable the form submit button
            $(this.element).find('input:not(:disabled),select:not(:disabled),textarea:not(:disabled)').each(function (idx, input) {
                // make a concatenate & between valid flags
                isValid &= input.validity.valid;
            });

            isValid
                ? $(this.element).find(`[type='submit'],[type='test']`).removeAttr("disabled")
                : $(this.element).find(`[type='submit'],[type='test']`).attr("disabled", "disabled");
        }

        cleanForm() {
            /* remove validation class from fields */
            $(this.element).find('input,textarea,select').each(function (i, input) {
                $(this).removeClass(`is-valid`).removeClass(`is-invalid`);
            });
            /* reset all the values */
            $(this.element)[0].reset();
        }

        makeRequest() {

            const $feedbackLabel = $(this.element).find(`.invalid-feedback`);
            const submitButton = $(this.element).find(`[type='submit']`);
            let dataToSend = this.options.beforeSumbit(this.data);

            dataToSend.csrf = this.csrf;
            dataToSend = $.extend(dataToSend, this.options.submitOptions);

            /* clean previous state and disable button */
            submitButton.attr("disabled", "disabled");

            const self = this;

            if (this.options.endpoint) {
                let request;

                if (self.options.method == "post") {
                    request = $.ajax({
                        url: this.options.endpoint,
                        data: JSON.stringify(dataToSend),
                        method: self.options.method,
                        dataType: "json",
                        contentType: "application/json; charset=utf-8"
                    });
                }
                else {
                    request = $.get(this.options.endpoint, dataToSend);
                }

                this.isSubmitting = true;

                request.done(function (response, textStatus) {

                    // clear submitting state
                    self.isSubmitting = false;
                    // clear the current form state
                    self.currentState = null;

                    if (self.options.resetAfterSubmit) self.cleanForm();
                    $feedbackLabel.hide();

                    const success = self.options.onSubmitSuccess(response, dataToSend, self);
                    // if the submit return a true boolean then close the modal
                    if (success) {
                      if(self.dialog.modal)
                        self.dialog.modal('hide');
                      else {
                        self.dialog[0].hidden = true;
                        $(`.modal-backdrop.fade.show`).remove();
                      }
                    }

                    /* unbind the old closure on submit event and bind a new one */
                    $(self.element).off('submit', self.submitHandler);
                    self.delegateSubmit();
                })
                .fail(function (jqxhr, textStatus, errorThrown) {

                    self.isSubmitting = false;
                    const response = jqxhr.responseJSON;
                    if (response.rc !== undefined && response.rc < 0) {
                        $feedbackLabel.html(response.rc_str_hr).show();
                    }

                    self.options.onSubmitError(response, dataToSend, textStatus, errorThrown);
                })
                .always(function (d) {
                    submitButton.removeAttr("disabled");
                });

            } else { // no endpoint

                    // clear the current form state
                    self.currentState = null;

                    //if (self.options.resetAfterSubmit) self.cleanForm();
                    $feedbackLabel.hide();

                    const success = self.options.onSubmitSuccess({}, dataToSend, self);
                    // if the submit return a true boolean then close the modal
                    if (success) {
                      if(self.dialog.modal)
                        self.dialog.modal('hide');
                      else
                        self.dialog[0].hidden = true;
                    }

                    /* unbind the old closure on submit event and bind a new one */
                    $(self.element).off('submit', self.submitHandler);
                    self.delegateSubmit();

                    submitButton.removeAttr("disabled");
            }
        }

        delegateResetButton() {

            const self = this;
            const resetButton = $(this.element).find(`[type='reset']`);
            if (resetButton.length == 0) return;

            const defaultValues = NtopUtils.serializeFormArray($(this.element).serializeArray());

            resetButton.click(function (e) {

                e.preventDefault();

                // reset the previous values
                $(self.element).find('input:visible,select').each(function (i, input) {
                    const key = $(input).attr('name');
                    $(input).val(defaultValues[key])
                        .removeClass('is-invalid').removeClass('is-valid');
                });
            });
        }
    }

    const modalHandler = function (args) {

        if (this.length != 1) throw new Error("Only a form element can by initialized!");

        const options = $.extend({
            csrf: '',
            endpoint: '',
            resetAfterSubmit: true,
            /* True to skip the are-you-sure check on the dialog */
            dontDisableSubmit: false,
            /* True if the request isn't done by AJAX request */
            isSyncRequest: false,
            method: 'get',
            /**
             * Fetch data asynchronusly from the server or
             * loads data directly from the current page.
             * The function must returns the fetched data.
             *
             * @returns Returns the fetched data.
             * @example Below there is an example showing
             * how to use the function when fetching data from the server
             * ```
             * loadFormData: async function() {
             *      const data = await fetch(`endpoint/to/data`);
             *      const user = await data.json();
             *      return user;
             * }
             * ```
             */
            loadFormData: function () { },

            /**
             * onModalInit() is invoked when the plugin has been initialized.
             * This function is used to load the fetched data from `loadFormData()`
             * inside the form modal inputs.
             *
             * @param {object} loadedData This argument contains the fetched data obtained
             * from `loadFormData()`
             * @example Below there is an example showing how to use
             * the function (we suppose that loadFormData() returns the following
             * object: `loadedUser = {firstname: 'Foo', lastname: 'Bar', id: 1428103}`)
             * ```
             * onModalInit: function(loadedUser) {
             *      $(`#userModal form input#firstname`).val(loadedUser.firstname);
             *      $(`#userModal form input#lastname`).val(loadedUser.lastname);
             *      $(`#userModal form input#id`).val(loadedUser.id);
             * }
             * ```
             */
            onModalInit: function (loadedData) { },

            onModalShow: function () { },

            /**
             * The function beforeSubmit() is invoked after the user
             * submit the form. The function must return the data to
             * send to the endpoint. If the chosen method is `post`
             * a csrf will be add to the returned object.
             *
             * @example We show below a simple example how to use the function:
             * ```
             * beforeSubmit: function() {
             *      const body = {
             *          action: 'edit',
             *          JSON: JSON.stringify(serializeArrayForm($(`form`).serializeArray()))
             *      };
             *      return body;
             * }
             * ```
             */
            beforeSumbit: function () { return {} },

            /**
             * This function is invoked when the request to the endpoint
             * terminates successfully (200). Before the call of this function
             * a new csrf retrived from the server will be set for
             * future calls.
             *
             * @param {object} response This object contains the response
             * from the server
             *
             * @example Below there is an example showing a simple user case:
             * ```
             * onSubmitSuccess: function(response) {
             *      if (response.success) {
             *          console.log(`The user info has been edit with success!`);
             *      }
             * }
             * ```
             */
            onSubmitSuccess: function (response) { },

            /**
             * This function is invoked when the request to the endpoint
             * terminates with failure (!= 200). Before the call of this function
             * a new csrf retrived from the server will be set for
             * future calls.
             *
             * @param {object} sent This object contains the sent data to the endpoint
             * @param {string} textStatus It contains the error text status obtained
             * @param {object} errorThrown This object contains info about the error
             *
             * @example Below there is an example showing a simple user case:
             * ```
             * onSubmitError: function(sent, textStatus, errorThrown) {
             *      if (errorThrown) {
             *          console.error(`Ops, something went wrong!`);
             *          console.error(errorThrown);
             *      }
             * }
             * ```
             */
            onSubmitError: function (sent, textStatus, errorThrown) { },

            /**
             * This function is invoked when the user click the reset input
             * inside the form.
             *
             * @param {object} defaultData It contains the fetched data from
             * `loadFormData()`.
             *
             * @example Below there is an example how to use the function:
             * ```
             * onModalReset: function(defaultData) {
             *      $(`input#id`).val(defaultData.id);
             *      $(`input#name`).val(defaultData.name);
             *      $(`input#address`).val(defaultData.address);
             * }
             * ```
             */
            onModalReset: function (defaultData) { },
        }, args);

        const mh = new ModalHandler(this, options);
        mh.delegateSubmit();

        return mh;
    };

    /**
        (C) 2022-24 - ntop.org
    */
    const types = {
        no_formatting: {
            id: "no_formatting",
            um: null,
            step: null,
            decimal: null,
            scale_values: null,
        },
        number: {
            id: "number",
            um: ["", "K", "M", "B", "T"],
            step: 1000,
            decimal: null,
            scale_values: null,
        },
        full_number: {
            id: "number",
            um: ["", "K", "M", "B", "T"],
            step: 1000,
            decimal: null,
            scale_values: null,
            thousands_sep: ",", /* Comment this to enable "um" scaled style */
        },
        bytes: {
            id: "bytes",
            um: ["B", "KB", "MB", "GB", "TB", "PB", "EB"],
            step: 1024,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        bps: {
            id: "bps",
            um: ["bps", "Kbps", "Mbps", "Gbps", "Tbps", "Pbps"],
            step: 1000,
            decimal: 2,
            scale_values: 8,
            absolute_value: true,
        },
        bps_no_scale: {
            id: "bps_no_scale",
            um: ["bps", "Kbps", "Mbps", "Gbps", "Tbps", "Pbps"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        speed: {
            id: "speed",
            um: ["bit", "Kbit", "Mbit", "Gbit", "Tbit", "Pbit"],
            step: 1000,
            decimal: 0,
            scale_values: null,
            absolute_value: true,
        },
        flows: {
            id: "flows",
            um: ["flows", "Kflows", "Mflows", "Gflows"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        fps: {
            id: "fps",
            um: ["flows/s", "Kflows/s", "Mflows/s", "Gflows/s"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        fps_short: {
            id: "fps_short",
            um: ["fps", "Kfps", "Mfps", "Gfps"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        alerts: {
            id: "alerts",
            um: ["alerts", "Kalerts", "Malerts", "Galerts"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        alertps: {
            id: "alertps",
            um: ["alerts/s", "Kalerts/s", "Malerts/s", "Galerts/s"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        hits: {
            id: "hits",
            um: ["hits", "Khits", "Mhits", "Ghits"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        hitss: {
            id: "hitss",
            um: ["hits/s", "Khits/s", "Mhits/s", "Ghits/s"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        packets: {
            id: "packets",
            um: ["packets", "Kpackets", "Mpackets", "Gpackets", "Tpackets"],
            step: 1000,
            decimal: 0,
            scale_values: null,
            absolute_value: true,
        },
        pps: {
            id: "pps",
            um: ["pps", "Kpps", "Mpps", "Gpps", "Tpps"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        ms: {
            id: "ms",
            um: ["ms"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        drops: {
            id: "drops",
            um: ["dps", "Kdps", "Mdps", "Gdps", "Tdps"],
            step: 1000,
            decimal: 2,
            scale_values: null,
            absolute_value: true,
        },
        percentage: {
            id: "percentage",
            um: ["%"],
            step: 101,
            decimal: 1,
            scale_values: null,
            max_value: 100,
            absolute_value: true,
        },
        ratio: {
            id: "ratio",
            um: [""],
            step: 101,
            decimal: 2,
            scale_values: null,
            max_value: 100,
            absolute_value: true,
        },
        date: {
            id: "date",
            um: null,
            step: null,
            decimal: null,
            scale_values: null
        }
    };

    function getUnitMeasureLen(type) {
        // 000.00
        let t = types[type];
        let spaceValue = 3;
        if (t.decimal != null && t.decimal > 0) {
            spaceValue = 6;
        }
        let spaceUm = 0;
        if (t.um != null) {
            spaceUm = Math.max(...t.um.map((um) => um.length));
        }
        return (spaceValue + 1 + spaceUm);
    }

    function getScaleFactorIndex(type, value) {
        let typeOptions = types[type];
        if (type == types.no_formatting.id || value == null) {
            return null;
        }
        if (typeOptions.scale_values != null) {
            value *= typeOptions.scale_values;
        }
        let step = typeOptions.step;
        let negativeValue = value < 0;
        if (negativeValue) { value *= -1; }
        let i = 0;
        let measures = typeOptions.um;
        while (value >= step && i < measures.length) {
            value = value / step;
            i += 1;
        }
        return i;
    }

    function formatAccounting(amount, decimalCount = 0, decimal = ".", thousands = "'") {
        try {
            decimalCount = Math.abs(decimalCount);
            decimalCount = isNaN(decimalCount) ? 2 : decimalCount;

            const negativeSign = amount < 0 ? "-" : "";

            let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString();
            let j = (i.length > 3) ? i.length % 3 : 0;

            return negativeSign +
                (j ? i.substr(0, j) + thousands : '') +
                i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) +
                (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "");
        } catch (e) {
            console.log(e);
        }
    }

    function getFormatter(type, absoluteValue, scaleFactorIndex) {
        let typeOptions = types[type];
        if (typeOptions == null) { return null; }

        absoluteValue |= typeOptions.absolute_value;
        let formatter = function (value) {
            if (value == null) {
                return '';
            }
            if (type == types.no_formatting.id) {
                return value;
            }
            if (typeOptions.scale_values != null) {
                value *= typeOptions.scale_values;
            }
            let negativeValue = value < 0;
            if (negativeValue) { value *= -1; }

            if (typeOptions.max_value != null && value > typeOptions.max_value) {
                value = typeOptions.max_value;
            }

            if (typeOptions.thousands_sep) {
                value = value + '';
                var x = value.split('.');
                var x1 = x[0];
                var x2 = (x.length > 1) ? ('.' + x[1]) : '';
                var rgx = /(\d+)(\d{3})/;
                while (rgx.test(x1)) {
                    x1 = x1.replace(rgx, '$1' + ',' + '$2');
                }
                return x1 + x2;
            }

            let step = typeOptions.step;
            let decimal = typeOptions.decimal;
            let measures = typeOptions.um;
            let i = 0;

            while ((value >= step && i < measures.length && !scaleFactorIndex) || (scaleFactorIndex != null && i < scaleFactorIndex)) {
                value = value / step;
                i += 1;
            }

            if (decimal != null && decimal > 0) {
                value = value * Math.pow(10, decimal);
                value = Math.round(value);
                value = value / Math.pow(10, decimal);
                value = value.toFixed(decimal);
            } else {
                if (i > 0) {
                    /* Has a decimal number due to the step */
                    value = Number(value.toFixed(1));
                } else {
                    /* Has a decimal number */
                    value = Math.round(value);
                }
            }

            if (negativeValue && !absoluteValue) { value *= -1; }
            let valString = `${value}`;
            // if (valString.length < maxLenValue) {
            //     valString = valString.padEnd(maxLenValue - valString.length, " ");
            // }
            let mString = `${measures[i]}`;
            // if (mString.length < maxLenUm) {
            //     mString = mString.padStart(maxLenUm - mString.length, "_");
            // }
            let text = `${valString} ${mString}`;
            return text;
        };
        return formatter;
    }

    function formatDateTime(date, type = 'datetime') {
        if (!date) {
            return '';
        }

        // localize to server timestamp
        date = utc_s_to_server_date(date);

        // check that date exists
        if (isNaN(date.getTime())) {
            return '';
        }

        const now = new Date();

        // create date only
        const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        const inputDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
        // Calculate difference in days
        const delta_days = Math.floor((today.getTime() - inputDate.getTime()) / (1000 * 60 * 60 * 24));

        // Time formatter
        const time_formatter = date.toLocaleTimeString('en-GB', {
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            hour12: false
        });

        let formatted_date = '';

        if (delta_days === 0) {
            // Today
            formatted_date = 'Today';
            if (type === 'date_only') {
                return formatted_date;
            }
            return `${time_formatter}`;
        } else if (delta_days === 1) {
            // Yesterday
            formatted_date = 'Yesterday';
        } else if (delta_days >= 2 && delta_days <= 6) {
            // Within the last week - show weekday
            formatted_date = date.toLocaleDateString('en-GB', { weekday: 'short' });
        } else if (delta_days >= 7 && delta_days <= 365) {
            // Within the last year - show month and day
            formatted_date = date.toLocaleDateString('en-GB', {
                month: 'short',
                day: 'numeric'
            });
        } else if (delta_days > 365) {
            // More than one year ago - show full date
            formatted_date = date.toLocaleDateString('en-GB', {
                year: 'numeric',
                month: 'short',
                day: 'numeric'
            });
        } else {
            // Future dates (negative delta_days)
            formatted_date = date.toLocaleDateString('en-GB', {
                year: 'numeric',
                month: 'short',
                day: 'numeric'
            });
        }

        // Return based on type
        if (type === 'date_only') {
            return formatted_date;
        }

        return `${formatted_date}, ${time_formatter}`;
    }

    function utc_s_to_server_date(utc_seconds) {
        let utc = utc_seconds * 1000;
        let d_local = new Date(utc);
        let local_offset = d_local.getTimezoneOffset();
        let server_offset = moment.tz(utc, ntop_zoneinfo)._offset;
        let offset_minutes = server_offset + local_offset;
        let offset_ms = offset_minutes * 1000 * 60;
        var d_server = new Date(utc + offset_ms);
        return d_server;
    }

    function server_date_to_date(date, format) {
        let utc = date.getTime();
        let local_offset = date.getTimezoneOffset();
        let server_offset = moment.tz(utc, ntop_zoneinfo)._offset;
        let offset_minutes = server_offset + local_offset;
        let offset_ms = offset_minutes * 1000 * 60;
        var d_local = new Date(utc - offset_ms);
        return d_local;
    }

    // formats the as as 
    function formatAsn(asn, as_name) {
        if (asn !== 0) {
            return `${asn} (${as_name})`
        }
    }

    function getMidnightEpoch(utc_seconds) {
        let utc = utc_seconds * 1000;
        const utc_midnight = moment.tz(utc, ntop_zoneinfo).startOf('day');
        return utc_midnight.valueOf() / 1000;
    }

    /* Makes uppercase each first character of a string */
    function capitalizeFirstLetters(str) {
        return str
            .split(' ')
            .map(word => {
                if (word.length === 0) return word;
                return word[0].toUpperCase() + word.slice(1);
            })
            .join(' ');
    }

    // This function is used to format a standard a tag, with value - name
    function formatHTMLaTagNameValue(value, name, url, short_version) {
        let a_tag = '';
        if (name && name != value) {
            if (short_version) {
                a_tag = `<a href="${url}" data-bs-toggle='tooltip' data-bs-placement='bottom' title='${value}'>${name}</a>`;
            } else {
                a_tag = `<a href="${url}">${name} (${value})</a>`;
            }
        } else {
            a_tag = `<a href="${url}">${value}</a>`;
        }

        return a_tag
    }

    const formatterUtils = function () {
        return {
            types,
            getUnitMeasureLen,
            getFormatter,
            getScaleFactorIndex,
            formatAccounting,
            formatDateTime,
            utc_s_to_server_date,
            server_date_to_date,
            formatAsn,
            getMidnightEpoch,
            capitalizeFirstLetters,
            formatHTMLaTagNameValue
        };
    }();

    window.NtopUtils = NtopUtils$1;

    window.datatableInitRefreshRows = datatableInitRefreshRows;
    window.datatableForEachRow = datatableForEachRow;
    window.datatableIsEmpty = datatableIsEmpty;
    window.datatableRemoveEmptyRow = datatableRemoveEmptyRow;
    window.datatableAddEmptyRow = datatableAddEmptyRow;
    window.datatableGetNumDisplayedItems = datatableGetNumDisplayedItems;
    window.datatableGetByForm = datatableGetByForm;
    window.datatableUndoAddRow = datatableUndoAddRow;
    window.datatableAddButtonCallback = datatableAddButtonCallback;
    window.datatableAddDeleteButtonCallback = datatableAddDeleteButtonCallback;
    window.datatableAddActionButtonCallback = datatableAddActionButtonCallback;
    window.datatableAddFilterButtonCallback = datatableAddFilterButtonCallback;
    window.datatableAddLinkButtonCallback = datatableAddLinkButtonCallback;
    window.datatableMakeSelectUnique = datatableMakeSelectUnique;
    window.datatableIsLastPage = datatableIsLastPage;
    window.datatableGetColumn = datatableGetColumn;
    window.datatableGetColumnIndex = datatableGetColumnIndex;

    window.$.fn.modalHandler = modalHandler;

    window.ebpfUtils = ebpfUtils;
    window.ntopFormatterUtils = formatterUtils;

    function makeUniqueValidator(items_function) {
      return function(field) {
        var cmp_name = field.val();
        var count = 0;

        // this will be checked separately, with 'required' argument
        if(! cmp_name)
          return true;

        items_function(field).each(function() {
          var name = $(this).val();
          if (name == cmp_name)
            count = count + 1;
        });

        return count == 1;
      }
    }

    function memberValueValidator(input) {
      var member = input.val();
      if (member === "") return true;

      return NtopUtils.is_mac_address(member) || NtopUtils.is_network_mask(member, true);
    }

    function makePasswordPatternValidator(pattern) {
      return function passwordPatternValidator(input) {
        // required is checked separately
        if(!input.val()) return true;
        return $(input).val().match(pattern);
      }
    }

    function passwordMatchValidator(input) {
      var other_input = $(input).closest("form").find("[data-passwordmatch]").not(input);
      if(!input.val() || !other_input.val()) return true;
      return other_input.val() === input.val();
    }

    function poolnameValidator(input) {
      // required is checked separately
      if(!input.val()) return true;
      return $(input).val().match(/^[a-z0-9_]*$/);
    }

    function passwordMatchRecheck(form) {
      var items = $(form).find("[data-passwordmatch]");
      var not_empty = 0;

      items.each(function() {
        if($(this).val() != "") not_empty++;
      });

      if(not_empty == items.length) items.trigger('input');
    }

    function hostOrMacValidator(input) {
      var host = input.val();

      /* Handled separately */
      if (host === "") return true;

      return NtopUtils.is_mac_address(host) || NtopUtils.is_good_ipv4(host) || NtopUtils.is_good_ipv6(host);
    }

    function ipAddressValidator(input) {
      var host = input.val();

      /* Handled separately */
      if (host === "") return true;

      return NtopUtils.is_good_ipv4(host) || NtopUtils.is_good_ipv6(host);
    }

    var filters_to_validate = {};

    function bpfValidator(filter_field, sync = false) {
      var filter = filter_field.val();

      if (filter.trim() === "") {
        return true;
      }

      var key = filter_field.attr("name");
      var timeout = 250;

      if (!filters_to_validate[key])
         filters_to_validate[key] = {ajax_obj:null, valid:true, timer:null, submit_remind:false, last_val:null};
      var status = filters_to_validate[key];

      var sendAjax = function () {
        status.timer = null;

        var finally_check = function (valid) {
          status.ajax_obj = null;
          status.valid = valid;
          status.last_val = filter;
        };

        if (status.last_val !== filter) {
          if (status.ajax_obj)
            status.ajax_obj.abort();

          status.ajax_obj = $.ajax({
            type: "GET",
            url: `${http_prefix}/lua/pro/rest/v2/check/filter.lua`,
            async: !sync,
            data: {
              query: filter,
            }, error: function() {
              finally_check(status.valid);
            }, success: function(data) {
              var valid = data.response ? true : false;
              finally_check(valid);
            }
          });
        } else {
          // possibly process the reminder
          finally_check(status.valid);
        }
      };

      if (sync) {
        sendAjax();
      } else if (status.last_val === filter) ; else {
        if (status.timer) {
          clearTimeout(status.timer);
          status.submit_remind = false;
        }
        status.timer = setTimeout(sendAjax, timeout);
      }

      return status.valid;
    }

    window.makeUniqueValidator = makeUniqueValidator;
    window.memberValueValidator = memberValueValidator;
    window.makePasswordPatternValidator = makePasswordPatternValidator;
    window.passwordMatchValidator = passwordMatchValidator;
    window.poolnameValidator = poolnameValidator;
    window.passwordMatchRecheck = passwordMatchRecheck;
    window.hostOrMacValidator = hostOrMacValidator;
    window.ipAddressValidator = ipAddressValidator;
    window.bpfValidator = bpfValidator;

    /**
        (C) 2022 - ntop.org    
    */

    const ntopng_sync$1 = function () {
        let components_ready = {};
        let subscribers = [];
        return {
            ready: function (component_name) {
                components_ready[component_name] = true;
                subscribers.filter((s) => s.component_name == component_name).forEach((s) => s.resolve());
                subscribers = subscribers.filter((s) => s.component_name != component_name);
            },
            on_ready: function (component_name) {
                return new Promise((resolve, rejevt) => {
                    if (components_ready[component_name]) {
                        resolve();
                        return;
                    }
                    subscribers.push({ resolve, component_name, completed: false });
                });
            },
        };
    }();

    /**
    * Utility globals functions.
    */
    const ntopng_utility$1 = function () {
        let global_http_headers = {};
        return {
            is_array: function (e) {
                return Array.isArray(e);
            },
            is_object: function (e) {
                return typeof e === 'object'
                    && !this.is_array(e)
                    && e !== null;
            },
            /**
             * Deep copy of a object.
             * @param {object} obj.
             * @returns {object}.
             */
            clone: function (obj) {
                if (obj == null) { return null; }
                if (this.is_object(obj)) {
                    /* This does the deep copy using jquery for objects */
                    return $.extend(true, {}, obj);
                } else if (Array.isArray(obj)) {
                    /* This does the deep copy using jquery for other types */
                    let res = [];
                    for (let i = 0; i < obj.length; i += 1) {
                        let el = this.clone(obj[i]);
                        res.push(el);
                    }
                    return res;
                } else {
                    // return JSON.parse(JSON.stringify(obj))
                    return obj;
                }
            },
            object_to_array: function (obj) {
                if (obj == null) { return []; }
                let array = [];
                for (let key in obj) {
                    array.push(obj[key]);
                }
                return array;
            },
            get_utc_seconds: function (utc_ms) {
                if (utc_ms == null) { utc_ms = Date.now(); }
                return Number.parseInt(utc_ms / 1000);
            },
            get_timeframes_dict: function () {
                const min = 60;
                let t_day = new Date();
                let t_week = new Date();
                let t_month = new Date();
                let t_year = new Date();
                return {
                    "live": 0,
                    "min": min,
                    "5_min": min * 5,
                    "10_min": min * 10,
                    "30_min": min * 30,
                    hour: min * 60,
                    "2_hours": 2 * min * 60,
                    "6_hours": 6 * min * 60,
                    "12_hours": 12 * min * 60,
                    day: this.get_utc_seconds(Date.now() - t_day.setDate(t_day.getDate() - 1)),
                    week: this.get_utc_seconds(Date.now() - t_week.setDate(t_week.getDate() - 7)),
                    month: this.get_utc_seconds(Date.now() - t_month.setMonth(t_month.getMonth() - 1)),
                    year: this.get_utc_seconds(Date.now() - t_year.setMonth(t_year.getMonth() - 12)),
                };
            },
            // given valid interval string get time in seconds
            get_timeframe_from_timeframe_id: function (timeframe_id) {
                let timeframes_dict = this.get_timeframes_dict();
                // timeframes_dict[interval_string] == null => key is not present
                if (timeframes_dict[timeframe_id] == null) {
                    throw `Wrong timeframe_id passed ${timeframe_id}, valid intervals are: ${Object.keys(timeframes_dict).join(", ")}`;
                }
                return timeframes_dict[timeframe_id];
            },
            round_time_by_timeframe_id: function (ts, timeframe_id) {
                const timeframe = this.get_timeframe_from_timeframe_id(timeframe_id);
                if (timeframe) {
                    return ts - (ts % timeframe);
                }
                return ts
            },
            // method to set default epoch begin to 30_min ago
            set_default_time_interval: function (time_interval_id = "30_min", round_timeframe_id) {
                let epoch = {
                    epoch_begin: ntopng_url_manager$1.get_url_entry("epoch_begin"),
                    epoch_end: ntopng_url_manager$1.get_url_entry("epoch_end"),
                };
                const now_s = this.get_utc_seconds(Date.now());
                let seconds_in_interval = this.get_timeframe_from_timeframe_id(time_interval_id);
                epoch.epoch_begin = now_s - seconds_in_interval;
                epoch.epoch_end = now_s;
                if (round_timeframe_id != null) {
                    epoch.epoch_begin = this.round_time_by_timeframe_id(epoch.epoch_begin, round_timeframe_id);
                    epoch.epoch_end = this.round_time_by_timeframe_id(epoch.epoch_end, round_timeframe_id);
                }
                ntopng_url_manager$1.set_key_to_url("epoch_begin", epoch.epoch_begin);
                ntopng_url_manager$1.set_key_to_url("epoch_end", epoch.epoch_end);

                return epoch;
            },
            //should take a string as parameter that represent time: min, 5_min, 30_min, hour, 2_hours, 6_hours, 12_hours, day, week, month, year. ID time_interval_id is null, default must be 30_min
            // return epoch_interval only if epoch url is set
            check_and_set_default_time_interval: function (time_interval_id = "30_min", f_condition, get_epoch = false, round_timeframe_id) {
                let epoch = this.get_url_epoch_interval();

                // if time_interval_id is 30 (default)
                if (epoch.epoch_begin == null || epoch.epoch_end == null || (f_condition != null && f_condition(epoch) == true)) {
                    epoch = this.set_default_time_interval(time_interval_id, round_timeframe_id);
                    return epoch;
                }
                if (get_epoch == true) {
                    return epoch;
                }
                return null;
            },
            get_url_epoch_interval: function () {
                let epoch = {
                    epoch_begin: ntopng_url_manager$1.get_url_entry("epoch_begin"),
                    epoch_end: ntopng_url_manager$1.get_url_entry("epoch_end"),
                };
                return epoch;
            },
            from_utc_s_to_server_date: function (utc_seconds) {
                let utc = utc_seconds * 1000;
                let d_local = new Date(utc);
                let local_offset = d_local.getTimezoneOffset();
                let server_offset = moment.tz(utc, ntop_zoneinfo)._offset;
                let offset_minutes = server_offset + local_offset;
                let offset_ms = offset_minutes * 1000 * 60;
                var d_server = new Date(utc + offset_ms);
                return d_server;
            },
            get_date_format: async function (is_range_picker, csrf, http_prefix) {
                const rest_params = {
                    csrf: csrf
                };

                let date_format_url = `${http_prefix}/lua/rest/v2/get/timeseries/date_format.lua`;
                const url = NtopUtils.buildURL(date_format_url, {
                    is_range_picker: is_range_picker
                });
                return await ntopng_utility$1.http_request(url, rest_params);
            },
            from_utc_to_server_date_format: function (utc_ms, format) {
                if (format == null) { format = "DD/MMM/YYYY HH:mm:ss"; }
                let m = moment.tz(utc_ms, ntop_zoneinfo);
                let tz_server = m.format(format);
                return tz_server;
            },
            copy_object_keys: function (source_obj, dest_obj, recursive_object = false) {
                if (source_obj == null) {
                    return;
                }
                for (let key in source_obj) {
                    if (source_obj[key] == null) { continue; }
                    /* Security check for Prototype pollution vulnerability */
                    if (key === "__proto__" || key === "constructor") { continue; }
                    if (recursive_object == true && this.is_object(source_obj[key]) && this.is_object(dest_obj[key])) {
                        this.copy_object_keys(source_obj[key], dest_obj[key], recursive_object);
                    } else {
                        dest_obj[key] = source_obj[key];
                    }
                }
            },
            get_cve_details_url(cve_id, scan_type) {
                //  IMPORTANT: The retrieved value must match the value in 
                //  scripts/lua/modules/vulnerability_scan/cve_utils.lua for the 'cve_utils.getDocURL' function.
                if (scan_type == "cve") {
                    return `https://nvd.nist.gov/vuln/detail/${cve_id}`;
                } else if (scan_type == "openvas") {
                    return `https://vulners.com/openvas/OPENVAS:${cve_id}`;
                }
            },
            set_http_globals_headers(headers) {
                global_http_headers = headers;
            },
            http_post_request: async function (url, params, throw_exception, not_unwrap) {
                let headers = {
                    'Content-Type': 'application/json'
                };
                if (params.csrf == null) {
                    throw `NULL csrf in ${url} POST request.`;
                }
                return this.http_request(url, { method: 'post', headers, body: JSON.stringify(params) }, throw_exception, not_unwrap);
            },
            http_request: async function (url, options, throw_exception, not_unwrap) {
                try {
                    if (options == null) {
                        options = {};
                    }
                    if (options.headers == null) {
                        options.headers = {};
                    }
                    if (options.headers != null && global_http_headers != null) {
                        options.headers = {
                            ...options.headers,
                            ...global_http_headers,
                        };
                    }
                    let res = await fetch(url, options);
                    if (res.ok === false) {
                        console.error(`http_request ${url}\n ok == false`);
                        console.error(res);
                        return null;
                    }
                    let json_res = await res.json();
                    if (not_unwrap === true) { return json_res; }
                    return json_res.rsp;
                } catch (err) {
                    console.error(err);
                    console.error("URL: " + url);
                    if (throw_exception == true) { throw err; }
                    return null;
                }
            },
            download_URI: function (uri, name) {
                var link = document.createElement("a");
                link.download = name;
                link.href = uri;
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            },
            get_random_string: function () {
                return Math.random().toString(16).substr(2, 8);
            },
            string_hash_code: function (s) {
                let hash = 0, i, chr;
                if (s.length === 0) return hash;
                for (i = 0; i < s.length; i++) {
                    chr = s.charCodeAt(i);
                    hash = ((hash << 5) - hash) + chr;
                    hash |= 0; // Convert to 32bit integer
                }
                return hash;
            },
            mod: function (x, y) {
                x = x % y;
                if (x < 0) { x += y; }
                return x;
            }
        }
    }();

    /**
    * Allows to manage the application global status.
    * The status is incapsulated into the url.
    */
    const ntopng_status_manager$1 = function () {
        let global_status = {};
        /** @type {{ [id: string]: (status: object) => void}} */
        let subscribers = {}; // dictionary of { [id: string]: f_on_ntopng_status_change() }
        const clone = (e) => ntopng_utility$1.clone(e);

        const replace_global_status = function (status) {
            global_status = status;
        };

        /**
         * Notifies the status to all subscribers with id different from skip_id.
         * @param {object} status object that represent the application status.
         * @param {string} skip_id if != null doesn't notify the subscribers with skip_id identifier.
         */
        const notify_subscribers = function (status, skip_id) {
            for (let id in subscribers) {
                if (id == skip_id) { continue; }
                let f_on_change = subscribers[id];
                f_on_change(clone(status));
            }
        };

        return {
            /**
             * Gets the current global application status.
             * @returns {object}
             */
            get_status: function (not_clone) {
                if (not_clone == true) {
                    return global_status;
                }
                return clone(global_status);
            },

            update_subscribers: function () {
                const status = this.get_status();
                notify_subscribers(status);
            },

            /**
             * Allows to subscribers f_on_change callback on status change event.
             * @param {string} id an identifier of the subscribtion. 
             * @param {(status:object) => void} f_on_change callback that take object status as param.
             * @param {boolean} get_init_notify if true the callback it's immediately called with the last status available.
             */
            on_status_change: function (id, f_on_change, get_init_notify) {
                subscribers[id] = f_on_change;
                if (get_init_notify == true) {
                    let status = this.get_status();
                    f_on_change(clone(status));
                }
            },

            /**
             * Raplaces the application status and notifies the new status to all subscribers.
             * Notifies the new status to all subscribers.
             * @param {Object} status object that represent the application status.
             * @param {string} skip_id if != null doesn't notify the subscribers with skip_id identifier.
             */
            replace_status: function (status, skip_id) {
                replace_global_status(status);
                notify_subscribers(status, skip_id);
            },

            /**
             * Adds or replaces all obj param keys to the application status.
             * Notifies the new status to all subscribers.
             * @param {Object} obj object to add or edit to the application status. 
             * @param {string} skip_id if != null doesn't notify the subscribers with skip_id identifier.
             */
            add_obj_to_status: function (obj, skip_id) {
                let new_status = this.get_status();
                ntopng_utility$1.copy_object_keys(obj, new_status);
                this.replace_status(new_status, skip_id);
            },

            /**
             * Adds or replaces the value key to the application status.
             * Notifies the new status to all subscribers.
             * @param {string} key key to adds or replaces.
             * @param {any} value value to adds or replaces.
             * @param {*} skip_id if != null doesn't notify the subscribers with skip_id identifier.
             */
            add_value_to_status: function (key, value, skip_id) {
                let new_status = this.get_status();
                new_status[key] = value;
                // /* This is needed to have muliple filters for the same key */
                // (new_status[key] && new_status[key].search(value) === -1) ? new_status[key] += "," + value : new_status[key] = value

                this.replace_status(new_status, skip_id);
            },
        }
    }();

    const ntopng_params_url_serializer = {
        // filters: function(key, filters) {
        // 	if (filters == null) { return ""; }
        // 	let filters_groups = {};
        // 	filters.forEach((f) => {
        // 	    let group = filters_groups[f.id];
        // 	    if (group == null) {
        // 		group = [];
        // 		filters_groups[f.id] = group;
        // 	    }
        // 	    group.push(f);
        // 	});
        // 	let url_params_array = [];
        // 	for (let f_id in filters_groups) {
        // 	    let group = filters_groups[f_id];
        // 	    let url_values = group.filter((f) => f.value != null && f.operator != null && f.operator != "").map((f) => `${f.value};${f.operator}`).join(",");
        // 	    let url_params = ntopng_url_manager.serialize_param(f_id, url_values);
        // 	    url_params_array.push(url_params);
        // 	}
        // 	return url_params_array.join("&");
        // },
    };

    const ntopng_url_manager$1 = function () {
        /** @type {{ [key: string]: (obj: any) => string}} */
        let custom_params_serializer = {};
        ntopng_utility$1.copy_object_keys(ntopng_params_url_serializer, custom_params_serializer);

        return {

            get_url_params: function () {
                return window.location.search.substring(1);
            },

            get_url_search_params: function (url) {
                if (url == null) {
                    url = this.get_url_params();
                }
                // for(const [key, value] of entries) {
                const url_params = new URLSearchParams(url);
                return url_params;
            },

            get_url_entries: function (url) {
                const url_params = this.get_url_search_params(url);
                const entries = url_params.entries();
                return entries;
            },

            get_url_entry: function (param_name, url) {
                let entries = this.get_url_entries(url);
                for (const [key, value] of entries) {
                    if (key == param_name) { return value; }
                }
                return null;
            },

            get_url_object: function (url) {
                let entries = this.get_url_entries(url);
                let obj = {};
                for (const [key, value] of entries) {
                    obj[key] = value;
                }
                return obj;
            },

            open_new_window: function (url) {
                if (url == null) {
                    url = window.location;
                }
                window.open(url);
            },

            reload_url: function () {
                window.location.reload();
            },

            go_to_url: function (url) {
                window.history.pushState({}, '', window.location);
                window.location.replace(url);
            },

            replace_url: function (url_params) {
                window.history.replaceState({}, null, `?${url_params}`);
            },

            replace_url_and_reload: function (url_params) {
                this.replace_url(url_params);
                this.reload_url();
            },

            serialize_param: function (key, value) {
                if (value == null) {
                    value = "";
                }
                return `${key}=${encodeURIComponent(value)}`;
            },

            set_custom_key_serializer: function (key, f_get_url_param) {
                custom_params_serializer[key] = f_get_url_param;
            },

            /**
             * Convert js object into a string that represent url params.
             * Uses custom serializer if set.
             * @param {object} obj.
             * @returns {string}.
             */
            obj_to_url_params: function (obj) {
                let params = [];
                const default_serializer = this.serialize_param;
                for (let key in obj) {
                    let serializer = custom_params_serializer[key];
                    if (serializer == null) {
                        serializer = default_serializer;
                    }
                    let param = serializer(key, obj[key]);
                    params.push(param);
                }
                let url_params = params.join("&");
                return url_params;
            },

            delete_params: function (params_key) {
                let search_params = this.get_url_search_params();
                params_key.forEach((p) => {
                    search_params.delete(p);
                });
                this.replace_url(search_params.toString());
            },

            delete_key_from_url: function (key) {
                let search_params = this.get_url_search_params();
                search_params.delete(key);
                this.replace_url(search_params.toString());
            },

            set_key_to_url: function (key, value) {
                if (value == null) { value = ""; }
                let search_params = this.get_url_search_params();
                search_params.set(key, value);
                this.replace_url(search_params.toString());
            },

            add_obj_to_url: function (url_params_obj, url) {
                let new_url_params = this.obj_to_url_params(url_params_obj);
                let search_params = this.get_url_search_params(url);
                let new_entries = this.get_url_entries(new_url_params);
                for (const [key, value] of new_entries) {
                    search_params.set(key, value);
                }
                let new_url = search_params.toString();
                if (url != null) { return new_url; }
                this.replace_url(new_url);
            },
        }
    }();

    // export const ntopng_params_manager = function() {
    //     const new = function(params_in_url) {
    //     }
    //     return {
    //     }
    // }

    /**
    * Object that represents a list of prefedefined events that represent the status.
    */
    const ntopng_events$1 = {
        EPOCH_CHANGE: "epoch_change", // { epoch_begin: number, epoch_end: number }
        FILTERS_CHANGE: "filters_change", // {filters: {id: string, operator: string, value: string}[] }
    };

    const ntopng_events_compare = {
        EPOCH_CHANGE: function (new_status, old_status) {
            return new_status.epoch_begin != old_status.epoch_begin
                || new_status.epoch_end != old_status.epoch_end;
        },
        FILTERS_CHANGE: function (new_status, old_status) {
            return (new_status.filters == null && old_status.filters != null)
                || (new_status.filters != null && old_status.filters == null)
                || (new_status.filters != null && old_status.filters != null &&
                    (
                        (new_status.filters.length != old_status.filters.length)
                        || (new_status.filters.some((f_new) => old_status.filters.find((f_old) => f_old.id == f_new.id) == null))
                    )
                );
        },
    };

    /**
    * Object that represents a list of prefedefined custom events.
    */
    const ntopng_custom_events$1 = {
        SHOW_MODAL_FILTERS: "show_modal_filters", // {id: string, operator: string, value: string}
        MODAL_FILTERS_APPLY: "modal_filters_apply", // {id: string, label: string, operator: string, value: string, value_label: string}
        SHOW_GLOBAL_ALERT_INFO: "show_global_alert_info", // html_text: string
        VIS_DATA_LOADED: "vis_data_loaded",
        CHANGE_PAGE_TITLE: "change_page_title",
        DATATABLE_LOADED: "datatable_loaded",
        GET_INTERFACE_FATA: "get_interface_data", // object returned by /lua/rest/v2/get/interface/data.lua
        COMPONENT_EPOCH_INTERVAL_CHANGE: "component_epoch_interval_change", // { epoch_begin: number, epoch_end: number }
    };

    /**
    * A global events service that allows to manage the application global status.
    * The status is incapsulated into the url.
    */
    const ntopng_events_manager$1 = function () {
        const events_manager_id = "events_manager";
        let status = {};

        /** @type {{ [event_name: string]: { [id: string]: (status: object) => void}}} */
        let events_subscribers = {}; // dictionary of { [event_name: string]: { [id: string]: f_on_event }

        const clone = (e) => ntopng_utility$1.clone(e);

        /**
         * Notifies the status to all subscribers with id different from skip_id.
         * @param {{ [id: string]: (status: object) => void}} subscribers dictionary of id => f_on_event().
         * @param {object} status object that represent the application status.
         * @param {string} skip_id if != null doesn't notify the subscribers with skip_id identifier.
         */
        const notify_subscribers = function (subscribers, status, skip_id) {
            for (let id in subscribers) {
                if (id == skip_id) { continue; }
                let f_on_change = subscribers[id];
                f_on_change(clone(status));
            }
        };

        /**
         * A callback that dispatches each event to all subscribers.
         * @param {object} new_status 
         */
        const on_status_change = function (new_status) {
            for (let event_name in ntopng_events$1) {
                let f_compare = ntopng_events_compare[event_name];
                if (f_compare(new_status, status) == true) {
                    let subscribers = events_subscribers[event_name];
                    notify_subscribers(subscribers, new_status);
                }
            }

            status = new_status;
        };

        const get_event_for_single_dest = (event, dest_id) => {
            return `${event}_${dest_id}`;
        };

        ntopng_status_manager$1.on_status_change(events_manager_id, on_status_change, true);

        const emit = function (event, params, skip_id, dest_id) {
            if (dest_id != null) {
                event = get_event_for_single_dest(event, dest_id);
            }
            let subscribers = events_subscribers[event];
            if (subscribers == null) { return; }
            notify_subscribers(subscribers, params, skip_id);
        };

        const on_event = function (id, event, f_on_event, get_init_notify, is_single_dest_event) {
            if (is_single_dest_event == true) {
                event = get_event_for_single_dest(event, id);
            }
            if (events_subscribers[event] == null) {
                events_subscribers[event] = {};
            }
            if (get_init_notify == true) {
                let status = ntopng_status_manager$1.get_status();
                f_on_event(clone(status));
            }
            events_subscribers[event][id] = f_on_event;
        };

        return {
            emit_custom_event: function (event, params, dest_id) {
                emit(event, params, null, dest_id);
            },
            on_custom_event: function (id, event, f_on_event, is_single_dest_event) {
                on_event(id, event, f_on_event, null, is_single_dest_event);
            },
            /**
             * Changes the application status and emits the new status to all subcribers registered to the event. 
             * @param {string} event event name.
             * @param {object} new_status object to add or edit to the application status.
             * @param {string} skip_id if != null doesn't notify the subscribers with skip_id identifier.
             */
            emit_event: function (event, new_status, skip_id) {
                emit(event, new_status, skip_id);
                ntopng_status_manager$1.add_obj_to_status(new_status, events_manager_id);
            },
            /**
             * Allows to subscribers f_on_event callback on status change on event event_name.
             * @param {string} id an identifier of the subscribtion. 
             * @param {string} event event name. 
             * @param {(status:object) => void} f_on_event callback that take object status as param.
             * @param {boolean} get_init_notify if true the callback it's immediately called with the last status available.
             */
            on_event_change: function (id, event, f_on_event, get_init_notify) {
                on_event(id, event, f_on_event, get_init_notify);
            },
        };
    }();

    window.ntopng_events = ntopng_events$1;
    window.ntopng_events_manager = ntopng_events_manager$1;
    window.ntopng_status_manager = ntopng_status_manager$1;
    window.ntopng_utility = ntopng_utility$1;
    window.ntopng_url_manager = ntopng_url_manager$1;
    window.ntopng_sync = ntopng_sync$1;
    window.ntopng_custom_events = ntopng_custom_events$1;

    // http://jsfiddle.net/stephenboak/hYuPb/

    // Wrapper function
    function do_pie(name, update_url, url_params, units, refresh) {
    	var pie = new PieChart(name, update_url, url_params, units, refresh);
    	if (refresh)
    		pie.setInterval(setInterval(function () { pie.update(); }, refresh));

    	// Return new class instance, with
    	return pie;
    }

    function PieChart(name, update_url, url_params, units, refresh) {

    	// Add object properties like this
    	this.name = name;
    	this.update_url = update_url;
    	this.url_params = url_params;
    	this.units = units;
    	this.refresh = refresh;

      let streakerDataAdded = [];
      let paths = "";
      let lines = [];
      let valueLabels = [];
      let nameLabels = [];

    	var pieData = [];
    	var oldPieData = [];
    	var filteredPieData = [];
    	var rsp = create_pie_chart(name, units);
    	var arc_group = rsp[0];
    	var donut = rsp[1];	
    	var totalValue = rsp[2];
    	var color = rsp[4];
    	var tweenDuration = rsp[5];
    	var arc = rsp[6];
    	var label_group = rsp[7];	
    	var r = rsp[9];
    	var textOffset = rsp[10];


    	// to run each time data is generated

    	this.update = function () {
    		// console.log(this.name);
    		// console.log(this.url_params);
    		$.ajax({
    			type: 'GET',
    			url: this.update_url,
    			data: this.url_params,
    			success: function (content) {
    				let parsed_content;

    				if (typeof (content) == "object")
    					parsed_content = content;
    				else if (typeof (content) == "string")
    					parsed_content = jQuery.parseJSON(content);

    				if (parsed_content)
    					update_pie_chart(parsed_content);
    			}
    		});
    	};

    	///////////////////////////////////////////////////////////
    	// STREAKER CONNECTION ////////////////////////////////////
    	///////////////////////////////////////////////////////////

    	// Needed to draw the pie immediately
    	this.update();
    	this.update();

    	// var updateInterval = window.setInterval(update, refresh);

    	function compare_by_label(a, b) {
    		if (a.label < b.label) {
    			return -1;
    		} else if (a.label > b.label) {
    			return 1;
    		} else {
    			return 0;
    		}
    	}

    	function update_pie_chart(data) {
    		if (data.rsp) // detect REST API v1
    			data = data.rsp;

    		data.sort(compare_by_label);
    		streakerDataAdded = data;
    		oldPieData = filteredPieData;
    		pieData = donut(streakerDataAdded);

    		var totalOctets = 0;
    		filteredPieData = pieData.filter(filterData);
    		function filterData(element, index, array) {
    			element.name = streakerDataAdded[index].label;
    			element.value = streakerDataAdded[index].value;
    			element.url = streakerDataAdded[index].url;
    			totalOctets += element.value;
    			return (element.value > 0);
    		}

    		if ((filteredPieData.length > 0) && (oldPieData.length > 0)) {
    			//REMOVE PLACEHOLDER CIRCLE
    			arc_group.selectAll("circle").remove();

    			if (totalValue) {
    				totalValue.text(function () {
    					var kb = totalOctets / 1024;
    					return kb.toFixed(1);
    					//return bchart.label.abbreviated(totalOctets*8);
    				});
    			}

    			//DRAW ARC PATHS
    			paths = arc_group.selectAll("path").data(filteredPieData);
    			paths.enter().append("svg:path")
    				.attr("stroke", "white")
    				.attr("stroke-width", 0.5)
    				.attr("fill", function (d, i) { return color(i); })
    				.transition()
    				.duration(tweenDuration)
    				.attrTween("d", pieTween);
    			paths
    				.transition()
    				.duration(tweenDuration)
    				.attrTween("d", pieTween);
    			paths.exit()
    				.transition()
    				.duration(tweenDuration)
    				.attrTween("d", removePieTween)
    				.remove();

    			//DRAW TICK MARK LINES FOR LABELS
    			lines = label_group.selectAll("line").data(filteredPieData);
    			lines.enter().append("svg:line")
    				.attr("x1", 0)
    				.attr("x2", 0)
    				.attr("y1", -r - 3)
    				.attr("y2", -r - 8)
    				.attr("stroke", "gray")
    				.attr("transform", function (d) {
    					return "rotate(" + (d.startAngle + d.endAngle) / 2 * (180 / Math.PI) + ")";
    				});
    			lines.transition()
    				.duration(tweenDuration)
    				.attr("transform", function (d) {
    					return "rotate(" + (d.startAngle + d.endAngle) / 2 * (180 / Math.PI) + ")";
    				});
    			lines.exit().remove();

    			//DRAW LABELS WITH PERCENTAGE VALUES
    			valueLabels = label_group.selectAll("text.value").data(filteredPieData)
    				.attr("dy", function (d) {
    					if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
    						return 5;
    					} else {
    						return -7;
    					}
    				})
    				.attr("text-anchor", function (d) {
    					if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
    						return "beginning";
    					} else {
    						return "end";
    					}
    				})
    				.text(function (d) {

    					const percentage = (d.value / totalOctets) * 100;
    					// approssimate the number to the third deciaml digit and show only the first decimal
    					let percentageLabel = percentage.toFixed(1) + "%";
    					return percentageLabel;
    				});

    			valueLabels.enter().append("svg:text")
    				.attr("class", "value")
    				.attr("transform", function (d) {
    					return "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) * (r + textOffset) + "," + Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) * (r + textOffset) + ")";
    				})
    				.attr("dy", function (d) {
    					if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
    						return 5;
    					} else {
    						return -7;
    					}
    				})
    				.attr("text-anchor", function (d) {
    					if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
    						return "beginning";
    					} else {
    						return "end";
    					}
    				}).text(function (d) {
    					if (totalOctets <= 1) return "";
    					const percentage = (d.value / totalOctets) * 100;
    					let percentageLabel = percentage.toFixed(1) + "%";
    					return percentageLabel;
    				});

    			valueLabels.transition().duration(tweenDuration).attrTween("transform", textTween);
    			valueLabels.exit().remove();

    			//DRAW LABELS WITH ENTITY NAMES
    			nameLabels = label_group.selectAll("text.units").data(filteredPieData)
    				.attr("dy", function (d) {
    					if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
    						return 17;
    					} else {
    						return 5;
    					}
    				})
    				.attr("text-anchor", function (d) {
    					if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
    						return "beginning";
    					} else {
    						return "end";
    					}
    				}).text(function (d) {
    					return d.name;
    				})
    				.on("click", function (d) { if (d.url) window.location.href = d.url; });

    			nameLabels.enter().append("svg:text")
    				.attr("class", "units")
    				.attr("transform", function (d) {
    					return "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) * (r + textOffset) + "," + Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) * (r + textOffset) + ")";
    				})
    				.attr("dy", function (d) {
    					if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
    						return 17;
    					} else {
    						return 5;
    					}
    				})
    				.attr("text-anchor", function (d) {
    					if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
    						return "beginning";
    					} else {
    						return "end";
    					}
    				}).text(function (d) {
    					return d.name;
    				})
    				.on("click", function (d) { if (d.url) window.location.href = d.url; });

    			nameLabels.transition().duration(tweenDuration).attrTween("transform", textTween);

    			nameLabels.exit().remove();
    		}
    	}

    	///////////////////////////////////////////////////////////
    	// FUNCTIONS //////////////////////////////////////////////
    	///////////////////////////////////////////////////////////

    	// Interpolate the arcs in data space.
    	function pieTween(d, i) {
    		var s0;
    		var e0;
    		if (oldPieData[i]) {
    			s0 = oldPieData[i].startAngle;
    			e0 = oldPieData[i].endAngle;
    		} else if (!(oldPieData[i]) && oldPieData[i - 1]) {
    			s0 = oldPieData[i - 1].endAngle;
    			e0 = oldPieData[i - 1].endAngle;
    		} else if (!(oldPieData[i - 1]) && oldPieData.length > 0) {
    			s0 = oldPieData[oldPieData.length - 1].endAngle;
    			e0 = oldPieData[oldPieData.length - 1].endAngle;
    		} else {
    			s0 = 0;
    			e0 = 0;
    		}
    		var i = d3.interpolate({ startAngle: s0, endAngle: e0 }, { startAngle: d.startAngle, endAngle: d.endAngle });
    		return function (t) {
    			var b = i(t);
    			return arc(b);
    		};
    	}

    	function removePieTween(d, i) {
    		s0 = 2 * Math.PI;
    		e0 = 2 * Math.PI;
    		var i = d3.interpolate({ startAngle: d.startAngle, endAngle: d.endAngle }, { startAngle: s0, endAngle: e0 });
    		return function (t) {
    			var b = i(t);
    			return arc(b);
    		};
    	}

    	function textTween(d, i) {
    		var a;
    		if (oldPieData[i]) {
    			a = (oldPieData[i].startAngle + oldPieData[i].endAngle - Math.PI) / 2;
    		} else if (!(oldPieData[i]) && oldPieData[i - 1]) {
    			a = (oldPieData[i - 1].startAngle + oldPieData[i - 1].endAngle - Math.PI) / 2;
    		} else if (!(oldPieData[i - 1]) && oldPieData.length > 0) {
    			a = (oldPieData[oldPieData.length - 1].startAngle + oldPieData[oldPieData.length - 1].endAngle - Math.PI) / 2;
    		} else {
    			a = 0;
    		}
    		var b = (d.startAngle + d.endAngle - Math.PI) / 2;

    		var fn = d3.interpolateNumber(a, b);
    		return function (t) {
    			var val = fn(t);
    			return "translate(" + Math.cos(val) * (r + textOffset) + "," + Math.sin(val) * (r + textOffset) + ")";
    		};
    	}

    }

    ///////////////////////////////////////////////////////////
    // PUBLIC FUNCIONTS ////////////////////////////////////
    ///////////////////////////////////////////////////////////


    PieChart.prototype.setUrlParams = function (url_params) {
    	this.url_params = url_params;
    	this.forceUpdate();
    };

    PieChart.prototype.forceUpdate = function (url_params) {
    	this.stopInterval();
    	this.update();
    	this.startInterval();
    };

    PieChart.prototype.setInterval = function (p_pieInterval) {
    	this.pieInterval = p_pieInterval;
    };

    PieChart.prototype.stopInterval = function () {
    	//disabled graph interval
    	clearInterval(this.pieInterval);
    };

    PieChart.prototype.startInterval = function () {
    	this.pieInterval = setInterval(this.update(), this.refresh);
    };
    ///////////////////////////////////////////////////////////
    // INIT FUNCIONTS ////////////////////////////////////
    ///////////////////////////////////////////////////////////

    function create_pie_chart(name, units) {
    	var w = 500; //380 - Please keep in sync with pie-chart.css
    	var h = 325; //280
    	var ir = 52; //45
    	var textOffset = 14;
    	var tweenDuration = 250;
    	var r = 116; //100;

    	if ($(name).hasClass("pie-chart-small")) {
    		w = 330;
    		h = 250;
    		r = w / 5 + 15;
    		ir = r / 2;
    	}

    	//D3 helper function to populate pie slice parameters from array data
    	var donut = d3.layout.pie().value(function (d) {
    		if (d.value == 0) { d.value = 1; } // Force to 1, in order to update the graph
    		return d.value;
    	});

    	//D3 helper function to create colors from an ordinal scale
    	var color = d3.scale.category20();

    	//D3 helper function to draw arcs, populates parameter "d" in path object
    	var arc = d3.svg.arc()
    		.startAngle(function (d) { return d.startAngle; })
    		.endAngle(function (d) { return d.endAngle; })
    		.innerRadius(ir)
    		.outerRadius(r);

    	///////////////////////////////////////////////////////////
    	// CREATE VIS & GROUPS ////////////////////////////////////
    	///////////////////////////////////////////////////////////

    	var vis = d3.select(name).append("svg:svg")
    		.attr("width", w)
    		.attr("height", h)
    		.attr("viewBox", "0 0 " + w + " " + h)
    		.attr("preserveAspectRatio", "xMidYMid");

    	//GROUP FOR ARCS/PATHS
    	var arc_group = vis.append("svg:g")
    		.attr("class", "arc")
    		.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");

    	//GROUP FOR LABELS
    	var label_group = vis.append("svg:g")
    		.attr("class", "label_group")
    		.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");

    	//GROUP FOR CENTER TEXT
    	var center_group = vis.append("svg:g")
    		.attr("class", "center_group")
    		.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");

    	//PLACEHOLDER GRAY CIRCLE
    	arc_group.append("svg:circle")
    		.attr("fill", "#EFEFEF")
    		.attr("r", r);

    	///////////////////////////////////////////////////////////
    	// CENTER TEXT ////////////////////////////////////////////
    	///////////////////////////////////////////////////////////

    	//WHITE CIRCLE BEHIND LABELS
    	center_group.append("svg:circle")
    		.attr("fill", "white")
    		.attr("r", ir);

    	var totalUnits = null;
    	var totalValue = null;
    	
    	if (units) {
    		// "TOTAL" LABEL
    		center_group.append("svg:text")
    			.attr("class", "label")
    			.attr("dy", -15)
    			.attr("text-anchor", "middle") // text-align: right
    			.text("TOTAL");

    		//TOTAL TRAFFIC VALUE
    		totalValue = center_group.append("svg:text")
    			.attr("class", "total")
    			.attr("dy", 7)
    			.attr("text-anchor", "middle") // text-align: right
    			.text("Waiting...");

    		//UNITS LABEL
    		totalUnits = center_group.append("svg:text")
    			.attr("class", "units")
    			.attr("dy", 21)
    			.attr("text-anchor", "middle") // text-align: right
    			.text(units);
    	}

    	return ([arc_group, donut, totalValue, totalUnits, color, tweenDuration, arc, label_group, center_group, r, textOffset]);
    }

    /**
     * (C) 2013-21 - ntop.org
     */

    const DEFINED_WIDGETS = {};
    /* Used to implement the on click events onto the graph */
    const DEFINED_EVENTS = {
        /* On click event used by the flow analyze section, redirect to the current url + a single filter */
        "db_analyze" : function (event, chartContext, config) {
            const { dataPointIndex } = config;
            const { filter } = config.w.config;
            let value;

            if(config.w.config.filtering_labels)
                value = config.w.config.filtering_labels[dataPointIndex];

            if(filter.length == 0 || value === undefined)
                return;

            let status = ntopng_status_manager.get_status();
            let filters = status.filters;
            filters.push({id: filter[0], operator: "eq", value: value});
            // notify that filters status is updated
            ntopng_events_manager.emit_event(ntopng_events.FILTERS_CHANGE, {filters});
        },

        "none" : function (event, chartContext, config) {
            return;
        },
        
        /* Standard on click event, redirect to the url */
        "standard" : function (event, chartContext, config) {
            const { seriesIndex, dataPointIndex } = config;
            const { series } = config.w.config;
            if (seriesIndex === -1) return;
            if (series === undefined) return;

            const serie = series[seriesIndex];
            if (serie.base_url !== undefined) {
                const default_url = (serie.start_url || ''); 
                const search = serie.data[dataPointIndex].meta.url_query;
                location.href = `${serie.base_url}?${default_url}${search}`;
            }
        },
    };

    const DEFINED_TOOLTIP = {
        /* On click event used by the flow analyze section, redirect to the current url + a single filter */
        "format_bytes" : function(value, { config, seriesIndex, dataPointIndex }) {
            return NtopUtils.bytesToSize(value);
        },

        "format_pkts" : function(value, { config, seriesIndex, dataPointIndex }) {
            return NtopUtils.formatPackets(value);
        },

        /* On click event used by the flow analyze section, redirect to the current url + a single filter */
        "format_value" : function(value, { config, seriesIndex, dataPointIndex }) {
            return NtopUtils.formatValue(value);
        },

        "format_multiple_date" : function(value, { config, seriesIndex, dataPointIndex }) {
            return new Date(value[0]) + " - " + new Date(value[1])
        },

        /*
         *  This formatter is used by the bubble host map, from the y axis,
         *  used to show the Hosts, with their respective values 
         */
        "format_label_from_xy" : function({series, seriesIndex, dataPointIndex, w}) {
            const serie = w.config.series[seriesIndex]["data"][dataPointIndex];
            
            const x_value = serie["x"];
            const y_value = serie["y"];
            const host_name = serie["meta"]["label"];

            const x_axis_title = w.config.xaxis.title.text;
            const y_axis_title = w.config.yaxis[0].title.text;

            return (`
            <div class='apexcharts-theme-light apexcharts-active' id='test'>
                <div class='apexcharts-tooltip-title' style='font-family: Helvetica, Arial, sans-serif; font-size: 12px;'>
                    ${host_name}
                </div>
                <div class='apexcharts-tooltip-series-group apexcharts-active d-block'>
                    <div class='apexcharts-tooltip-text text-left'>
                        <b>${x_axis_title}</b>: ${x_value}
                    </div>
                    <div class='apexcharts-tooltip-text text-left'>
                        <b>${y_axis_title}</b>: ${y_value}
                    </div>
                </div>
            </div>`)
        },
        "format_label_from_xname" : function({series, seriesIndex, dataPointIndex, w}) {
            const serie = w.config.series[seriesIndex]["data"][dataPointIndex];
            const name = serie["name"];
            const y_value = serie["y"];
            const host_name = serie["meta"]["label"];

            const x_axis_title = w.config.xaxis.title.text;
            const y_axis_title = w.config.yaxis[0].title.text;

            return (`
            <div class='apexcharts-theme-light apexcharts-active' id='test'>
                <div class='apexcharts-tooltip-title' style='font-family: Helvetica, Arial, sans-serif; font-size: 12px;'>
                    ${host_name}
                </div>
                <div class='apexcharts-tooltip-series-group apexcharts-active d-block'>
                    <div class='apexcharts-tooltip-text text-left'>
                        <b>${x_axis_title}</b>: ${name}
                    </div>
                    <div class='apexcharts-tooltip-text text-left'>
                        <b>${y_axis_title}</b>: ${y_value}
                    </div>
                </div>
            </div>`)
        },
    };

    /* Standard Formatter */
    const DEFAULT_FORMATTER = DEFINED_TOOLTIP["format_value"];

    class WidgetUtils {

        static registerWidget(widget) {
            if (widget === null) throw new Error(`The passed widget reference is null!`);
            if (widget.name in DEFINED_WIDGETS) throw new Error(`The widget ${widget.name} is already defined!`);
            DEFINED_WIDGETS[widget.name] = widget;
        }

        static getWidgetByName(widgetName) {
            if (widgetName in DEFINED_WIDGETS) {
                return DEFINED_WIDGETS[widgetName];
            }
            throw new Error(`Widget ${widgetName} not found!`)
        }
    }

    /**
     * Define a simple wrapper class for the widgets.
     */
    class Widget {

        constructor(name, datasource = {}, updateTime = 0, additionalParams = {}) {

            // field containing the data fetched from the datasources provided
            this._fetchedData = [];

            this.name = name;

            // if 0 then don't update the chart automatically, the time
            // is expressed in milliseconds
            this._updateTime = updateTime;

            this._datasource = datasource;
            this._additionalParams = additionalParams;
        }

        /**
         * Init the widget.
         */
        async init() {

            // register the widget to the DEFINED_WIDGETS object
            WidgetUtils.registerWidget(this);
            this._fetchedData = await this._fetchData();

            if (this._updateTime > 0) {
                setInterval(async () => { await this.update(this._datasource.params); }, this._updateTime);
            }
        }

        /**
         * Destroy the widget freeing the resources used.
         */
        async destroy() { }

        /**
         * Force the widget to reload it's data.
         */
        async destroyAndUpdate(datasourceParams = {}) {
            await this.destroy();
            await this.update(datasourceParams);
        }

        async updateByUrl(url) {
          const u = new URL(`${location.origin}${this._datasource.name}`);
          let entries = ntopng_url_manager.get_url_entries(url);
          for (const [key, value] of entries) {
              u.searchParams.set(key, value);
          }
          this._datasource.endpoint = u.pathname + u.search;
          this._fetchedData = await this._fetchData();
        }
      
        async update(datasourceParams = {}) {
    	// build the new endpoint
            const u = new URL(`${location.origin}${this._datasource.name}`);

            for (const [key, value] of Object.entries(datasourceParams)) {
                u.searchParams.set(key, value);
            }

            this._datasource.endpoint = u.pathname + u.search;
            this._fetchedData = await this._fetchData();
        }

        /**
         * For each datasources provided to the constructor,
         * do a GET request to a REST endpoint.
         */
        async _fetchData() {
            const req = await fetch(`${http_prefix}${this._datasource.endpoint}`);
            return await req.json();
        }

    }

    class ChartWidget extends Widget {

        constructor(name, type = 'line', datasource = {}, updateTime = 0, additionalParams = {}) {
            super(name, datasource, updateTime, additionalParams);

            this._chartType = type;
            this._chart = {};
            this._$htmlChart = document.querySelector(`#canvas-widget-${name}`);
        }

        static registerEventCallback(widgetName, eventName, callback) {
            setTimeout(async () => {
                try {
                    const widget = WidgetUtils.getWidgetByName(widgetName);
                    const updatedOptions = {
                        chart: {
                            events: {
                                [eventName]: callback
                            }
                        }
                    };
                    await widget._chart.updateOptions(updatedOptions);
                }
                catch (e) {

                }
            }, 1000);
        }

        _generateConfig() {
            const config = {
                series: [],
                tooltip: {
                    enabledOnSeries: [0],
                    x: {
                        show: true,
                        format: 'dd/MM/yyyy HH:mm:ss',
                    },
                    y: {
                        formatter: function(value, { series, seriesIndex, dataPointIndex, w }) {
                            return value;
                        },
                    },
                    z: {
                        show: false,
                    }
                },
                chart: {
                    type: this._chartType,
                    events: {},
                    height: '100%',
                    toolbar: {
                        show: false,
                    }
                },
                xaxis: {
                    labels: {
                        style: {
                            fontSize: '14px',
                        }
                    },
                    tooltip: {
                        enabled: true,
                        formatter: function(value) {
                            return value;
                        }
                    }
                },
                yaxis: {
                    labels: {
                        style: {
                            fontSize: '14px',
                        }
                    },
                    tooltip: {
                        enabled: true,
                        formatter: function(value) {
                            return value;
                        }
                    }
                },
                zaxis: {
                    labels: {
                        style: {
                            fontSize: '14px',
                        }
                    },
                    tooltip: {
                        enabled: true
                    }
                },
                dataLabels: {
                    enabled: true,
                    style: {
                        fontSize: '14px',
                    }
                },
    	    labels: [],
                legend: {
                    show: true,
                    fontSize: '14px',
                    position: 'bottom',
                    onItemClick: {
                        toggleDataSeries: true,
                    },
                },
                plotOptions: {
                    bar: {
                        borderRadius: 4,
                        horizontal: true,
                    }
                },
                noData: {
                    text: 'No Data',
                    align: 'center',
                    verticalAlign: 'middle',
                    style: {
                        fontSize: '24px'
                    }
                }
            };

            // check if the additionalParams field contains an apex property,
            // then merge the two configurations giving priority to the custom one
            if (this._additionalParams && this._additionalParams.apex) {
                const mergedConfig = Object.assign(config, this._additionalParams.apex);
                return mergedConfig;
            }

            return config;
        }

        _buildTooltip(config, rsp) {
            /* By default the areaChart tooltip[y] is overwritten */
            config["tooltip"]["y"] = {
                formatter: function(value, { series, seriesIndex, dataPointIndex, w }) {
                    return value;
                }
            };

            /* Changing events if given */
            if (rsp['tooltip']) {
                for (const axis in rsp['tooltip']) {
                    if (axis === "x" || axis === "y" || axis === "z") {
                        const formatter = rsp['tooltip'][axis]['formatter'];
                        if(!config['tooltip'][axis])
                            config['tooltip'][axis] = {};

                        config['tooltip'][axis]['formatter'] = DEFINED_TOOLTIP[formatter] || NtopUtils[formatter];
                    }
                }

                /* Customizable tooltip requested */
                if(rsp['tooltip']['custom'])
                    config['tooltip']['custom'] = DEFINED_TOOLTIP[rsp['tooltip']['custom']] || NtopUtils[rsp['tooltip']['custom']];
            }
        }

        _buildAxisFormatter(config, axisName) {

            const axis = config[axisName];
            
            if (axis === undefined || axis.labels === undefined) return;
            
            // enable formatters
            if (axis.labels.ntop_utils_formatter !== undefined && axis.labels.ntop_utils_formatter !== 'none') {
                
                const selectedFormatter = axis.labels.ntop_utils_formatter;

                if (NtopUtils[selectedFormatter] === undefined) {
                    console.error(`xaxis: Formatting function '${selectedFormatter}' didn't found inside NtopUtils.`);
                }
                else {
                    axis.labels.formatter = NtopUtils[selectedFormatter];
                }
            }        // enable formatters
        }

        _buildDataLabels(config, rsp) {
            if (rsp["dataLabels"]) {
                for (const [dataLabelsOpts, data] of Object.entries(rsp["dataLabels"])) {
                    config["dataLabels"][dataLabelsOpts] = data;
                }
            }   

            let formatter = config["dataLabels"]["formatter"];
            
            if(formatter && DEFINED_TOOLTIP[formatter]) {
                config["dataLabels"]["formatter"] = DEFINED_TOOLTIP[formatter];
            }
        }

        _buildConfig() {

            const config = this._generateConfig();
            const rsp = this._fetchedData.rsp;
            
            // add additional params fetched from the datasource
            const additionals = ['series', 'xaxis', 'yaxis', 'colors', 'labels', 'fill', 'filter', 'filtering_labels'];
            
            for (const additional of additionals) {

                if (rsp[additional] === undefined) continue;

                if (config[additional] !== undefined) {
                    config[additional] = Object.assign(config[additional], rsp[additional]);
                }
                else {
                    config[additional] = rsp[additional];
                }
            }
            
            /* Changing events if given */
            if (rsp['events']) {
                /* Just pass a table of events. e.g. { events = { click = "db_analyze", updated = "standard" } }*/
                for (const event in rsp['events']) {
                    config['chart']['events'][event] = DEFINED_EVENTS[rsp['events'][event]];
                }
            }

            if (rsp['horizontal_chart'] !== undefined) {
                config['plotOptions']['bar']['horizontal'] = rsp['horizontal_chart'];
            }

            this._buildTooltip(config, rsp);
            this._buildAxisFormatter(config, 'xaxis');
            this._buildAxisFormatter(config, 'yaxis');
            this._buildDataLabels(config, rsp);

            return config;
        }

        _initializeChart() {
            const config = this._buildConfig();
            this._chartConfig = config;
            this._chart = new ApexCharts(this._$htmlChart, this._chartConfig);
            this._chart.render();
        }

        async init() {
            await super.init();
            this._initializeChart();
        }

        async destroy() {
            await super.destroy();
            this._chart.destroy();
            this._chart = null;
        }

        async update(datasourceParams = {}) {
            if(this._chartConfig !== undefined) {	    
              if (datasourceParams) {
                await super.update(datasourceParams);
              } else {
                await super.updateByUrl();
              }
              
              if (this._chart != null) {
                    // expecting that rsp contains an object called series
                    const { colors, series, dataLabels, labels, xaxis, filtering_labels } = this._fetchedData.rsp;
                    // update the colors list
                    this._chartConfig.colors = colors;
                    this._chartConfig.series = series;
                    
                    if(xaxis && xaxis.categories)
                        this._chartConfig.xaxis.categories = xaxis.categories;
                    
                    if(filtering_labels)
                        this._chartConfig.filtering_labels = filtering_labels;

                    if(dataLabels) {
                        let formatter = this._chartConfig.dataLabels.formatter;
                        if(formatter && DEFINED_TOOLTIP[formatter])
                            this._chartConfig.dataLabels.formatter = DEFINED_TOOLTIP[formatter];
                        else
                            this._chartConfig.dataLabels.formatter = DEFAULT_FORMATTER;
                    }
                        
                    if(labels) 
                        this._chartConfig.labels = labels;

                    this._chart.updateOptions(this._chartConfig, true);
                }
            }
        }

        async destroyAndUpdate(datasource = {}) {
            await super.destroyAndUpdate(datasource);
            this._initializeChart();
        }

    }

    const fixSubMenuPosition = ($submenu, $hoverButton) => {

        const MIN_SPACE = 20;
        const MIN_HEIGHT = 150;

        let distFromAbove = $hoverButton.position().top;
        const submenuHeight = $submenu.height();
        const documentHeight = $(window).height();

        // if the submenu is too high to be shown then set
        // the overflow on y axis
        if (submenuHeight + distFromAbove >= documentHeight) {

            const currentSubmenuHeight = documentHeight - distFromAbove;
            if (currentSubmenuHeight <= MIN_HEIGHT) {
                distFromAbove = distFromAbove - submenuHeight + $hoverButton.outerHeight();
            }
            else {
                $submenu.css({'max-height': currentSubmenuHeight - MIN_SPACE, 'overflow-y': 'auto'});
            }

        }

        // set the submenu height
        $submenu.css('top', `${distFromAbove}px`);

    };

    $(window).on('scroll', function(){

        const UPPER_LIMIT = 32;
        const windowScrollTop = $(this).scrollTop();

        if (windowScrollTop >= UPPER_LIMIT) {
            $(`#n-navbar`).addClass("scrolled bg-light");
        }
        else {
            $(`#n-navbar`).removeClass("scrolled bg-light");
        }

    });

    $(() => {

        const toggleSidebar = () => {
            // if the layer doesn't exists then create it
            if ($(`.sidebar-close-layer`).length == 0) {

                const $layer = $(`<div class='sidebar-close-layer' style='display:none'></div>`);
                // when the user clicks on the layer
                $layer.on('click', function(){
                    // remove active class from sidebar
                    $(`#n-sidebar`).removeClass('active');
                    // hide the layer and remove it from the DOM
                    $layer.fadeOut(function() {
                        $(this).remove();
                    });
                });

                // append the layer to the wrapper
                $(`#wrapper`).append($layer);
                // show the layer inside the page
                $layer.fadeIn();
            }
            else {
                // hide the existing layer and destroy it
                $(`.sidebar-close-layer`).fadeOut(function() {
                    $(this).remove();
                });
            }

            // show/hide the sidebar
            $(`#n-sidebar`).toggleClass('active');
        };

        $('#n-sidebar a.submenu').bind({
            mouseenter: function() {
                let submenu = $(this).parent().find(`div[id$='submenu']`);
                fixSubMenuPosition(submenu, $(this));
                submenu.show();
            },
            mouseleave: function() {
                let submenu = $(this).parent().find(`div[id$='submenu']`);
                submenu.hide();
            }
        });

        $(`div[id$='submenu']`).bind({
            mouseenter: function() {
                $(this).show();
            },
            mouseleave: function() {
                $(this).hide();
            }
        });

        /* toggle sidebar display */
        $(`button[data-bs-toggle='sidebar']`).on('click', function() {
            toggleSidebar();
        });
    });

    $(window).on('resize', function() {

        // re-calc submenu height
        const $currentSubmenu = $('#n-sidebar').find(`div.show[id$='submenu']`);

        if ($currentSubmenu.length > 0) {

            const $hoverButton = $currentSubmenu.parent().find(`a[data-bs-toggle='collapse']`);
            fixSubMenuPosition($currentSubmenu, $hoverButton);
        }

    });

    /**
        (C) 2022 - ntop.org
    */

    const ntopChartApex$1 = function () {
        // define default chartOptions for all chart type.
        const _default_BASE_ChartOptions = {
            series: [],
            chart: {
                height: "100%",
                width: "100%",
                toolbar: {
                    show: false,
                    tools: {
                        zoomout: false,
                        download: false,
                        zoomin: false,
                        zoom: " ",
                        selection: false,
                        pan: false,
                        reset: false
                    }
                },
                events: {}
            },
            xaxis: {
                tooltip: {
                    enabled: false,
                },
            },
            yaxis: {
                labels: {
                    show: true,
                    style: {
                        colors: [],
                        fontSize: "11px",
                        fontWeight: 400,
                        cssClass: ""
                    }
                },
                title: {
                    rotate: -90,
                    offsetY: 0,
                    offsetX: 0,
                    style: {
                        fontSize: "11px",
                        fontWeight: 900,
                        cssClass: ""
                    }
                },
                tooltip: {
                    enabled: false,
                },
            },
            grid: {
                show: false,
            },
            legend: {
                show: true
            },
        };

        // define default xaxis formatter for chart with datetime on xaxis.
        const _setXTimeFormatter = function (chartOptions) {
            chartOptions.xaxis.labels.formatter = function (value, { series, seriesIndex, dataPointIndex, w }) {
                return ntopng_utility$1.from_utc_to_server_date_format(value);
            };
        };

        // define default chartOptions for area chart type.
        const _default_TS_COLUMN_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_COLUMN_ChartOptions = {
                chart: {
                    stacked: true,
                    type: "bar",
                    zoom: {
                        enabled: false,
                        type: "x",
                    },
                },
                tooltip: {
                    x: {
                        format: "dd MMM yyyy HH:mm"
                    },
                    y: {}
                },
                xaxis: {
                    labels: {
                        show: true,
                        datetimeUTC: false,
                    },
                    axisTicks: {
                        show: false,
                    },
                    type: "datetime",
                    axisBorder: {
                        show: true,
                    },
                    convertedCatToNumeric: false
                },
                dataLabels: {
                    enabled: false
                },
                stroke: {
                    show: false,
                    curve: "smooth"
                },
                fill: {
                    type: "solid"
                },
                plotOptions: {
                    bar: {
                        startingShape: "rounded",
                        endingShape: "rounded",
                        borderRadius: 8,
                    }
                }
            };

            ntopng_utility$1.copy_object_keys(TS_COLUMN_ChartOptions, chartOptions, true);
            return chartOptions;
        }();

        // define default chartOptions for area chart type.
        const _default_HEATMAP_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let HEATMAP_ChartOptions = {
                chart: {
                    type: "heatmap",
                    zoom: {
                        enabled: false,
                        type: "x",
                    },
                },
                tooltip: {
                    x: {
                        show: true,
                        format: "dd MMM yyyy HH:mm"
                    },
                    y: {
                        formatter: (serieName) => {
                            if (serieName && serieName == 1) {
                                return "Flows Available";
                            }
                            return "No Flows"
                        }
                    }
                },
                xaxis: {
                    labels: {
                        show: false,
                        datetimeUTC: false,
                    },
                    axisTicks: {
                        show: false,
                    },
                    type: "datetime",
                    axisBorder: {
                        show: true,
                    },
                    convertedCatToNumeric: false
                },
                dataLabels: {
                    enabled: false
                },
                stroke: {
                    show: false,
                    curve: "smooth"
                },
                colors: ["#008FFB"],
                fill: {
                    type: "solid"
                }
            };

            ntopng_utility$1.copy_object_keys(HEATMAP_ChartOptions, chartOptions, true);
            return chartOptions;
        }();

        // define default chartOptions for area chart type.
        const _default_TS_STACKED_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_STACKED_ChartOptions = {
                chart: {
                    stacked: true,
                    type: "area",
                    zoom: {
                        enabled: false,
                        type: "x",
                    },
                    clipMarkers: false
                },
                tooltip: {
                    // shared: true,
                    x: {
                        format: "dd MMM yyyy HH:mm:ss"
                    },
                    y: {}
                },
                xaxis: {
                    labels: {
                        show: true,
                        datetimeUTC: false,
                        formatter: null,
                    },
                    axisTicks: {
                        show: false,
                    },
                    type: "datetime",
                    axisBorder: {
                        show: true,
                    },
                    convertedCatToNumeric: false
                },
                dataLabels: {
                    enabled: false
                },
                stroke: {
                    show: false,
                    curve: "smooth"
                },
                fill: {
                    type: "solid"
                },
                plotOptions: {
                    area: {
                        stacked: true
                    }
                }
            };
            ntopng_utility$1.copy_object_keys(TS_STACKED_ChartOptions, chartOptions, true);
            return chartOptions;
        }();


        // define default chartOptions for area chart type.
        const _default_TS_POLAR_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_STACKED_ChartOptions = {
                chart: {
                    stacked: true,
                    type: "polarArea",
                    height: 400
                },
                yaxis: {
                    show: true,
                    labels: {
                        formatter: NtopUtils$1.bytesToSize
                    }
                },
                dataLabels: {
                    enabled: true,
                    formatter: function (val, opts) {
                        return (val ? `${val.toFixed(1)}%` : `0%`)
                    },
                },
                legend: {
                    enabled: true,
                    position: 'bottom',
                },
                stroke: {
                    show: false,
                    curve: "smooth"
                },
                fill: {
                    type: "solid"
                },
                tooltip: {
                    y: {
                        formatter: NtopUtils$1.bytesToSize
                    },
                },
            };
            ntopng_utility$1.copy_object_keys(TS_STACKED_ChartOptions, chartOptions, true);
            return chartOptions;
        }();

        // define default chartOptions for area chart type.
        const _default_TS_DONUT_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_STACKED_ChartOptions = {
                chart: {
                    stacked: true,
                    type: "donut",
                    height: 300
                },
                yaxis: {
                    show: true,
                    labels: {
                        formatter: NtopUtils$1.bytesToSize
                    }
                },
                dataLabels: {
                    enabled: true,
                    formatter: function (val, opts) {
                        return (val ? `${val.toFixed(1)}%` : `0%`)
                    },
                },
                legend: {
                    enabled: true,
                    position: 'bottom',
                },
                stroke: {
                    show: false,
                    curve: "smooth"
                },
                fill: {
                    type: "solid"
                },
                tooltip: {
                    y: {
                        formatter: formatterUtils.getFormatter("number"),
                    },
                },
                noData: {
                    text: 'No Data',
                    style: {
                        color: undefined,
                        fontSize: '24px',
                        fontFamily: undefined
                    }
                }
            };
            ntopng_utility$1.copy_object_keys(TS_STACKED_ChartOptions, chartOptions, true);
            return chartOptions;
        }();

        // define default chartOptions for area chart type.
        const _default_TS_RADIALBAR_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_STACKED_ChartOptions = {
                chart: {
                    stacked: true,
                    type: "radialBar",
                    height: 300
                },
                yaxis: {
                    show: true,
                },
                dataLabels: {
                    enabled: true,
                    formatter: function (val, opts) {
                        return val
                    },
                },
                stroke: {
                    show: false,
                    curve: "smooth"
                },
                fill: {
                    type: "solid"
                },
                tooltip: {
                    y: {
                        formatter: NtopUtils$1.bytesToSize
                    },
                },
                noData: {
                    text: 'No Data',
                    style: {
                        color: undefined,
                        fontSize: '24px',
                        fontFamily: undefined
                    }
                },
                plotOptions: {
                    radialBar: {
                        offsetY: 0,
                        startAngle: 0,
                        endAngle: 270,
                        hollow: {
                            margin: 5,
                            size: '30%',
                            background: 'transparent',
                            image: undefined,
                        },
                        dataLabels: {
                            name: {
                                show: false,
                            },
                            value: {
                                show: false,
                            }
                        }
                    }
                },
                legend: {
                    show: true,
                    floating: true,
                    fontSize: '16px',
                    position: 'left',
                    offsetX: 160,
                    offsetY: 15,
                    labels: {
                        useSeriesColors: true,
                    },
                    markers: {
                        size: 0
                    },
                    formatter: function (seriesName, opts) {
                        return seriesName + ":  " + opts.w.globals.series[opts.seriesIndex]
                    },
                    itemMargin: {
                        vertical: 3
                    }
                },
                responsive: [{
                    breakpoint: 480,
                    options: {
                        legend: {
                            show: false
                        }
                    }
                }]
            };
            ntopng_utility$1.copy_object_keys(TS_STACKED_ChartOptions, chartOptions, true);
            return chartOptions;
        }();

        // define default chartOptions for area chart type.
        const _default_TS_PIE_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_STACKED_ChartOptions = {
                chart: {
                    stacked: true,
                    type: "pie",
                    height: 400
                },
                yaxis: {
                    show: true,
                    labels: {
                        formatter: NtopUtils$1.bytesToSize
                    }
                },
                dataLabels: {
                    enabled: true,
                    formatter: function (val, opts) {
                        return (val ? `${val.toFixed(1)}%` : `0%`)
                    },
                },
                legend: {
                    enabled: true,
                    position: 'bottom',
                },
                stroke: {
                    show: false,
                    curve: "smooth"
                },
                fill: {
                    type: "solid"
                },
                tooltip: {
                    y: {
                        formatter: NtopUtils$1.bytesToSize
                    },
                },
            };
            ntopng_utility$1.copy_object_keys(TS_STACKED_ChartOptions, chartOptions, true);
            return chartOptions;
        }();


        // define default chartOptions for area chart type.
        const _default_TS_BAR_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_BAR_ChartOptions = {
                chart: {
                    type: "bar",
                    height: 300
                },
                stroke: {
                    show: true,
                    width: 2,
                    colors: ['transparent']
                },
                dataLabels: {
                    enabled: false
                },
                yaxis: {
                    labels: {
                        show: true, // Mostra i valori sull'asse Y
                    },
                },
                grid: {
                    show: true, // Abilita la griglia
                    yaxis: {
                        lines: {
                            show: true, // Mostra le linee della griglia su Y
                        }
                    }
                },
                plotOptions: {
                    bar: {
                        horizontal: true,
                        columnWidth: '55%',
                        borderRadius: 5,
                        borderRadiusApplication: 'end'
                    },
                },
                fill: {
                    opacity: 1
                },
                tooltip: {
                    y: {
                        title: {
                            formatter: () => ''
                        },
                        formatter: NtopUtils$1.formatValue
                    },
                },
            };
            ntopng_utility$1.copy_object_keys(TS_BAR_ChartOptions, chartOptions, true);
            return chartOptions;
        }();

        // define default chartOptions for line chart type.
        const _default_TS_LINE_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_LINE_ChartOptions = {
                chart: {
                    type: "line",
                    zoom: {
                        enabled: false,
                        type: "x",
                    },
                },
                tooltip: {
                    shared: true,
                    x: {
                        format: "dd MMM yyyy HH:mm:ss"
                    },
                    y: {}
                },
                xaxis: {
                    labels: {
                        show: false,
                        datetimeUTC: false,
                        formatter: null,
                    },
                    axisTicks: {
                        show: true
                    },
                    type: "datetime",
                    axisBorder: {
                        show: true
                    },
                    convertedCatToNumeric: false
                },
                stroke: {
                    show: true,
                    width: 2,
                    curve: "smooth"
                },
                grid: {
                    show: true,
                },
                dataLabels: {
                    enabled: false
                },
            };
            ntopng_utility$1.copy_object_keys(TS_LINE_ChartOptions, chartOptions, true);
            return chartOptions;
        }();

        const format_label_from_xname = function ({ series, seriesIndex, dataPointIndex, w }) {
            const serie = w.config.series[seriesIndex]["data"][dataPointIndex];
            const name = serie["name"];
            const y_value = serie["y"];
            const host_name = serie["meta"]["label"];

            const x_axis_title = w.config.xaxis.title.text;
            const y_axis_title = w.config.yaxis[0].title.text;

            return (`
          <div class='apexcharts-theme-light apexcharts-active' id='test'>
              <div class='apexcharts-tooltip-title' style='font-family: Helvetica, Arial, sans-serif; font-size: 12px;'>
                  ${host_name}
              </div>
              <div class='apexcharts-tooltip-series-group apexcharts-active d-block'>
                  <div class='apexcharts-tooltip-text text-left'>
                      <b>${x_axis_title}</b>: ${name}
                  </div>
                  <div class='apexcharts-tooltip-text text-left'>
                      <b>${y_axis_title}</b>: ${y_value}
                  </div>
              </div>
          </div>`)
        };

        // define default chartOptions for line chart type.
        const _default_TS_BUBBLE_ChartOptions = function () {
            let chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
            let TS_BUBBLE_ChartOptions = {
                chart: {
                    width: '100%',
                    height: '100%',
                    stacked: true,
                    type: "bubble",
                    zoom: {
                        autoScaleYaxis: true
                    },
                },
                legend: {
                    enabled: true,
                    position: 'bottom',
                },
                stroke: {
                    show: false,
                    curve: "smooth"
                },
                fill: {
                    type: "solid"
                },
                events: {
                    dataPointSelection: "standard",
                },
                grid: {
                    padding: {
                        left: 6
                    },
                },
                xaxis: {
                    type: 'numeric',
                    labels: {}
                },
                yaxis: {
                    type: 'numeric',
                    forceNiceScale: true,
                    labels: {}
                },
                dataLabels: {
                    enabled: false
                },
                tooltip: {
                    custom: format_label_from_xname,
                }
            };
            ntopng_utility$1.copy_object_keys(TS_BUBBLE_ChartOptions, chartOptions, true);
            return chartOptions;
        }();

        return {
            typeChart: {
                TS_LINE: "TS_LINE",
                TS_STACKED: "TS_STACKED",
                TS_COLUMN: "TS_COLUMN",
                PIE: "PIE",
                DONUT: "DONUT",
                RADIALBAR: "RADIALBAR",
                HEATMAP: "HEATMAP",
                RADAR: "RADAR",
                BUBBLE: "BUBBLE",
                BASE: "BASE",
                BAR: "BAR"
            },
            newChart: function (type) {
                let _chartOptions = {};
                let _chart;

                if (type == this.typeChart.TS_STACKED) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_STACKED_ChartOptions);
                    _setXTimeFormatter(_chartOptions);
                } else if (type == this.typeChart.TS_LINE) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_LINE_ChartOptions);
                    _setXTimeFormatter(_chartOptions);
                } else if (type == this.typeChart.TS_COLUMN) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_COLUMN_ChartOptions);
                    _setXTimeFormatter(_chartOptions);
                } else if (type == this.typeChart.HEATMAP) {
                    _chartOptions = ntopng_utility$1.clone(_default_HEATMAP_ChartOptions);
                    _setXTimeFormatter(_chartOptions);
                } else if (type == this.typeChart.PIE) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_PIE_ChartOptions);
                } else if (type == this.typeChart.DONUT) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_DONUT_ChartOptions);
                } else if (type == this.typeChart.RADIALBAR) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_RADIALBAR_ChartOptions);
                } else if (type == this.typeChart.BAR) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_BAR_ChartOptions);
                } else if (type == this.typeChart.POLAR) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_POLAR_ChartOptions);
                } else if (type == this.typeChart.BUBBLE) {
                    _chartOptions = ntopng_utility$1.clone(_default_TS_BUBBLE_ChartOptions);
                } else if (type == this.typeChart.BASE) {
                    _chartOptions = ntopng_utility$1.clone(_default_BASE_ChartOptions);
                } else {
                    throw `ntopChartApex::newChart: chart type = ${type} unsupported`;
                }
                const setYaxisFormatter = (chartOptions) => {
                    if (typeof (chartOptions?.yaxis?.labels?.formatter) == "string") {
                        const formatter = chartOptions.yaxis.labels.formatter;
                        let chartFormatter = formatterUtils.getFormatter(formatter);
                        if (chartFormatter != null) {
                            chartOptions.yaxis.labels.formatter = chartFormatter;
                        } else {
                            if (formatter == "formatValue") {
                                chartOptions.yaxis.labels.formatter = formatterUtils.getFormatter("number");
                            }
                            else if (formatter == "bytesToSize") {
                                chartOptions.yaxis.labels.formatter = formatterUtils.getFormatter("bytes");
                            }
                        }
                    }
                };
                return {
                    drawChart: function (htmlElement, chartOptions) {
                        // add/replace chartOptions fields in _chartOptions
                        setYaxisFormatter(chartOptions);
                        ntopng_utility$1.copy_object_keys(chartOptions, _chartOptions, true);
                        _chart = new ApexCharts(htmlElement, _chartOptions);
                        _chart.render();
                    },
                    to_data_uri: async function (options) {
                        if (_chart == null) { return; }
                        let res = await _chart.dataURI(options);
                        return res.imgURI;
                    },
                    destroyChart: function () {
                        if (_chart == null) { return; }
                        _chart.destroy();
                    },
                    updateChart: function (chartOptions) {
                        if (_chart == null) { return; }
                        setYaxisFormatter(chartOptions);
                        _chart.updateOptions(chartOptions, false, false, false);
                    },
                    updateSeries: function (series) {
                        if (_chart == null) { return; }
                        _chart.updateSeries(series);
                    },
                    registerEvent: function (eventName, callback, updateChart = false) {
                        _chartOptions.chart.events[eventName] = callback;
                        if (updateChart == true) {
                            _chart.updateOptions(_chartOptions);
                        }
                    },
                };
            },
        };
    }();

    /*
      (C) 2013-23 - ntop.org
     */

    /* This function check if value is not set (null or empty).
     * Do not check for 0 as it may be a valid value. */
    const QoEQualityBadge = (value) => {
        let badge = '';
        if (value > 90 && value <= 100) {
            badge = 'bg-success';
        } else if (value > 75) {
            badge = 'bg-primary';
        } else if (value > 60) {
            badge = 'bg-info';
        } else if (value > 50) {
            badge = 'bg-warning';
        } else if (value > 0) {
            badge = 'bg-danger';
        }

        return badge;
    };

    /* ******************************************************************** */

    /* *** NOTE: in order to be able to used these icons, please include in the HTML/vue page
        <script src="https://unpkg.com/lucide@latest"></script> 
    */
    /* This function check if value is not set (null or empty).
     * Do not check for 0 as it may be a valid value. */
    const QoEQualityIcon = (value) => {
        let icon = '';
        if (value > 90 && value <= 100) {
            icon = '<svg data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + QoEQualityLabel(value) + '" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-signal mb-1 text-success" style="margin-top:-4px;"><path d="M2 20h.01"/><path d="M7 20v-4"/><path d="M12 20v-8"/><path d="M17 20V8"/><path d="M22 4v16"/></svg>';
        } else if (value > 75) {
            icon = '<svg data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + QoEQualityLabel(value) + '" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-signal-high mb-1 text-success" style="margin-top:-4px;"><path d="M2 20h.01"/><path d="M7 20v-4"/><path d="M12 20v-8"/><path d="M17 20V8"/></svg>';
        } else if (value > 60) {
            icon = '<svg data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + QoEQualityLabel(value) + '" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-signal-high mb-1 text-success" style="margin-top:-4px;"><path d="M2 20h.01"/><path d="M7 20v-4"/><path d="M12 20v-8"/><path d="M17 20V8"/></svg>';
        } else if (value > 50) {
            icon = '<svg data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + QoEQualityLabel(value) + '" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-signal-medium mb-1 text-warning" style="margin-top:-4px;"><path d="M2 20h.01"/><path d="M7 20v-4"/><path d="M12 20v-8"/></svg>';
        } else if (value > 0) {
            icon = '<svg data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + QoEQualityLabel(value) + '" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-signal-low mb-1 text-danger" style="margin-top:-4px;"><path d="M2 20h.01"/><path d="M7 20v-4"/></svg>';
        }

        return icon;
    };

    /* ******************************************************************** */

    /* This function check if value is not set (null or empty).
     * Do not check for 0 as it may be a valid value. */
    const QoEQualityLabel = (value) => {
        let label = '';
        if (value > 90 && value <= 100) {
            label = i18n('flow_details.qoe_excellent_label');
        } else if (value > 75) {
            label = i18n('flow_details.qoe_good_label');
        } else if (value > 60) {
            label = i18n('flow_details.qoe_fair_label');
        } else if (value > 50) {
            label = i18n('flow_details.qoe_degraded_label');
        } else if (value > 0) {
            label = i18n('flow_details.qoe_poor_label');
        }

        return label;
    };

    /* ******************************************************************** */

    /* This function check if value is not set (null or empty).
     * Do not check for 0 as it may be a valid value. */
    const GetQoEID = (value) => {
        let id = '';
        if (value > 90 && value <=100) {
            id = 91;
        } else if (value > 75) {
            id = 76;
        } else if (value > 60) {
            id = 61;
        } else if (value > 50) {
            id = 51;
        } else if (value > 0) {
            id = 1;
        }

        return id;
    };

    /* ******************************************************************** */

    const dataUtils$1 = function () {
        return {
            QoEQualityBadge,
            QoEQualityLabel,
            QoEQualityIcon,
            GetQoEID,
        };
    }();

    /**
     * (C) 2020-21 - ntop.org
     * This file contains utilities used by the *new* datatables.
     */


    const DataTableHandlers = function () {
        let handlersIdDict = {};
        return {
            addHandler: function (h) {
                let handlers = handlersIdDict[h.handlerId];
                if (handlers == null) {
                    handlers = [];
                    handlersIdDict[h.handlerId] = handlers;
                }
                handlers.push(() => {
                    h.onClick();
                });
                return `window['_DataTableButtonsOnClick']('${h.handlerId}', '${handlers.length - 1}')`;
            },
            getHandler: function (handlerId, rowId) {
                let handlers = handlersIdDict[handlerId];
                if (handlers == null) { return null; }
                return handlers[rowId];
            },
            deleteHandlersById: function (handlerId) {
                handlersIdDict[handlerId] = null;
            },
        }
    }();

    window["_DataTableButtonsOnClick"] = function (handlerId, rowId) {
        let onClick = DataTableHandlers.getHandler(handlerId, rowId);
        if (onClick != null) {
            onClick();
        }
    };

    class DataTableFiltersMenu$1 {

        /**
         *
         * @param {options}
         */
        constructor({ tableAPI, filterMenuKey, filterTitle, filters, columnIndex, icon = null, extraAttributes = "", id = null, url = null, urlParams = null, removeAllEntry = false, callbackFunction = null }) {
            this.rawFilters = filters;
            this.tableAPI = tableAPI;
            this.filterTitle = filterTitle;
            this.icon = icon;
            this.filterMenuKey = filterMenuKey;
            this.columnIndex = columnIndex;
            this.preventUpdate = false;
            this.currentFilterSelected = undefined;
            this.$datatableWrapper = $(tableAPI.context[0].nTableWrapper);
            this.extraAttributes = extraAttributes;
            this.id = id;
            this.url = url;
            this.removeAllEntry = removeAllEntry;
            this.callbackFunction = callbackFunction;
        }

        get selectedFilter() {
            return this.currentFilterSelected;
        }

        init() {

            const self = this;

            // when the datatable has been initialized render the dropdown
            this.$datatableWrapper.on('init.dt', function () {
                self._render(self.rawFilters);
            });

            // on ajax reload then update the datatable entries
            this.tableAPI.on('draw', function () {
                self._update();
            });

            return self;
        }

        _countEntries(regex, data = []) {

            if (regex === undefined) {
                console.error("DataTableFiltersMenu::_countEntries() => the passed regex is undefined!");
            }

            const reg = new RegExp(regex);
            return data.filter(cellValue => reg.test(cellValue)).length;
        }

        _createMenuEntry(filter) {

            const self = this;
            let $entry = $(`<li class='dropdown-item pointer'>${filter.label} </li>`);

            if (self.url) {
                $entry = $(`<li class='dropdown-item pointer'><a href=# class='p-1 standard-color'>${filter.label} </li>`);

                if (filter.currently_active == true) {
                    // set active filter title and key
                    if (self.$dropdown.title.parent().find(`i.fas`).length == 0) {
                        self.$dropdown.title.parent().prepend(`<i class='fas fa-filter'></i>`);
                    }

                    const newContent = $entry.html();
                    self.$dropdown.title.html(newContent);
                    // remove the active class from the li elements
                    self.$dropdown.container.find('li').removeClass(`active`);
                    // add active class to current entry
                    if (filter.key !== 'all') {
                        $entry.addClass(`active`);
                    }
                }
            } else if (filter.regex !== undefined && (filter.countable === undefined || filter.countable)) {
                const data = this.tableAPI.columns(this.columnIndex).data()[0];
                const count = this._countEntries(filter.regex, data);
                const $counter = $(`<span class='counter'>(${count})</span>`);

                // if the count is 0 then hide the menu entry
                if (count == 0) $entry.hide();

                //append the $counter object inside the $entry
                $entry.append($counter);
            }

            $entry.on('click', function (e) {
                // set active filter title and key
                if (self.$dropdown.title.parent().find(`i.fas`).length == 0) {
                    self.$dropdown.title.parent().prepend(`<i class='fas fa-filter'></i>`);
                }

                const newContent = $entry.html();
                self.$dropdown.title.html(newContent);
                // remove the active class from the li elements
                self.$dropdown.container.find('li').removeClass(`active`);
                // add active class to current entry
                if (filter.key !== 'all') {
                    $entry.addClass(`active`);
                }

                if (self.callbackFunction) {
                    self.callbackFunction(self.tableAPI, filter);
                    if (filter.callback) filter.callback();
                    return;
                }

                if (!self.url) {
                    self.preventUpdate = true;

                    // if the filter have a callback then call it
                    if (filter.callback) filter.callback();
                    // perform the table filtering
                    self.tableAPI.column(self.columnIndex).search(filter.regex, true, false).draw();
                    // set current filter
                    self.currentFilterSelected = filter;
                } else {
                    self.urlParams = window.location.search;
                    const newUrlParams = new URLSearchParams(self.urlParams);
                    newUrlParams.set(self.filterMenuKey, (typeof (filter.id) != "undefined") ? filter.id : '');

                    window.history.pushState('', '', window.location.pathname + '?' + newUrlParams.toString());
                    location.reload();
                }
            });

            return $entry;
        }

        _createFilters(filters) {

            const filtersCreated = {};

            // for each filter defined in this.filters
            for (const filter of filters) {

                const $filter = this._createMenuEntry(filter);
                // save the filter inside the $filters object
                filtersCreated[filter.key] = { filter: filter, $node: $filter };
            }

            return filtersCreated;
        }

        _render(filters) {
            if (typeof this.columnIndex == 'undefined') {
                $(`<span id="${this.id}" ${this.extraAttributes} title="${this.filterTitle}">${this.icon || this.filterTitle}</span>`).insertBefore(this.$datatableWrapper.find('.dataTables_filter').parent());
            } else {
                const $dropdownContainer = $(`<div id='${this.filterMenuKey}_dropdown' class='dropdown d-inline'></div>`);
                const $dropdownButton = $(`<button class='btn-link btn dropdown-toggle' data-bs-toggle="dropdown" type='button'></button>`);
                const $dropdownTitle = $(`<span class='filter-title'>${this.filterTitle}</span>`);
                $dropdownButton.append($dropdownTitle);

                this.$dropdown = {
                    container: $dropdownContainer,
                    title: $dropdownTitle,
                    button: $dropdownButton
                };

                this.filters = this._createFilters(filters);

                const $menuContainer = $(`<ul class='dropdown-menu dropdown-menu-lg-end scrollable-dropdown' id='${this.filterMenuKey}_dropdown_menu'></ul>`);
                for (const [_, filter] of Object.entries(this.filters)) {
                    $menuContainer.append(filter.$node);
                }

                // the All entry is created by the object
                if (!this.removeAllEntry) {
                    const allFilter = this._generateAllFilter();
                    $menuContainer.prepend(this._createMenuEntry(allFilter));
                }

                // append the created dropdown inside
                $dropdownContainer.append($dropdownButton);
                $dropdownContainer.append($menuContainer);
                // append the dropdown menu inside the filter wrapper
                $dropdownContainer.insertBefore(this.$datatableWrapper.find('.dataTables_filter').parent());

                this._selectFilterFromState(this.filterMenuKey);
            }
        }

        _selectFilterFromState(filterKey) {

            if (!this.tableAPI.state) return;
            if (!this.tableAPI.state.loaded()) return;
            if (!this.tableAPI.state.loaded().filters) return;

            // save the current table state
            tableAPI.state.save();
        }

        _generateAllFilter() {
            return {
                key: 'all',
                label: i18n_ext.all,
                regex: '',
                countable: false,
                callback: () => {
                    this.$dropdown.title.parent().find('i.fas.fa-filter').remove();
                    this.$dropdown.title.html(`${this.filterTitle}`);
                }
            };
        }

        _update() {

            // if the filters have not been initialized by _render then return
            if (this.filters === undefined) return;
            if (this.preventUpdate) {
                this.preventUpdate = false;
                return;
            }

            for (const [_, filter] of Object.entries(this.filters)) {
                if (filter.countable == false || filter.filter.countable == false) continue;

                const data = this.tableAPI.columns(this.columnIndex).data()[0];
                const count = this._countEntries(filter.filter.regex, data);

                // hide the filter if the count is zero
                (count == 0) ? filter.$node.hide() : filter.$node.show();
                // update the counter label
                filter.$node.find('.counter').text(`(${count})`);
                // update the selected button counter
                this.$dropdown.button.find('.counter').text(`(${count})`);
            }
        }

    }

    class DataTableUtils$1 {

        /**
         * Return a standard config for the Sprymedia (c) DataTables
         */
        static getStdDatatableConfig(dtButtons = [], dom = "<'row'<'col-sm-2 d-inline-block'l><'col-sm-10 text-end d-inline-block'<'dt-search'f>B>rtip>") {

            // hide the buttons section if there aren't buttons inside the array
            if (dtButtons.length == 0) {
                dom = "fBrtip";
            }

            return {
                dom: dom,
                pagingType: 'full_numbers',
                lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
                language: {
                    search: i18n.script_search,
                    paginate: {
                        previous: '&lt;',
                        next: '&gt;',
                        first: '«',
                        last: '»'
                    }
                },
                saveState: true,
                responsive: true,
                buttons: {
                    buttons: dtButtons,
                    dom: {
                        button: {
                            className: 'btn btn-link'
                        },
                        container: {
                            className: 'd-inline-block'
                        }
                    }
                }
            }
        }

        static createLinkCallback(action) {
            let handler = "";
            let fOnClick = DataTableHandlers.addHandler(action.handler);
            handler = `onclick="${fOnClick}"`;
            return `<a href=#
                   ${handler}>
                   ${action.text || ''}
                </a>`;
        }

        /**
         * Example of action:
         * {
         *  class: string,
         *  data: object,
         *  icon: string,
         *  modal: string,
         *  href: string,
         *  hidden: bool,
         * }
         * @param {*} actions
         */
        static createActionButtons(actions = []) {

            const buttons = [];
            const dropdownButton = '<button type="button" class="btn btn-sm btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false"><i class="fas fa-align-justify"></i></button>';

            actions.forEach((action, i) => {
                let handler = "";
                if (action.handler) {
                    let fOnClick = DataTableHandlers.addHandler(action.handler);
                    handler = `onclick="${fOnClick}"`;
                }
                let button = (`
            <li>
                <a
                    ${(action.href || action.modal) ? `href='${action.href || action.modal}'` : ``}
                    ${handler}
                    ${(action.onclick) ? `onclick='${action.onclick}'` : ``}
                    ${action.modal ? "data-bs-toggle='modal'" : ``}
                    class='dropdown-item ${action.class ? action.class : ``}'
                    ${action.hidden ? "style='display: none'" : ``}
                    ${action.external ? "target='_about'" : ``}
                    >
                    <i class='fas ${action.icon}'></i> ${action.title || ''}
                </a>
            </li>
            `);
                buttons.push(button);
            });

            const list = `<ul class="dropdown-menu">${buttons.join('')}</ul>`;

            return (`<div class='dropdown'>${dropdownButton}${list}</div>`);
        }

        static deleteButtonHandlers(handlerId) {
            DataTableHandlers.deleteHandlersById(handlerId);
        }

        static setAjaxConfig(config, url, dataSrc = '', method = "get", params = {}) {

            config.ajax = {
                url: url,
                type: method,
                dataSrc: dataSrc,
                data: function (d) {
                    return $.extend({}, d, params);
                }
            };

            return config;
        }

        static extendConfig(config, extension) {

            // if there are custom filters then manage state in this way
            if (extension.hasFilters) {

                extension.stateSaveCallback = function (settings, data) {
                    localStorage.setItem('DataTables_' + settings.sInstance, JSON.stringify(data));
                };

                extension.stateLoadCallback = function (settings) {
                    return JSON.parse(localStorage.getItem('DataTables_' + settings.sInstance));
                };

                // on saving the table state store the selected filters
                extension.stateSaveParams = function (settings, data) {

                    // save the filters selected from the user inside the state
                    $('[data-filter]').each(function () {

                        const activeFilter = $(this).find(`li.active`).data('filter-key');
                        if (!activeFilter) return;

                        // if the filters object is not allocated then initizializes it
                        if (!data.filters) data.filters = {};
                        data.filters[$(this).data('filter')] = activeFilter;

                    });
                };
            }

            // const userInitComplete = extension.initComplete;

            // const initComplete = (settings, json) => {
            //     if (userInitComplete !== undefined) userInitComplete(settings, json);
            //     // turn on tooltips
            //     $(`.actions-group [title]`).tooltip('enable');
            // };

            // // override initComplete function
            // extension.initComplete = initComplete;

            return $.extend({}, config, extension);
        }

        /**
         * Format the passed seconds into the "HH:MM:SS" string.
         * @param {number} seconds
         */
        static secondsToHHMMSS(seconds) {

            const padZeroes = n => `${n}`.padStart(2, '0');

            const sec = seconds % 60;
            const mins = Math.floor(seconds / 60) % 60;
            const hours = Math.floor(seconds / 3600);

            return `${padZeroes(hours)}:${padZeroes(mins)}:${padZeroes(sec)}`;
        }

        /**
        * Open the pool edit modal of a chosen pool if the query params contains the pool paramater
        * @param tableAPI
        */
        static openEditModalByQuery(params) {

            const urlParams = new URLSearchParams(window.location.search);
            if (!urlParams.has(params.paramName)) return;

            const dataID = urlParams.get(params.paramName);
            const data = params.datatableInstance.data().toArray().find((data => data[params.paramName] == dataID));

            // if the cancelIf param has been passed
            // then test the cancelIf function, if the return value
            // is true then cancel the modal opening
            if (typeof (params.cancelIf) === 'function') {
                if (params.cancelIf(data)) return;
            }

            const $modal = $(`#${params.modalHandler.getModalID()}`);

            // if the pool id is valid then open the edit modal
            if (data !== undefined) {
                params.modalHandler.invokeModalInit(data);
                $modal.modal('show');
            }

            if (!urlParams.has('referer')) {
                $modal.on('hidden.bs.modal', function (e) {

                    const url = new URL(window.location.href);
                    url.searchParams.delete(params.paramName);

                    history.replaceState({}, '', url.toString());
                });
                return;
            }
            const referer = urlParams.get('referer');

            $modal.on('hidden.bs.modal', function (e) {
                window.location = referer;
            });
        }

        static addToggleColumnsDropdown(tableAPI, toggleCallback = (col, visible) => { }) {

            if (tableAPI === undefined) {
                throw 'The $table is undefined!';
            }

            const tableID = tableAPI.table().node().id;

            DataTableUtils$1._loadColumnsVisibility(tableAPI).then(function (fetchedData) {

                let savedColumns = [-1];
                if (fetchedData.success) {
                    savedColumns = fetchedData.columns.map(i => parseInt(i));
                }
                else {
                    console.warn(fetchedData.message);
                }

                const columns = [];
                const ignoredColumns = [];
                const $datatableWrapper = $(tableAPI.context[0].nTableWrapper);

                // get the table headers 
                tableAPI.columns().every(function (i) {

                    // avoid already hidden columns
                    if (!tableAPI.column(i).visible()) {
                        ignoredColumns.push(i);
                        return;
                    }

                    columns.push({ index: i, name: this.header().textContent, label: this.i18n.name /* Human-readable column name */ });
                });

                const $btnGroup = $(`
                <div class="btn-group">
                    <button type="button" class="btn btn-link dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                        <i class="fas fa-eye"></i>
                    </button>
                </div>
            `);

                const $dropdownMenu = $(`<div class="dropdown-menu dropdown-menu-right" style='width: max-content;'><h6 class="dropdown-header">Show Columns</h6></div>`);
                const $checkboxes = $(`<div class='px-4'></div>`);

                for (let i = 0; i < columns.length; i++) {
                    const column = columns[i];

                    // Prevents columns with no names to be selectively hidden (e.g., the entity under the all alerts page)
                    if (column.name == "")
                        continue;

                    // create a checkbox and delegate a change event
                    const id = `toggle-${column.name.split().join('_')}`;

                    // check if the column id it's inside the savedColumns array
                    // if toggled is true then the column is not hidden
                    const toggled = savedColumns.indexOf(column.index) === -1;
                    if (!toggled) {
                        const col = tableAPI.column(column.index);
                        col.visible(false);
                    }

                    const $checkbox = $(`<input class="form-check-input" ${(toggled ? 'checked' : '')} type="checkbox" id="${id}">`);
                    const $wrapper = $(`
                    <div class="form-check form-switch">
                        <label class="form-check-label" for="${id}">
                            ${column.name}
                        </label>
                    </div>
                `);

                    $checkbox.on('change', function (e) {
                        $(`.overlay`).toggle(500);

                        // Get the column API object
                        const col = tableAPI.column(column.index);
                        // Toggle the visibility
                        col.visible(!col.visible());

                        const visible = col.visible();

                        const hiddenColumns = [];
                        // insert inside the array only the hidden columns
                        tableAPI.columns().every(function (i) {
                            if (tableAPI.column(i).visible() || ignoredColumns.indexOf(i) !== -1) return;
                            hiddenColumns.push(i);
                        });

                        // save the table view inside redis
                        $.post(`${http_prefix}/lua/datatable_columns.lua`, {
                            action: 'save', table: tableID, columns: hiddenColumns.join(','), csrf: window.__CSRF_DATATABLE__
                        }).then(function (data) {
                            if (data.success) return;
                            console.warn(data.message);
                        });

                        if (toggleCallback !== undefined) {
                            toggleCallback(col, visible);
                        }

                    });

                    $wrapper.prepend($checkbox);
                    $checkboxes.append($wrapper);
                }

                $dropdownMenu.on("click.bs.dropdown", function (e) { e.stopPropagation(); });

                // append the new node inside the datatable
                $btnGroup.append($dropdownMenu.append($checkboxes));
                $datatableWrapper.find('.dt-search').parent().append($btnGroup);
            });
        }

        static async _loadColumnsVisibility(tableAPI) {
            const tableID = tableAPI.table().node().id;
            return $.get(`${http_prefix}/lua/datatable_columns.lua?table=${tableID}&action=load`);
        }

    }

    class DataTableRenders$1 {

        static alertSeverityAndType(severity, type, alert) {
            return `${DataTableRenders$1.formatValueLabel(severity, type, alert)} ${DataTableRenders$1.formatValueLabel(alert.alert_id, type, alert)}`;
        }

        static hideIfZero(obj, type, row, zero_is_null) {
            let color = (obj.color !== undefined ? obj.color : "#aaa");
            let value = (obj.value !== undefined ? obj.value : obj);
            if (type === "display" && parseInt(value) === 0) color = "#aaa";
            let span = `<span style='color: ${color}'>${NtopUtils$1.fint(value)}</span>`;
            if (obj.url !== undefined) span = `<a href="${obj.url}">${span}</a>`;
            return span;
        }

        static secondsToTime(seconds, type, row, zero_is_null) {
            if (type === "display") return NtopUtils$1.secondsToTime(seconds);
            return seconds;
        }

        static filterize(key, value, label, tag_label, title, html, is_snmp_ip, ip) {
            let content = `<a class='tag-filter' data-bs-toggle='tooltip' data-bs-placement="bottom" data-tag-key='${key}' title='${title || value}' data-tag-value='${value}' data-tag-label='${tag_label || label || value}' href='javascript:void(0)'>${html || label || value}</a>`;
            if (is_snmp_ip != null) {
                if (is_snmp_ip) {
                    if (value) {
                        let url = NtopUtils$1.buildURL(`${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?host=${value}`);
                        content += ` <a href='${url}' data-bs-toggle='tooltip' data-bs-placement="bottom" title=''><i class='fas fa-laptop'></i></a>`;
                    }
                } else {
                    if (ip && value) {

                        if (value.includes(ip)) {
                            value = value.split("_")[1];
                        }
                        let url = NtopUtils$1.buildURL(`${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${ip}&snmp_port_idx=${value}`);
                        content += ` <a href='${url}' data-bs-toggle='tooltip' data-bs-placement="bottom" title=''><i class='fas fa-laptop'></i></a>`;
                    }
                }
            }
            return content;
        }

        static formatValueLabel(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.value;
            let cell = obj.label;
            if (zero_is_null == true && obj.value == 0) {
                cell = "";
            }
            if (obj.color) cell = `<span class='font-weight-bold' style='color: ${obj.color}'>${cell}</span>`;
            if (obj.title) cell = `<span data-bs-toggle="tooltip" data-bs-placement="bottom" title='${obj.title}'>${cell}</span>`;
            return cell;
        }

        static formatValueLabelEngaged(obj, type, row, zero_is_null) {
            let date_time = this.formatValueLabel(obj, type, row, zero_is_null);
            if (row.is_engaged) {
                date_time = `<i class="fa-solid fa-fire" data-bs-toggle="tooltip" data-bs-placement="top" title="${i18n('engaged_alert')}"></i> ${date_time}`;
            }
            return date_time;
        }

        static formatCategory(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.value;
            let cell = `<a class='tag-filter' data-tag-key='alert_category' data-tag-value='${obj.value}' data-tag-label='${obj.label}' href='javascript:void(0)'><i class="fa fas ${obj.icon}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="${obj.label}"></i></a>`;
            if (zero_is_null == true && obj.value == 0) {
                cell = "";
            }
            return cell;
        }

        static formatMitreId(obj) {
            if (obj.mitre_id !== "0") {
                return DataTableRenders$1.filterize('mitre_id', obj.mitre_id, obj.mitre_id_i18n, obj.mitre_id_i18n, obj.mitre_id_i18n);
            }
            return ""
        }

        static formatMitreTactic(obj) {
            if (obj.mitre_tactic !== "0") {
                return DataTableRenders$1.filterize('mitre_tactic', obj.mitre_tactic, i18n(obj.mitre_tactic_i18n), obj.mitre_tactic_i18n, obj.mitre_tactic_i18n);
            }

            return ""
        }

        static formatMitreTechnique(obj) {
            if (obj.mitre_technique !== "0") {
                return DataTableRenders$1.filterize('mitre_technique', obj.mitre_technique, i18n(obj.mitre_technique_i18n), obj.mitre_technique_i18n, obj.mitre_technique_i18n);
            }
            return ""
        }

        static formatMitreSubTechnique(obj) {
            if (obj.mitre_subtechnique !== "0") {
                return DataTableRenders$1.filterize('mitre_subtechnique', obj.mitre_subtechnique, i18n(obj.mitre_subtechnique_i18n), obj.mitre_subtechnique_i18n, obj.mitre_subtechnique_i18n);
            }
            return ""
        }

        static formatSRC2DSTTCPFlags(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.value;
            let cell = `<a class='tag-filter' data-tag-key='src2dst_tcp_flags' data-tag-value='${obj.value}' data-tag-label='${obj.label}' href='javascript:void(0)'>${obj.label}</a>`;
            return cell
        }

        static formatDST2SRCTCPFlags(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.value;
            let cell = `<a class='tag-filter' data-tag-key='dst2src_tcp_flags' data-tag-value='${obj.value}' data-tag-label='${obj.label}' href='javascript:void(0)'>${obj.label}</a>`;
            return cell
        }

        static formatScore(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.value;
            let cell = obj.label;
            if (zero_is_null == true && obj.value == 0) {
                cell = "";
            }
            if (obj.color) cell = `<span class='font-weight-bold' style='color: ${obj.color}'>${cell}</span>`;
            return `<a class='tag-filter' data-tag-key='score' title='${obj.label}' data-tag-value='${obj.value}' data-tag-label='${obj.label}' href='javascript:void(0)'>${cell}</a>`;
        }

        static formatQoE(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.value;
            obj.label;
            if (zero_is_null == true && obj.value == 0) ;
            return `<a class='tag-filter' data-tag-key='qoe_score' title='${obj.label}' data-tag-value='${dataUtils$1.GetQoEID(obj.value)}' data-tag-label='${obj.label}' href='javascript:void(0)'>${dataUtils$1.QoEQualityIcon(obj.value)}</a>`;
        }

        static formatMessage(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.value;

            let cell = obj.descr;
            if (obj.shorten_descr)
                cell = `<span data-bs-toggle="tooltip" data-bs-placement="bottom" title="${obj.descr}">${obj.shorten_descr}</span>`;

            return cell;
        }

        static formatTraffic(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.total_bytes;

            const traffic = `${NtopUtils$1.formatPackets(obj.total_packets)} / ${NtopUtils$1.bytesToVolume(obj.total_bytes)}`;
            return traffic;
        }

        static formatSubtype(obj, type, row, zero_is_null) {
            if (type !== "display") return obj;

            let label = DataTableRenders$1.filterize('subtype', obj, obj);

            return label;
        }

        static filterize_2(key, value, label, tag_label, title, html) {
            if (value == null || (value == 0 && (label == null || label == ""))) { return ""; }
            return `<a class='tag-filter' data-bs-toggle="tooltip" data-bs-placement="bottom" data-tag-key='${key}' title='${title || value}' data-tag-value='${value}' data-tag-label='${tag_label || label || value}' href='javascript:void(0)'>${html || label || value}</a>`;
        }

        static getFormatGenericField(field, zero_is_null) {
            return function (obj, type, row) {
                if (type !== "display") return obj.value;
                if (zero_is_null == true && obj?.value == 0) { return ""; }
                let formatted = '';
                //if (typeof row[field] === 'object') {
                formatted = DataTableRenders$1.filterize_2(field, row[field].value, row[field].label, row[field].label, row[field].label);
                if (obj.reference !== undefined)
                    formatted = formatted + ' ' + obj.reference;
                //} else {
                //    formatted = DataTableRenders.filterize_2(field, row[field], row[field], row[field], row[field]);
                //}
                return formatted;
            }
        }

        static formatSNMPInterface(obj, type, row) {
            if (type !== "display") return obj.value;
            if (obj.value === undefined || obj.value == '')
                return ''; /* interface not defined - probably a device-level alert */
            let cell = DataTableRenders$1.filterize('snmp_interface', `${row.ip}_${obj.value}`, obj.label, obj.label, obj.label, null, false, row.ip);
            if (obj.color) cell = `<span class='font-weight-bold' style='color: ${obj.color}'>${cell}</span>`;
            return cell;
        }

        static formatSNMPIP(obj, type, row, zero_is_null) {
            if (type !== "display") return obj;
            return DataTableRenders$1.filterize('ip', obj, obj, obj, obj, null, true);
        }

        static formatNetwork(obj, type, row, zero_is_null) {
            if (type !== "display") return obj;
            return DataTableRenders$1.filterize('network_name', obj, obj, obj, obj, null, false);
        }

        static formatAS(obj, type, row, zero_is_null) {
            if (type !== "display") return obj;
            return DataTableRenders$1.filterize('asn', obj, obj, obj, obj, null, false);
        }

        static formatProbeIP(obj, type, row, zero_is_null) {
            if (type !== "display") return obj;

            let label = DataTableRenders$1.filterize('probe_ip', obj.value, obj.label, obj.label, obj.label_long);

            return label;
        }

        static formatHost(obj, type, row, zero_is_null) {
            if (type !== "display") return obj;
            let html_ref = '';
            if (obj.reference !== undefined)
                html_ref = obj.reference;
            let label = "";

            let hostKey, hostValue;
            if (obj.label && obj.label != obj.value) {
                hostKey = "name";
                hostValue = obj.label_long;
                label = DataTableRenders$1.filterize('name', obj.label_long, obj.label, obj.label, obj.label_long);
            }
            else {
                hostKey = "ip";
                hostValue = obj.value;
                label = DataTableRenders$1.filterize('ip', obj.value, obj.label, obj.label, obj.label_long);
            }

            if (row.vlan_id && row.vlan_id != "" && row.vlan_id != "0") {
                label = DataTableRenders$1.filterize(hostKey, `${hostValue}@${row.vlan_id}`, `${obj.label}@${row.vlan_id}`, `${obj.label}@${row.vlan_id}`, `${obj.label_long}@${row.vlan_id}`);
            }

            if (obj.country)
                label = label + DataTableRenders$1.filterize('country', obj.country, obj.country, obj.country, obj.country, ' <img src="' + http_prefix + '/dist/images/blank.gif" class="flag flag-' + obj.country.toLowerCase() + '"></a> ');

            if (obj.is_multicast) {
                label = label + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("multicast") + "'><span class='badge bg-primary'>" + i18n("short_multicast") + "</span></abbr> ";
            } else if (obj.is_local) {
                label = label + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_local_host") + "'><span class='badge bg-success'>" + i18n("details.label_short_local_host") + "</span></abbr> ";
            } else {
                label = label + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_remote") + "'><span class='badge bg-secondary'>" + i18n("details.label_short_remote") + "</span></abbr> ";
            }

            if (row.role && row.role.value == 'attacker')
                label = label + ' ' + DataTableRenders$1.filterize('role', row.role.value,
                    '<i class="fas fa-skull" data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + row.role.label + '"></i>', row.role.label);
            else if (row.role && row.role.value == 'victim')
                label = label + ' ' + DataTableRenders$1.filterize('role', row.role.value,
                    '<i class="fas fa-sad-tear" data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + row.role.label + '"></i>', row.role.label);

            if (row.role_cli_srv && row.role_cli_srv.value == 'client')
                label = label + ' ' + DataTableRenders$1.filterize('role_cli_srv', row.role_cli_srv.value,
                    '<i class="fas fa-long-arrow-alt-right" data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + row.role_cli_srv.label + '"></i>', row.role_cli_srv.label);
            else if (row.role_cli_srv && row.role_cli_srv.value == 'server')
                label = label + ' ' + DataTableRenders$1.filterize('role_cli_srv', row.role_cli_srv.value,
                    '<i class="fas fa-long-arrow-alt-left" data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + row.role_cli_srv.label + '"></i>', row.role_cli_srv.label);

            return label + ' ' + html_ref;
        }

        static filterizeVlan(vlan, key, value, label, title) {
            let valueVlan = value;
            let labelVlan = label;
            let titleVlan = title;
            if (vlan && vlan.value != 0) {
                valueVlan = `${value}@${vlan.value}`;
                labelVlan = `${label}@${vlan.label}`;
                titleVlan = `${title}@${vlan.title}`;
            }
            return DataTableRenders$1.filterize(key, valueVlan, labelVlan, labelVlan, titleVlan);
        }

        static formatIP(ip, type, row, zero_is_null) {
            if (!ip) return "";
            let title = "";
            if (ip.label_long) title = ip.value + " [" + ip.label_long + "]";
            let label = DataTableRenders$1.filterize(ip.tag_key, ip.value, ip.label, ip.label, title);
            return label;
        }

        static formatFlowTuple(flow, type, row, zero_is_null) {
            let active_ref = (flow.active_url ? `<a href="${flow.active_url}"><i class="fas fa-stream"></i></a>` : "");
            let cliLabel = "";
            if (flow.cli_ip.name) {
                let title = "";
                if (flow.cli_ip.label_long) title = flow.cli_ip.value + " [" + flow.cli_ip.label_long + "]";
                cliLabel = DataTableRenders$1.filterizeVlan(flow.vlan, 'cli_name', flow.cli_ip.name, flow.cli_ip.label, title);
            } else
                cliLabel = DataTableRenders$1.filterizeVlan(flow.vlan, 'cli_ip', flow.cli_ip.value, flow.cli_ip.label, flow.cli_ip.label_long);

            let cliFlagLabel = '';

            if (flow.cli_ip.country && flow.cli_ip.country !== "nil")
                cliFlagLabel = DataTableRenders$1.filterize('cli_country', flow.cli_ip.country, flow.cli_ip.country, flow.cli_ip.country, flow.cli_ip.country, ' <img src="' + http_prefix + '/dist/images/blank.gif" class="flag flag-' + flow.cli_ip.country.toLowerCase() + '"></a> ');

            let cliPortLabel = ((flow.cli_port && flow.cli_port.value && flow.cli_port.value != 0) ? ":" + DataTableRenders$1.filterize('cli_port', flow.cli_port.value, flow.cli_port.label) : "");

            let cliBlacklisted = '';
            if (flow.cli_ip.blacklisted == true)
                cliBlacklisted = " <i class=\'fas fa-ban fa-sm\'  data-bs-toggle=\"tooltip\" data-bs-placement=\"bottom\" title=\'" + i18n("hosts_stats.blacklisted") + "\'></i>";

            let cliLocation = '';
            if (flow.cli_ip.location == 'multicast') {
                cliLocation = cliLocation + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("multicast") + "'><span class='badge bg-primary'>" + i18n("short_multicast") + "</span></abbr> ";
            } else if (flow.cli_ip.location == 'local') {
                cliLocation = cliLocation + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_local_host") + "'><span class='badge bg-success'>" + i18n("details.label_short_local_host") + "</span></abbr> ";
            } else {
                cliLocation = cliLocation + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_remote") + "'><span class='badge bg-secondary'>" + i18n("details.label_short_remote") + "</span></abbr> ";
            }

            let srvLabel = "";
            if (flow.srv_ip.name) {
                let title = "";
                if (flow.srv_ip.label_long) title = flow.srv_ip.value + " [" + flow.srv_ip.label_long + "]";
                srvLabel = DataTableRenders$1.filterizeVlan(flow.vlan, 'srv_name', flow.srv_ip.name, flow.srv_ip.label, title);
            } else
                srvLabel = DataTableRenders$1.filterizeVlan(flow.vlan, 'srv_ip', flow.srv_ip.value, flow.srv_ip.label, flow.srv_ip.label_long);
            let srvPortLabel = ((flow.srv_port && flow.srv_port.value && flow.srv_port.value != 0) ? ":" + DataTableRenders$1.filterize('srv_port', flow.srv_port.value, flow.srv_port.label) : "");

            let srvFlagLabel = '';

            if (flow.srv_ip.country && flow.srv_ip.country !== "nil")
                srvFlagLabel = DataTableRenders$1.filterize('srv_country', flow.srv_ip.country, flow.srv_ip.country, flow.srv_ip.country, flow.srv_ip.country, ' <img src="' + http_prefix + '/dist/images/blank.gif" class="flag flag-' + flow.srv_ip.country.toLowerCase() + '"></a> ');

            let srvBlacklisted = '';
            if (flow.srv_ip.blacklisted == true)
                srvBlacklisted = " <i class=\'fas fa-ban fa-sm\' data-bs-toggle=\"tooltip\" data-bs-placement=\"bottom\" title=\'" + i18n("hosts_stats.blacklisted") + "\'></i>";

            let srvLocation = '';
            if (flow.srv_ip.location == 'multicast') {
                srvLocation = srvLocation + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("multicast") + "'><span class='badge bg-primary'>" + i18n("short_multicast") + "</span></abbr> ";
            } else if (flow.srv_ip.location == 'local') {
                srvLocation = srvLocation + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_local_host") + "'><span class='badge bg-success'>" + i18n("details.label_short_local_host") + "</span></abbr> ";
            } else {
                srvLocation = srvLocation + " <abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_remote") + "'><span class='badge bg-secondary'>" + i18n("details.label_short_remote") + "</span></abbr> ";
            }

            let cliIcons = "";
            let srvIcons = "";
            if (row.cli_role) {
                if (row.cli_role.value == 'attacker')
                    cliIcons += DataTableRenders$1.filterize('role', 'attacker', '<i class="fas fa-skull" data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + row.cli_role.label + '"></i>', row.cli_role.tag_label);
                else if (row.cli_role.value == 'victim')
                    cliIcons += DataTableRenders$1.filterize('role', 'victim', '<i class="fas fa-sad-tear" data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + row.cli_role.label + '"></i>', row.cli_role.tag_label);
            }

            if (row.srv_role) {
                if (row.srv_role.value == 'attacker')
                    srvIcons += DataTableRenders$1.filterize('role', 'attacker', '<i class="fas fa-skull" data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + row.srv_role.label + '"></i>', row.srv_role.tag_label);
                else if (row.srv_role.value == 'victim')
                    srvIcons += DataTableRenders$1.filterize('role', 'victim', '<i class="fas fa-sad-tear" data-bs-toggle="tooltip" data-bs-placement="bottom" title="' + row.srv_role.label + '"></i>', row.srv_role.tag_label);
            }

            let flow_string = `${active_ref} ${cliLabel}${cliBlacklisted}${cliLocation}${cliFlagLabel}${cliPortLabel} ${cliIcons} ${flow.cli_ip.reference} <i class="fas fa-exchange-alt fa-lg" aria-hidden="true"></i> ${srvLabel}${srvBlacklisted}${srvLocation}${srvFlagLabel}${srvPortLabel} ${srvIcons} ${flow.srv_ip.reference}`;
            
            if (row.protocol_info_json && row.protocol_info_json.verdict && row.protocol_info_json.verdict.pass === 0)
                flow_string = `<strike>${flow_string}</strike>`;

            return flow_string;
        }

        static formatSubtypeValueLabel(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.name;
            let msg = DataTableRenders$1.filterize('subtype', obj.value, obj.name, obj.fullname, obj.fullname);

            return msg;
        }

        static formatNameDescription(obj, type, row, zero_is_null) {
            if (type !== "display") return obj.name;
            let msg = DataTableRenders$1.filterize('alert_id', obj.value, obj.name, obj.fullname, obj.fullname);

            return msg;
        }

        static applyCellStyle(cell, cellData, rowData, rowIndex, colIndex) {
            if (cellData.highlight) {
                $(cell).css("border-left", "5px solid " + cellData.highlight);
            }
        }
    }

    window.do_pie = do_pie;

    window.DataTableUtils = DataTableUtils$1;
    window.DataTableFiltersMenu = DataTableFiltersMenu$1;
    window.DataTableRenders = DataTableRenders$1;

    window.ChartWidget = ChartWidget;
    window.WidgetUtils = WidgetUtils;
    window.ntopChartApex = ntopChartApex$1;

    /* Handle Blog Notifications */
    $(function () {

      function blogNotifcationClick(e) {

        if (e.type == "mousedown" && (e.metaKey || e.ctrlKey || e.which !== 2)) return;

        const id = $(this).data('id');

        $.post(`${http_prefix}/lua/update_blog_posts.lua`, {
          blog_notification_id: id,
          csrf: window.__BLOG_NOTIFICATION_CSRF__
        },
          (data) => {

            if (data.success) {

              $(this).off('click').off('mousedown').attr('data-read', 'true').data('read', 'true').find('.badge').remove();
              
              const count = $(`.blog-notification[data-read='false']`).length;

              if (count == 0) {
                $('.notification-bell').remove();
              }
              else {
                $('.notification-bell').html(count);
              }
            }
          });
      }

      // on the notifications not yet read delegate the click event
      $(`.blog-notification[data-read='false']`).on('click', blogNotifcationClick).on('mousedown', blogNotifcationClick);
    });

    /**
     * Make a map and return a function for checking if a key
     * is in that map.
     * IMPORTANT: all calls of this function must be prefixed with
     * \/\*#\_\_PURE\_\_\*\/
     * So that rollup can tree-shake them if necessary.
     */
    function makeMap(str, expectsLowerCase) {
        const map = Object.create(null);
        const list = str.split(',');
        for (let i = 0; i < list.length; i++) {
            map[list[i]] = true;
        }
        return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val];
    }

    /**
     * dev only flag -> name mapping
     */
    const PatchFlagNames = {
        [1 /* TEXT */]: `TEXT`,
        [2 /* CLASS */]: `CLASS`,
        [4 /* STYLE */]: `STYLE`,
        [8 /* PROPS */]: `PROPS`,
        [16 /* FULL_PROPS */]: `FULL_PROPS`,
        [32 /* HYDRATE_EVENTS */]: `HYDRATE_EVENTS`,
        [64 /* STABLE_FRAGMENT */]: `STABLE_FRAGMENT`,
        [128 /* KEYED_FRAGMENT */]: `KEYED_FRAGMENT`,
        [256 /* UNKEYED_FRAGMENT */]: `UNKEYED_FRAGMENT`,
        [512 /* NEED_PATCH */]: `NEED_PATCH`,
        [1024 /* DYNAMIC_SLOTS */]: `DYNAMIC_SLOTS`,
        [2048 /* DEV_ROOT_FRAGMENT */]: `DEV_ROOT_FRAGMENT`,
        [-1 /* HOISTED */]: `HOISTED`,
        [-2 /* BAIL */]: `BAIL`
    };

    /**
     * Dev only
     */
    const slotFlagsText = {
        [1 /* STABLE */]: 'STABLE',
        [2 /* DYNAMIC */]: 'DYNAMIC',
        [3 /* FORWARDED */]: 'FORWARDED'
    };

    const GLOBALS_WHITE_LISTED = 'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' +
        'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
        'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt';
    const isGloballyWhitelisted = /*#__PURE__*/ makeMap(GLOBALS_WHITE_LISTED);

    const range = 2;
    function generateCodeFrame(source, start = 0, end = source.length) {
        // Split the content into individual lines but capture the newline sequence
        // that separated each line. This is important because the actual sequence is
        // needed to properly take into account the full line length for offset
        // comparison
        let lines = source.split(/(\r?\n)/);
        // Separate the lines and newline sequences into separate arrays for easier referencing
        const newlineSequences = lines.filter((_, idx) => idx % 2 === 1);
        lines = lines.filter((_, idx) => idx % 2 === 0);
        let count = 0;
        const res = [];
        for (let i = 0; i < lines.length; i++) {
            count +=
                lines[i].length +
                    ((newlineSequences[i] && newlineSequences[i].length) || 0);
            if (count >= start) {
                for (let j = i - range; j <= i + range || end > count; j++) {
                    if (j < 0 || j >= lines.length)
                        continue;
                    const line = j + 1;
                    res.push(`${line}${' '.repeat(Math.max(3 - String(line).length, 0))}|  ${lines[j]}`);
                    const lineLength = lines[j].length;
                    const newLineSeqLength = (newlineSequences[j] && newlineSequences[j].length) || 0;
                    if (j === i) {
                        // push underline
                        const pad = start - (count - (lineLength + newLineSeqLength));
                        const length = Math.max(1, end > count ? lineLength - pad : end - start);
                        res.push(`   |  ` + ' '.repeat(pad) + '^'.repeat(length));
                    }
                    else if (j > i) {
                        if (end > count) {
                            const length = Math.max(Math.min(end - count, lineLength), 1);
                            res.push(`   |  ` + '^'.repeat(length));
                        }
                        count += lineLength + newLineSeqLength;
                    }
                }
                break;
            }
        }
        return res.join('\n');
    }

    /**
     * On the client we only need to offer special cases for boolean attributes that
     * have different names from their corresponding dom properties:
     * - itemscope -> N/A
     * - allowfullscreen -> allowFullscreen
     * - formnovalidate -> formNoValidate
     * - ismap -> isMap
     * - nomodule -> noModule
     * - novalidate -> noValidate
     * - readonly -> readOnly
     */
    const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`;
    const isSpecialBooleanAttr = /*#__PURE__*/ makeMap(specialBooleanAttrs);
    /**
     * Boolean attributes should be included if the value is truthy or ''.
     * e.g. `<select multiple>` compiles to `{ multiple: '' }`
     */
    function includeBooleanAttr(value) {
        return !!value || value === '';
    }

    function normalizeStyle(value) {
        if (isArray(value)) {
            const res = {};
            for (let i = 0; i < value.length; i++) {
                const item = value[i];
                const normalized = isString(item)
                    ? parseStringStyle(item)
                    : normalizeStyle(item);
                if (normalized) {
                    for (const key in normalized) {
                        res[key] = normalized[key];
                    }
                }
            }
            return res;
        }
        else if (isString(value)) {
            return value;
        }
        else if (isObject(value)) {
            return value;
        }
    }
    const listDelimiterRE = /;(?![^(]*\))/g;
    const propertyDelimiterRE = /:(.+)/;
    function parseStringStyle(cssText) {
        const ret = {};
        cssText.split(listDelimiterRE).forEach(item => {
            if (item) {
                const tmp = item.split(propertyDelimiterRE);
                tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
            }
        });
        return ret;
    }
    function normalizeClass(value) {
        let res = '';
        if (isString(value)) {
            res = value;
        }
        else if (isArray(value)) {
            for (let i = 0; i < value.length; i++) {
                const normalized = normalizeClass(value[i]);
                if (normalized) {
                    res += normalized + ' ';
                }
            }
        }
        else if (isObject(value)) {
            for (const name in value) {
                if (value[name]) {
                    res += name + ' ';
                }
            }
        }
        return res.trim();
    }
    function normalizeProps(props) {
        if (!props)
            return null;
        let { class: klass, style } = props;
        if (klass && !isString(klass)) {
            props.class = normalizeClass(klass);
        }
        if (style) {
            props.style = normalizeStyle(style);
        }
        return props;
    }

    // These tag configs are shared between compiler-dom and runtime-dom, so they
    // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
    const HTML_TAGS = 'html,body,base,head,link,meta,style,title,address,article,aside,footer,' +
        'header,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,' +
        'figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,' +
        'data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,' +
        'time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,' +
        'canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,' +
        'th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,' +
        'option,output,progress,select,textarea,details,dialog,menu,' +
        'summary,template,blockquote,iframe,tfoot';
    // https://developer.mozilla.org/en-US/docs/Web/SVG/Element
    const SVG_TAGS = 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' +
        'defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,' +
        'feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,' +
        'feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,' +
        'feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,' +
        'fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,' +
        'foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,' +
        'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' +
        'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' +
        'text,textPath,title,tspan,unknown,use,view';
    const VOID_TAGS = 'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr';
    /**
     * Compiler only.
     * Do NOT use in runtime code paths unless behind `true` flag.
     */
    const isHTMLTag = /*#__PURE__*/ makeMap(HTML_TAGS);
    /**
     * Compiler only.
     * Do NOT use in runtime code paths unless behind `true` flag.
     */
    const isSVGTag = /*#__PURE__*/ makeMap(SVG_TAGS);
    /**
     * Compiler only.
     * Do NOT use in runtime code paths unless behind `true` flag.
     */
    const isVoidTag = /*#__PURE__*/ makeMap(VOID_TAGS);

    function looseCompareArrays(a, b) {
        if (a.length !== b.length)
            return false;
        let equal = true;
        for (let i = 0; equal && i < a.length; i++) {
            equal = looseEqual(a[i], b[i]);
        }
        return equal;
    }
    function looseEqual(a, b) {
        if (a === b)
            return true;
        let aValidType = isDate(a);
        let bValidType = isDate(b);
        if (aValidType || bValidType) {
            return aValidType && bValidType ? a.getTime() === b.getTime() : false;
        }
        aValidType = isSymbol(a);
        bValidType = isSymbol(b);
        if (aValidType || bValidType) {
            return a === b;
        }
        aValidType = isArray(a);
        bValidType = isArray(b);
        if (aValidType || bValidType) {
            return aValidType && bValidType ? looseCompareArrays(a, b) : false;
        }
        aValidType = isObject(a);
        bValidType = isObject(b);
        if (aValidType || bValidType) {
            /* istanbul ignore if: this if will probably never be called */
            if (!aValidType || !bValidType) {
                return false;
            }
            const aKeysCount = Object.keys(a).length;
            const bKeysCount = Object.keys(b).length;
            if (aKeysCount !== bKeysCount) {
                return false;
            }
            for (const key in a) {
                const aHasKey = a.hasOwnProperty(key);
                const bHasKey = b.hasOwnProperty(key);
                if ((aHasKey && !bHasKey) ||
                    (!aHasKey && bHasKey) ||
                    !looseEqual(a[key], b[key])) {
                    return false;
                }
            }
        }
        return String(a) === String(b);
    }
    function looseIndexOf(arr, val) {
        return arr.findIndex(item => looseEqual(item, val));
    }

    /**
     * For converting {{ interpolation }} values to displayed strings.
     * @private
     */
    const toDisplayString = (val) => {
        return isString(val)
            ? val
            : val == null
                ? ''
                : isArray(val) ||
                    (isObject(val) &&
                        (val.toString === objectToString || !isFunction(val.toString)))
                    ? JSON.stringify(val, replacer, 2)
                    : String(val);
    };
    const replacer = (_key, val) => {
        // can't use isRef here since @vue/shared has no deps
        if (val && val.__v_isRef) {
            return replacer(_key, val.value);
        }
        else if (isMap(val)) {
            return {
                [`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val]) => {
                    entries[`${key} =>`] = val;
                    return entries;
                }, {})
            };
        }
        else if (isSet(val)) {
            return {
                [`Set(${val.size})`]: [...val.values()]
            };
        }
        else if (isObject(val) && !isArray(val) && !isPlainObject(val)) {
            return String(val);
        }
        return val;
    };

    const EMPTY_OBJ = Object.freeze({})
        ;
    const EMPTY_ARR = Object.freeze([]) ;
    const NOOP = () => { };
    /**
     * Always return false.
     */
    const NO = () => false;
    const onRE = /^on[^a-z]/;
    const isOn = (key) => onRE.test(key);
    const isModelListener = (key) => key.startsWith('onUpdate:');
    const extend = Object.assign;
    const remove = (arr, el) => {
        const i = arr.indexOf(el);
        if (i > -1) {
            arr.splice(i, 1);
        }
    };
    const hasOwnProperty = Object.prototype.hasOwnProperty;
    const hasOwn = (val, key) => hasOwnProperty.call(val, key);
    const isArray = Array.isArray;
    const isMap = (val) => toTypeString(val) === '[object Map]';
    const isSet = (val) => toTypeString(val) === '[object Set]';
    const isDate = (val) => toTypeString(val) === '[object Date]';
    const isFunction = (val) => typeof val === 'function';
    const isString = (val) => typeof val === 'string';
    const isSymbol = (val) => typeof val === 'symbol';
    const isObject = (val) => val !== null && typeof val === 'object';
    const isPromise = (val) => {
        return isObject(val) && isFunction(val.then) && isFunction(val.catch);
    };
    const objectToString = Object.prototype.toString;
    const toTypeString = (value) => objectToString.call(value);
    const toRawType = (value) => {
        // extract "RawType" from strings like "[object RawType]"
        return toTypeString(value).slice(8, -1);
    };
    const isPlainObject = (val) => toTypeString(val) === '[object Object]';
    const isIntegerKey = (key) => isString(key) &&
        key !== 'NaN' &&
        key[0] !== '-' &&
        '' + parseInt(key, 10) === key;
    const isReservedProp = /*#__PURE__*/ makeMap(
    // the leading comma is intentional so empty string "" is also included
    ',key,ref,ref_for,ref_key,' +
        'onVnodeBeforeMount,onVnodeMounted,' +
        'onVnodeBeforeUpdate,onVnodeUpdated,' +
        'onVnodeBeforeUnmount,onVnodeUnmounted');
    const isBuiltInDirective = /*#__PURE__*/ makeMap('bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo');
    const cacheStringFunction = (fn) => {
        const cache = Object.create(null);
        return ((str) => {
            const hit = cache[str];
            return hit || (cache[str] = fn(str));
        });
    };
    const camelizeRE = /-(\w)/g;
    /**
     * @private
     */
    const camelize = cacheStringFunction((str) => {
        return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
    });
    const hyphenateRE = /\B([A-Z])/g;
    /**
     * @private
     */
    const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase());
    /**
     * @private
     */
    const capitalize = cacheStringFunction((str) => str.charAt(0).toUpperCase() + str.slice(1));
    /**
     * @private
     */
    const toHandlerKey = cacheStringFunction((str) => str ? `on${capitalize(str)}` : ``);
    // compare whether a value has changed, accounting for NaN.
    const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
    const invokeArrayFns = (fns, arg) => {
        for (let i = 0; i < fns.length; i++) {
            fns[i](arg);
        }
    };
    const def = (obj, key, value) => {
        Object.defineProperty(obj, key, {
            configurable: true,
            enumerable: false,
            value
        });
    };
    const toNumber = (val) => {
        const n = parseFloat(val);
        return isNaN(n) ? val : n;
    };
    let _globalThis;
    const getGlobalThis = () => {
        return (_globalThis ||
            (_globalThis =
                typeof globalThis !== 'undefined'
                    ? globalThis
                    : typeof self !== 'undefined'
                        ? self
                        : typeof window !== 'undefined'
                            ? window
                            : typeof global !== 'undefined'
                                ? global
                                : {}));
    };

    function warn(msg, ...args) {
        console.warn(`[Vue warn] ${msg}`, ...args);
    }

    let activeEffectScope;
    class EffectScope {
        constructor(detached = false) {
            /**
             * @internal
             */
            this.active = true;
            /**
             * @internal
             */
            this.effects = [];
            /**
             * @internal
             */
            this.cleanups = [];
            if (!detached && activeEffectScope) {
                this.parent = activeEffectScope;
                this.index =
                    (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1;
            }
        }
        run(fn) {
            if (this.active) {
                const currentEffectScope = activeEffectScope;
                try {
                    activeEffectScope = this;
                    return fn();
                }
                finally {
                    activeEffectScope = currentEffectScope;
                }
            }
            else {
                warn(`cannot run an inactive effect scope.`);
            }
        }
        /**
         * This should only be called on non-detached scopes
         * @internal
         */
        on() {
            activeEffectScope = this;
        }
        /**
         * This should only be called on non-detached scopes
         * @internal
         */
        off() {
            activeEffectScope = this.parent;
        }
        stop(fromParent) {
            if (this.active) {
                let i, l;
                for (i = 0, l = this.effects.length; i < l; i++) {
                    this.effects[i].stop();
                }
                for (i = 0, l = this.cleanups.length; i < l; i++) {
                    this.cleanups[i]();
                }
                if (this.scopes) {
                    for (i = 0, l = this.scopes.length; i < l; i++) {
                        this.scopes[i].stop(true);
                    }
                }
                // nested scope, dereference from parent to avoid memory leaks
                if (this.parent && !fromParent) {
                    // optimized O(1) removal
                    const last = this.parent.scopes.pop();
                    if (last && last !== this) {
                        this.parent.scopes[this.index] = last;
                        last.index = this.index;
                    }
                }
                this.active = false;
            }
        }
    }
    function effectScope(detached) {
        return new EffectScope(detached);
    }
    function recordEffectScope(effect, scope = activeEffectScope) {
        if (scope && scope.active) {
            scope.effects.push(effect);
        }
    }
    function getCurrentScope() {
        return activeEffectScope;
    }
    function onScopeDispose(fn) {
        if (activeEffectScope) {
            activeEffectScope.cleanups.push(fn);
        }
        else {
            warn(`onScopeDispose() is called when there is no active effect scope` +
                ` to be associated with.`);
        }
    }

    const createDep = (effects) => {
        const dep = new Set(effects);
        dep.w = 0;
        dep.n = 0;
        return dep;
    };
    const wasTracked = (dep) => (dep.w & trackOpBit) > 0;
    const newTracked = (dep) => (dep.n & trackOpBit) > 0;
    const initDepMarkers = ({ deps }) => {
        if (deps.length) {
            for (let i = 0; i < deps.length; i++) {
                deps[i].w |= trackOpBit; // set was tracked
            }
        }
    };
    const finalizeDepMarkers = (effect) => {
        const { deps } = effect;
        if (deps.length) {
            let ptr = 0;
            for (let i = 0; i < deps.length; i++) {
                const dep = deps[i];
                if (wasTracked(dep) && !newTracked(dep)) {
                    dep.delete(effect);
                }
                else {
                    deps[ptr++] = dep;
                }
                // clear bits
                dep.w &= ~trackOpBit;
                dep.n &= ~trackOpBit;
            }
            deps.length = ptr;
        }
    };

    const targetMap = new WeakMap();
    // The number of effects currently being tracked recursively.
    let effectTrackDepth = 0;
    let trackOpBit = 1;
    /**
     * The bitwise track markers support at most 30 levels of recursion.
     * This value is chosen to enable modern JS engines to use a SMI on all platforms.
     * When recursion depth is greater, fall back to using a full cleanup.
     */
    const maxMarkerBits = 30;
    let activeEffect;
    const ITERATE_KEY = Symbol('iterate' );
    const MAP_KEY_ITERATE_KEY = Symbol('Map key iterate' );
    class ReactiveEffect {
        constructor(fn, scheduler = null, scope) {
            this.fn = fn;
            this.scheduler = scheduler;
            this.active = true;
            this.deps = [];
            this.parent = undefined;
            recordEffectScope(this, scope);
        }
        run() {
            if (!this.active) {
                return this.fn();
            }
            let parent = activeEffect;
            let lastShouldTrack = shouldTrack;
            while (parent) {
                if (parent === this) {
                    return;
                }
                parent = parent.parent;
            }
            try {
                this.parent = activeEffect;
                activeEffect = this;
                shouldTrack = true;
                trackOpBit = 1 << ++effectTrackDepth;
                if (effectTrackDepth <= maxMarkerBits) {
                    initDepMarkers(this);
                }
                else {
                    cleanupEffect(this);
                }
                return this.fn();
            }
            finally {
                if (effectTrackDepth <= maxMarkerBits) {
                    finalizeDepMarkers(this);
                }
                trackOpBit = 1 << --effectTrackDepth;
                activeEffect = this.parent;
                shouldTrack = lastShouldTrack;
                this.parent = undefined;
                if (this.deferStop) {
                    this.stop();
                }
            }
        }
        stop() {
            // stopped while running itself - defer the cleanup
            if (activeEffect === this) {
                this.deferStop = true;
            }
            else if (this.active) {
                cleanupEffect(this);
                if (this.onStop) {
                    this.onStop();
                }
                this.active = false;
            }
        }
    }
    function cleanupEffect(effect) {
        const { deps } = effect;
        if (deps.length) {
            for (let i = 0; i < deps.length; i++) {
                deps[i].delete(effect);
            }
            deps.length = 0;
        }
    }
    function effect(fn, options) {
        if (fn.effect) {
            fn = fn.effect.fn;
        }
        const _effect = new ReactiveEffect(fn);
        if (options) {
            extend(_effect, options);
            if (options.scope)
                recordEffectScope(_effect, options.scope);
        }
        if (!options || !options.lazy) {
            _effect.run();
        }
        const runner = _effect.run.bind(_effect);
        runner.effect = _effect;
        return runner;
    }
    function stop(runner) {
        runner.effect.stop();
    }
    let shouldTrack = true;
    const trackStack = [];
    function pauseTracking() {
        trackStack.push(shouldTrack);
        shouldTrack = false;
    }
    function resetTracking() {
        const last = trackStack.pop();
        shouldTrack = last === undefined ? true : last;
    }
    function track(target, type, key) {
        if (shouldTrack && activeEffect) {
            let depsMap = targetMap.get(target);
            if (!depsMap) {
                targetMap.set(target, (depsMap = new Map()));
            }
            let dep = depsMap.get(key);
            if (!dep) {
                depsMap.set(key, (dep = createDep()));
            }
            const eventInfo = { effect: activeEffect, target, type, key }
                ;
            trackEffects(dep, eventInfo);
        }
    }
    function trackEffects(dep, debuggerEventExtraInfo) {
        let shouldTrack = false;
        if (effectTrackDepth <= maxMarkerBits) {
            if (!newTracked(dep)) {
                dep.n |= trackOpBit; // set newly tracked
                shouldTrack = !wasTracked(dep);
            }
        }
        else {
            // Full cleanup mode.
            shouldTrack = !dep.has(activeEffect);
        }
        if (shouldTrack) {
            dep.add(activeEffect);
            activeEffect.deps.push(dep);
            if (activeEffect.onTrack) {
                activeEffect.onTrack(Object.assign({ effect: activeEffect }, debuggerEventExtraInfo));
            }
        }
    }
    function trigger(target, type, key, newValue, oldValue, oldTarget) {
        const depsMap = targetMap.get(target);
        if (!depsMap) {
            // never been tracked
            return;
        }
        let deps = [];
        if (type === "clear" /* CLEAR */) {
            // collection being cleared
            // trigger all effects for target
            deps = [...depsMap.values()];
        }
        else if (key === 'length' && isArray(target)) {
            depsMap.forEach((dep, key) => {
                if (key === 'length' || key >= newValue) {
                    deps.push(dep);
                }
            });
        }
        else {
            // schedule runs for SET | ADD | DELETE
            if (key !== void 0) {
                deps.push(depsMap.get(key));
            }
            // also run for iteration key on ADD | DELETE | Map.SET
            switch (type) {
                case "add" /* ADD */:
                    if (!isArray(target)) {
                        deps.push(depsMap.get(ITERATE_KEY));
                        if (isMap(target)) {
                            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
                        }
                    }
                    else if (isIntegerKey(key)) {
                        // new index added to array -> length changes
                        deps.push(depsMap.get('length'));
                    }
                    break;
                case "delete" /* DELETE */:
                    if (!isArray(target)) {
                        deps.push(depsMap.get(ITERATE_KEY));
                        if (isMap(target)) {
                            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
                        }
                    }
                    break;
                case "set" /* SET */:
                    if (isMap(target)) {
                        deps.push(depsMap.get(ITERATE_KEY));
                    }
                    break;
            }
        }
        const eventInfo = { target, type, key, newValue, oldValue, oldTarget }
            ;
        if (deps.length === 1) {
            if (deps[0]) {
                {
                    triggerEffects(deps[0], eventInfo);
                }
            }
        }
        else {
            const effects = [];
            for (const dep of deps) {
                if (dep) {
                    effects.push(...dep);
                }
            }
            {
                triggerEffects(createDep(effects), eventInfo);
            }
        }
    }
    function triggerEffects(dep, debuggerEventExtraInfo) {
        // spread into array for stabilization
        const effects = isArray(dep) ? dep : [...dep];
        for (const effect of effects) {
            if (effect.computed) {
                triggerEffect(effect, debuggerEventExtraInfo);
            }
        }
        for (const effect of effects) {
            if (!effect.computed) {
                triggerEffect(effect, debuggerEventExtraInfo);
            }
        }
    }
    function triggerEffect(effect, debuggerEventExtraInfo) {
        if (effect !== activeEffect || effect.allowRecurse) {
            if (effect.onTrigger) {
                effect.onTrigger(extend({ effect }, debuggerEventExtraInfo));
            }
            if (effect.scheduler) {
                effect.scheduler();
            }
            else {
                effect.run();
            }
        }
    }

    const isNonTrackableKeys = /*#__PURE__*/ makeMap(`__proto__,__v_isRef,__isVue`);
    const builtInSymbols = new Set(
    /*#__PURE__*/
    Object.getOwnPropertyNames(Symbol)
        // ios10.x Object.getOwnPropertyNames(Symbol) can enumerate 'arguments' and 'caller'
        // but accessing them on Symbol leads to TypeError because Symbol is a strict mode
        // function
        .filter(key => key !== 'arguments' && key !== 'caller')
        .map(key => Symbol[key])
        .filter(isSymbol));
    const get = /*#__PURE__*/ createGetter();
    const shallowGet = /*#__PURE__*/ createGetter(false, true);
    const readonlyGet = /*#__PURE__*/ createGetter(true);
    const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true);
    const arrayInstrumentations = /*#__PURE__*/ createArrayInstrumentations();
    function createArrayInstrumentations() {
        const instrumentations = {};
        ['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
            instrumentations[key] = function (...args) {
                const arr = toRaw(this);
                for (let i = 0, l = this.length; i < l; i++) {
                    track(arr, "get" /* GET */, i + '');
                }
                // we run the method using the original args first (which may be reactive)
                const res = arr[key](...args);
                if (res === -1 || res === false) {
                    // if that didn't work, run it again using raw values.
                    return arr[key](...args.map(toRaw));
                }
                else {
                    return res;
                }
            };
        });
        ['push', 'pop', 'shift', 'unshift', 'splice'].forEach(key => {
            instrumentations[key] = function (...args) {
                pauseTracking();
                const res = toRaw(this)[key].apply(this, args);
                resetTracking();
                return res;
            };
        });
        return instrumentations;
    }
    function createGetter(isReadonly = false, shallow = false) {
        return function get(target, key, receiver) {
            if (key === "__v_isReactive" /* IS_REACTIVE */) {
                return !isReadonly;
            }
            else if (key === "__v_isReadonly" /* IS_READONLY */) {
                return isReadonly;
            }
            else if (key === "__v_isShallow" /* IS_SHALLOW */) {
                return shallow;
            }
            else if (key === "__v_raw" /* RAW */ &&
                receiver ===
                    (isReadonly
                        ? shallow
                            ? shallowReadonlyMap
                            : readonlyMap
                        : shallow
                            ? shallowReactiveMap
                            : reactiveMap).get(target)) {
                return target;
            }
            const targetIsArray = isArray(target);
            if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
                return Reflect.get(arrayInstrumentations, key, receiver);
            }
            const res = Reflect.get(target, key, receiver);
            if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
                return res;
            }
            if (!isReadonly) {
                track(target, "get" /* GET */, key);
            }
            if (shallow) {
                return res;
            }
            if (isRef(res)) {
                // ref unwrapping - skip unwrap for Array + integer key.
                return targetIsArray && isIntegerKey(key) ? res : res.value;
            }
            if (isObject(res)) {
                // Convert returned value into a proxy as well. we do the isObject check
                // here to avoid invalid value warning. Also need to lazy access readonly
                // and reactive here to avoid circular dependency.
                return isReadonly ? readonly(res) : reactive(res);
            }
            return res;
        };
    }
    const set = /*#__PURE__*/ createSetter();
    const shallowSet = /*#__PURE__*/ createSetter(true);
    function createSetter(shallow = false) {
        return function set(target, key, value, receiver) {
            let oldValue = target[key];
            if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
                return false;
            }
            if (!shallow && !isReadonly(value)) {
                if (!isShallow(value)) {
                    value = toRaw(value);
                    oldValue = toRaw(oldValue);
                }
                if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
                    oldValue.value = value;
                    return true;
                }
            }
            const hadKey = isArray(target) && isIntegerKey(key)
                ? Number(key) < target.length
                : hasOwn(target, key);
            const result = Reflect.set(target, key, value, receiver);
            // don't trigger if target is something up in the prototype chain of original
            if (target === toRaw(receiver)) {
                if (!hadKey) {
                    trigger(target, "add" /* ADD */, key, value);
                }
                else if (hasChanged(value, oldValue)) {
                    trigger(target, "set" /* SET */, key, value, oldValue);
                }
            }
            return result;
        };
    }
    function deleteProperty(target, key) {
        const hadKey = hasOwn(target, key);
        const oldValue = target[key];
        const result = Reflect.deleteProperty(target, key);
        if (result && hadKey) {
            trigger(target, "delete" /* DELETE */, key, undefined, oldValue);
        }
        return result;
    }
    function has(target, key) {
        const result = Reflect.has(target, key);
        if (!isSymbol(key) || !builtInSymbols.has(key)) {
            track(target, "has" /* HAS */, key);
        }
        return result;
    }
    function ownKeys(target) {
        track(target, "iterate" /* ITERATE */, isArray(target) ? 'length' : ITERATE_KEY);
        return Reflect.ownKeys(target);
    }
    const mutableHandlers = {
        get,
        set,
        deleteProperty,
        has,
        ownKeys
    };
    const readonlyHandlers = {
        get: readonlyGet,
        set(target, key) {
            {
                warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target);
            }
            return true;
        },
        deleteProperty(target, key) {
            {
                warn(`Delete operation on key "${String(key)}" failed: target is readonly.`, target);
            }
            return true;
        }
    };
    const shallowReactiveHandlers = /*#__PURE__*/ extend({}, mutableHandlers, {
        get: shallowGet,
        set: shallowSet
    });
    // Props handlers are special in the sense that it should not unwrap top-level
    // refs (in order to allow refs to be explicitly passed down), but should
    // retain the reactivity of the normal readonly object.
    const shallowReadonlyHandlers = /*#__PURE__*/ extend({}, readonlyHandlers, {
        get: shallowReadonlyGet
    });

    const toShallow = (value) => value;
    const getProto = (v) => Reflect.getPrototypeOf(v);
    function get$1(target, key, isReadonly = false, isShallow = false) {
        // #1772: readonly(reactive(Map)) should return readonly + reactive version
        // of the value
        target = target["__v_raw" /* RAW */];
        const rawTarget = toRaw(target);
        const rawKey = toRaw(key);
        if (!isReadonly) {
            if (key !== rawKey) {
                track(rawTarget, "get" /* GET */, key);
            }
            track(rawTarget, "get" /* GET */, rawKey);
        }
        const { has } = getProto(rawTarget);
        const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive;
        if (has.call(rawTarget, key)) {
            return wrap(target.get(key));
        }
        else if (has.call(rawTarget, rawKey)) {
            return wrap(target.get(rawKey));
        }
        else if (target !== rawTarget) {
            // #3602 readonly(reactive(Map))
            // ensure that the nested reactive `Map` can do tracking for itself
            target.get(key);
        }
    }
    function has$1(key, isReadonly = false) {
        const target = this["__v_raw" /* RAW */];
        const rawTarget = toRaw(target);
        const rawKey = toRaw(key);
        if (!isReadonly) {
            if (key !== rawKey) {
                track(rawTarget, "has" /* HAS */, key);
            }
            track(rawTarget, "has" /* HAS */, rawKey);
        }
        return key === rawKey
            ? target.has(key)
            : target.has(key) || target.has(rawKey);
    }
    function size(target, isReadonly = false) {
        target = target["__v_raw" /* RAW */];
        !isReadonly && track(toRaw(target), "iterate" /* ITERATE */, ITERATE_KEY);
        return Reflect.get(target, 'size', target);
    }
    function add(value) {
        value = toRaw(value);
        const target = toRaw(this);
        const proto = getProto(target);
        const hadKey = proto.has.call(target, value);
        if (!hadKey) {
            target.add(value);
            trigger(target, "add" /* ADD */, value, value);
        }
        return this;
    }
    function set$1(key, value) {
        value = toRaw(value);
        const target = toRaw(this);
        const { has, get } = getProto(target);
        let hadKey = has.call(target, key);
        if (!hadKey) {
            key = toRaw(key);
            hadKey = has.call(target, key);
        }
        else {
            checkIdentityKeys(target, has, key);
        }
        const oldValue = get.call(target, key);
        target.set(key, value);
        if (!hadKey) {
            trigger(target, "add" /* ADD */, key, value);
        }
        else if (hasChanged(value, oldValue)) {
            trigger(target, "set" /* SET */, key, value, oldValue);
        }
        return this;
    }
    function deleteEntry(key) {
        const target = toRaw(this);
        const { has, get } = getProto(target);
        let hadKey = has.call(target, key);
        if (!hadKey) {
            key = toRaw(key);
            hadKey = has.call(target, key);
        }
        else {
            checkIdentityKeys(target, has, key);
        }
        const oldValue = get ? get.call(target, key) : undefined;
        // forward the operation before queueing reactions
        const result = target.delete(key);
        if (hadKey) {
            trigger(target, "delete" /* DELETE */, key, undefined, oldValue);
        }
        return result;
    }
    function clear() {
        const target = toRaw(this);
        const hadItems = target.size !== 0;
        const oldTarget = isMap(target)
                ? new Map(target)
                : new Set(target)
            ;
        // forward the operation before queueing reactions
        const result = target.clear();
        if (hadItems) {
            trigger(target, "clear" /* CLEAR */, undefined, undefined, oldTarget);
        }
        return result;
    }
    function createForEach(isReadonly, isShallow) {
        return function forEach(callback, thisArg) {
            const observed = this;
            const target = observed["__v_raw" /* RAW */];
            const rawTarget = toRaw(target);
            const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive;
            !isReadonly && track(rawTarget, "iterate" /* ITERATE */, ITERATE_KEY);
            return target.forEach((value, key) => {
                // important: make sure the callback is
                // 1. invoked with the reactive map as `this` and 3rd arg
                // 2. the value received should be a corresponding reactive/readonly.
                return callback.call(thisArg, wrap(value), wrap(key), observed);
            });
        };
    }
    function createIterableMethod(method, isReadonly, isShallow) {
        return function (...args) {
            const target = this["__v_raw" /* RAW */];
            const rawTarget = toRaw(target);
            const targetIsMap = isMap(rawTarget);
            const isPair = method === 'entries' || (method === Symbol.iterator && targetIsMap);
            const isKeyOnly = method === 'keys' && targetIsMap;
            const innerIterator = target[method](...args);
            const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive;
            !isReadonly &&
                track(rawTarget, "iterate" /* ITERATE */, isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY);
            // return a wrapped iterator which returns observed versions of the
            // values emitted from the real iterator
            return {
                // iterator protocol
                next() {
                    const { value, done } = innerIterator.next();
                    return done
                        ? { value, done }
                        : {
                            value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),
                            done
                        };
                },
                // iterable protocol
                [Symbol.iterator]() {
                    return this;
                }
            };
        };
    }
    function createReadonlyMethod(type) {
        return function (...args) {
            {
                const key = args[0] ? `on key "${args[0]}" ` : ``;
                console.warn(`${capitalize(type)} operation ${key}failed: target is readonly.`, toRaw(this));
            }
            return type === "delete" /* DELETE */ ? false : this;
        };
    }
    function createInstrumentations() {
        const mutableInstrumentations = {
            get(key) {
                return get$1(this, key);
            },
            get size() {
                return size(this);
            },
            has: has$1,
            add,
            set: set$1,
            delete: deleteEntry,
            clear,
            forEach: createForEach(false, false)
        };
        const shallowInstrumentations = {
            get(key) {
                return get$1(this, key, false, true);
            },
            get size() {
                return size(this);
            },
            has: has$1,
            add,
            set: set$1,
            delete: deleteEntry,
            clear,
            forEach: createForEach(false, true)
        };
        const readonlyInstrumentations = {
            get(key) {
                return get$1(this, key, true);
            },
            get size() {
                return size(this, true);
            },
            has(key) {
                return has$1.call(this, key, true);
            },
            add: createReadonlyMethod("add" /* ADD */),
            set: createReadonlyMethod("set" /* SET */),
            delete: createReadonlyMethod("delete" /* DELETE */),
            clear: createReadonlyMethod("clear" /* CLEAR */),
            forEach: createForEach(true, false)
        };
        const shallowReadonlyInstrumentations = {
            get(key) {
                return get$1(this, key, true, true);
            },
            get size() {
                return size(this, true);
            },
            has(key) {
                return has$1.call(this, key, true);
            },
            add: createReadonlyMethod("add" /* ADD */),
            set: createReadonlyMethod("set" /* SET */),
            delete: createReadonlyMethod("delete" /* DELETE */),
            clear: createReadonlyMethod("clear" /* CLEAR */),
            forEach: createForEach(true, true)
        };
        const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator];
        iteratorMethods.forEach(method => {
            mutableInstrumentations[method] = createIterableMethod(method, false, false);
            readonlyInstrumentations[method] = createIterableMethod(method, true, false);
            shallowInstrumentations[method] = createIterableMethod(method, false, true);
            shallowReadonlyInstrumentations[method] = createIterableMethod(method, true, true);
        });
        return [
            mutableInstrumentations,
            readonlyInstrumentations,
            shallowInstrumentations,
            shallowReadonlyInstrumentations
        ];
    }
    const [mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations] = /* #__PURE__*/ createInstrumentations();
    function createInstrumentationGetter(isReadonly, shallow) {
        const instrumentations = shallow
            ? isReadonly
                ? shallowReadonlyInstrumentations
                : shallowInstrumentations
            : isReadonly
                ? readonlyInstrumentations
                : mutableInstrumentations;
        return (target, key, receiver) => {
            if (key === "__v_isReactive" /* IS_REACTIVE */) {
                return !isReadonly;
            }
            else if (key === "__v_isReadonly" /* IS_READONLY */) {
                return isReadonly;
            }
            else if (key === "__v_raw" /* RAW */) {
                return target;
            }
            return Reflect.get(hasOwn(instrumentations, key) && key in target
                ? instrumentations
                : target, key, receiver);
        };
    }
    const mutableCollectionHandlers = {
        get: /*#__PURE__*/ createInstrumentationGetter(false, false)
    };
    const shallowCollectionHandlers = {
        get: /*#__PURE__*/ createInstrumentationGetter(false, true)
    };
    const readonlyCollectionHandlers = {
        get: /*#__PURE__*/ createInstrumentationGetter(true, false)
    };
    const shallowReadonlyCollectionHandlers = {
        get: /*#__PURE__*/ createInstrumentationGetter(true, true)
    };
    function checkIdentityKeys(target, has, key) {
        const rawKey = toRaw(key);
        if (rawKey !== key && has.call(target, rawKey)) {
            const type = toRawType(target);
            console.warn(`Reactive ${type} contains both the raw and reactive ` +
                `versions of the same object${type === `Map` ? ` as keys` : ``}, ` +
                `which can lead to inconsistencies. ` +
                `Avoid differentiating between the raw and reactive versions ` +
                `of an object and only use the reactive version if possible.`);
        }
    }

    const reactiveMap = new WeakMap();
    const shallowReactiveMap = new WeakMap();
    const readonlyMap = new WeakMap();
    const shallowReadonlyMap = new WeakMap();
    function targetTypeMap(rawType) {
        switch (rawType) {
            case 'Object':
            case 'Array':
                return 1 /* COMMON */;
            case 'Map':
            case 'Set':
            case 'WeakMap':
            case 'WeakSet':
                return 2 /* COLLECTION */;
            default:
                return 0 /* INVALID */;
        }
    }
    function getTargetType(value) {
        return value["__v_skip" /* SKIP */] || !Object.isExtensible(value)
            ? 0 /* INVALID */
            : targetTypeMap(toRawType(value));
    }
    function reactive(target) {
        // if trying to observe a readonly proxy, return the readonly version.
        if (isReadonly(target)) {
            return target;
        }
        return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
    }
    /**
     * Return a shallowly-reactive copy of the original object, where only the root
     * level properties are reactive. It also does not auto-unwrap refs (even at the
     * root level).
     */
    function shallowReactive(target) {
        return createReactiveObject(target, false, shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap);
    }
    /**
     * Creates a readonly copy of the original object. Note the returned copy is not
     * made reactive, but `readonly` can be called on an already reactive object.
     */
    function readonly(target) {
        return createReactiveObject(target, true, readonlyHandlers, readonlyCollectionHandlers, readonlyMap);
    }
    /**
     * Returns a reactive-copy of the original object, where only the root level
     * properties are readonly, and does NOT unwrap refs nor recursively convert
     * returned properties.
     * This is used for creating the props proxy object for stateful components.
     */
    function shallowReadonly(target) {
        return createReactiveObject(target, true, shallowReadonlyHandlers, shallowReadonlyCollectionHandlers, shallowReadonlyMap);
    }
    function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers, proxyMap) {
        if (!isObject(target)) {
            {
                console.warn(`value cannot be made reactive: ${String(target)}`);
            }
            return target;
        }
        // target is already a Proxy, return it.
        // exception: calling readonly() on a reactive object
        if (target["__v_raw" /* RAW */] &&
            !(isReadonly && target["__v_isReactive" /* IS_REACTIVE */])) {
            return target;
        }
        // target already has corresponding Proxy
        const existingProxy = proxyMap.get(target);
        if (existingProxy) {
            return existingProxy;
        }
        // only specific value types can be observed.
        const targetType = getTargetType(target);
        if (targetType === 0 /* INVALID */) {
            return target;
        }
        const proxy = new Proxy(target, targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers);
        proxyMap.set(target, proxy);
        return proxy;
    }
    function isReactive(value) {
        if (isReadonly(value)) {
            return isReactive(value["__v_raw" /* RAW */]);
        }
        return !!(value && value["__v_isReactive" /* IS_REACTIVE */]);
    }
    function isReadonly(value) {
        return !!(value && value["__v_isReadonly" /* IS_READONLY */]);
    }
    function isShallow(value) {
        return !!(value && value["__v_isShallow" /* IS_SHALLOW */]);
    }
    function isProxy(value) {
        return isReactive(value) || isReadonly(value);
    }
    function toRaw(observed) {
        const raw = observed && observed["__v_raw" /* RAW */];
        return raw ? toRaw(raw) : observed;
    }
    function markRaw(value) {
        def(value, "__v_skip" /* SKIP */, true);
        return value;
    }
    const toReactive = (value) => isObject(value) ? reactive(value) : value;
    const toReadonly = (value) => isObject(value) ? readonly(value) : value;

    function trackRefValue(ref) {
        if (shouldTrack && activeEffect) {
            ref = toRaw(ref);
            {
                trackEffects(ref.dep || (ref.dep = createDep()), {
                    target: ref,
                    type: "get" /* GET */,
                    key: 'value'
                });
            }
        }
    }
    function triggerRefValue(ref, newVal) {
        ref = toRaw(ref);
        if (ref.dep) {
            {
                triggerEffects(ref.dep, {
                    target: ref,
                    type: "set" /* SET */,
                    key: 'value',
                    newValue: newVal
                });
            }
        }
    }
    function isRef(r) {
        return !!(r && r.__v_isRef === true);
    }
    function ref(value) {
        return createRef(value, false);
    }
    function shallowRef(value) {
        return createRef(value, true);
    }
    function createRef(rawValue, shallow) {
        if (isRef(rawValue)) {
            return rawValue;
        }
        return new RefImpl(rawValue, shallow);
    }
    class RefImpl {
        constructor(value, __v_isShallow) {
            this.__v_isShallow = __v_isShallow;
            this.dep = undefined;
            this.__v_isRef = true;
            this._rawValue = __v_isShallow ? value : toRaw(value);
            this._value = __v_isShallow ? value : toReactive(value);
        }
        get value() {
            trackRefValue(this);
            return this._value;
        }
        set value(newVal) {
            newVal = this.__v_isShallow ? newVal : toRaw(newVal);
            if (hasChanged(newVal, this._rawValue)) {
                this._rawValue = newVal;
                this._value = this.__v_isShallow ? newVal : toReactive(newVal);
                triggerRefValue(this, newVal);
            }
        }
    }
    function triggerRef(ref) {
        triggerRefValue(ref, ref.value );
    }
    function unref(ref) {
        return isRef(ref) ? ref.value : ref;
    }
    const shallowUnwrapHandlers = {
        get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
        set: (target, key, value, receiver) => {
            const oldValue = target[key];
            if (isRef(oldValue) && !isRef(value)) {
                oldValue.value = value;
                return true;
            }
            else {
                return Reflect.set(target, key, value, receiver);
            }
        }
    };
    function proxyRefs(objectWithRefs) {
        return isReactive(objectWithRefs)
            ? objectWithRefs
            : new Proxy(objectWithRefs, shallowUnwrapHandlers);
    }
    class CustomRefImpl {
        constructor(factory) {
            this.dep = undefined;
            this.__v_isRef = true;
            const { get, set } = factory(() => trackRefValue(this), () => triggerRefValue(this));
            this._get = get;
            this._set = set;
        }
        get value() {
            return this._get();
        }
        set value(newVal) {
            this._set(newVal);
        }
    }
    function customRef(factory) {
        return new CustomRefImpl(factory);
    }
    function toRefs(object) {
        if (!isProxy(object)) {
            console.warn(`toRefs() expects a reactive object but received a plain one.`);
        }
        const ret = isArray(object) ? new Array(object.length) : {};
        for (const key in object) {
            ret[key] = toRef(object, key);
        }
        return ret;
    }
    class ObjectRefImpl {
        constructor(_object, _key, _defaultValue) {
            this._object = _object;
            this._key = _key;
            this._defaultValue = _defaultValue;
            this.__v_isRef = true;
        }
        get value() {
            const val = this._object[this._key];
            return val === undefined ? this._defaultValue : val;
        }
        set value(newVal) {
            this._object[this._key] = newVal;
        }
    }
    function toRef(object, key, defaultValue) {
        const val = object[key];
        return isRef(val)
            ? val
            : new ObjectRefImpl(object, key, defaultValue);
    }

    class ComputedRefImpl {
        constructor(getter, _setter, isReadonly, isSSR) {
            this._setter = _setter;
            this.dep = undefined;
            this.__v_isRef = true;
            this._dirty = true;
            this.effect = new ReactiveEffect(getter, () => {
                if (!this._dirty) {
                    this._dirty = true;
                    triggerRefValue(this);
                }
            });
            this.effect.computed = this;
            this.effect.active = this._cacheable = !isSSR;
            this["__v_isReadonly" /* IS_READONLY */] = isReadonly;
        }
        get value() {
            // the computed ref may get wrapped by other proxies e.g. readonly() #3376
            const self = toRaw(this);
            trackRefValue(self);
            if (self._dirty || !self._cacheable) {
                self._dirty = false;
                self._value = self.effect.run();
            }
            return self._value;
        }
        set value(newValue) {
            this._setter(newValue);
        }
    }
    function computed(getterOrOptions, debugOptions, isSSR = false) {
        let getter;
        let setter;
        const onlyGetter = isFunction(getterOrOptions);
        if (onlyGetter) {
            getter = getterOrOptions;
            setter = () => {
                    console.warn('Write operation failed: computed value is readonly');
                }
                ;
        }
        else {
            getter = getterOrOptions.get;
            setter = getterOrOptions.set;
        }
        const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
        if (debugOptions && !isSSR) {
            cRef.effect.onTrack = debugOptions.onTrack;
            cRef.effect.onTrigger = debugOptions.onTrigger;
        }
        return cRef;
    }

    const stack = [];
    function pushWarningContext(vnode) {
        stack.push(vnode);
    }
    function popWarningContext() {
        stack.pop();
    }
    function warn$1(msg, ...args) {
        // avoid props formatting or warn handler tracking deps that might be mutated
        // during patch, leading to infinite recursion.
        pauseTracking();
        const instance = stack.length ? stack[stack.length - 1].component : null;
        const appWarnHandler = instance && instance.appContext.config.warnHandler;
        const trace = getComponentTrace();
        if (appWarnHandler) {
            callWithErrorHandling(appWarnHandler, instance, 11 /* APP_WARN_HANDLER */, [
                msg + args.join(''),
                instance && instance.proxy,
                trace
                    .map(({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>`)
                    .join('\n'),
                trace
            ]);
        }
        else {
            const warnArgs = [`[Vue warn]: ${msg}`, ...args];
            /* istanbul ignore if */
            if (trace.length &&
                // avoid spamming console during tests
                !false) {
                warnArgs.push(`\n`, ...formatTrace(trace));
            }
            console.warn(...warnArgs);
        }
        resetTracking();
    }
    function getComponentTrace() {
        let currentVNode = stack[stack.length - 1];
        if (!currentVNode) {
            return [];
        }
        // we can't just use the stack because it will be incomplete during updates
        // that did not start from the root. Re-construct the parent chain using
        // instance parent pointers.
        const normalizedStack = [];
        while (currentVNode) {
            const last = normalizedStack[0];
            if (last && last.vnode === currentVNode) {
                last.recurseCount++;
            }
            else {
                normalizedStack.push({
                    vnode: currentVNode,
                    recurseCount: 0
                });
            }
            const parentInstance = currentVNode.component && currentVNode.component.parent;
            currentVNode = parentInstance && parentInstance.vnode;
        }
        return normalizedStack;
    }
    /* istanbul ignore next */
    function formatTrace(trace) {
        const logs = [];
        trace.forEach((entry, i) => {
            logs.push(...(i === 0 ? [] : [`\n`]), ...formatTraceEntry(entry));
        });
        return logs;
    }
    function formatTraceEntry({ vnode, recurseCount }) {
        const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``;
        const isRoot = vnode.component ? vnode.component.parent == null : false;
        const open = ` at <${formatComponentName(vnode.component, vnode.type, isRoot)}`;
        const close = `>` + postfix;
        return vnode.props
            ? [open, ...formatProps(vnode.props), close]
            : [open + close];
    }
    /* istanbul ignore next */
    function formatProps(props) {
        const res = [];
        const keys = Object.keys(props);
        keys.slice(0, 3).forEach(key => {
            res.push(...formatProp(key, props[key]));
        });
        if (keys.length > 3) {
            res.push(` ...`);
        }
        return res;
    }
    /* istanbul ignore next */
    function formatProp(key, value, raw) {
        if (isString(value)) {
            value = JSON.stringify(value);
            return raw ? value : [`${key}=${value}`];
        }
        else if (typeof value === 'number' ||
            typeof value === 'boolean' ||
            value == null) {
            return raw ? value : [`${key}=${value}`];
        }
        else if (isRef(value)) {
            value = formatProp(key, toRaw(value.value), true);
            return raw ? value : [`${key}=Ref<`, value, `>`];
        }
        else if (isFunction(value)) {
            return [`${key}=fn${value.name ? `<${value.name}>` : ``}`];
        }
        else {
            value = toRaw(value);
            return raw ? value : [`${key}=`, value];
        }
    }

    const ErrorTypeStrings = {
        ["sp" /* SERVER_PREFETCH */]: 'serverPrefetch hook',
        ["bc" /* BEFORE_CREATE */]: 'beforeCreate hook',
        ["c" /* CREATED */]: 'created hook',
        ["bm" /* BEFORE_MOUNT */]: 'beforeMount hook',
        ["m" /* MOUNTED */]: 'mounted hook',
        ["bu" /* BEFORE_UPDATE */]: 'beforeUpdate hook',
        ["u" /* UPDATED */]: 'updated',
        ["bum" /* BEFORE_UNMOUNT */]: 'beforeUnmount hook',
        ["um" /* UNMOUNTED */]: 'unmounted hook',
        ["a" /* ACTIVATED */]: 'activated hook',
        ["da" /* DEACTIVATED */]: 'deactivated hook',
        ["ec" /* ERROR_CAPTURED */]: 'errorCaptured hook',
        ["rtc" /* RENDER_TRACKED */]: 'renderTracked hook',
        ["rtg" /* RENDER_TRIGGERED */]: 'renderTriggered hook',
        [0 /* SETUP_FUNCTION */]: 'setup function',
        [1 /* RENDER_FUNCTION */]: 'render function',
        [2 /* WATCH_GETTER */]: 'watcher getter',
        [3 /* WATCH_CALLBACK */]: 'watcher callback',
        [4 /* WATCH_CLEANUP */]: 'watcher cleanup function',
        [5 /* NATIVE_EVENT_HANDLER */]: 'native event handler',
        [6 /* COMPONENT_EVENT_HANDLER */]: 'component event handler',
        [7 /* VNODE_HOOK */]: 'vnode hook',
        [8 /* DIRECTIVE_HOOK */]: 'directive hook',
        [9 /* TRANSITION_HOOK */]: 'transition hook',
        [10 /* APP_ERROR_HANDLER */]: 'app errorHandler',
        [11 /* APP_WARN_HANDLER */]: 'app warnHandler',
        [12 /* FUNCTION_REF */]: 'ref function',
        [13 /* ASYNC_COMPONENT_LOADER */]: 'async component loader',
        [14 /* SCHEDULER */]: 'scheduler flush. This is likely a Vue internals bug. ' +
            'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/core'
    };
    function callWithErrorHandling(fn, instance, type, args) {
        let res;
        try {
            res = args ? fn(...args) : fn();
        }
        catch (err) {
            handleError(err, instance, type);
        }
        return res;
    }
    function callWithAsyncErrorHandling(fn, instance, type, args) {
        if (isFunction(fn)) {
            const res = callWithErrorHandling(fn, instance, type, args);
            if (res && isPromise(res)) {
                res.catch(err => {
                    handleError(err, instance, type);
                });
            }
            return res;
        }
        const values = [];
        for (let i = 0; i < fn.length; i++) {
            values.push(callWithAsyncErrorHandling(fn[i], instance, type, args));
        }
        return values;
    }
    function handleError(err, instance, type, throwInDev = true) {
        const contextVNode = instance ? instance.vnode : null;
        if (instance) {
            let cur = instance.parent;
            // the exposed instance is the render proxy to keep it consistent with 2.x
            const exposedInstance = instance.proxy;
            // in production the hook receives only the error code
            const errorInfo = ErrorTypeStrings[type] ;
            while (cur) {
                const errorCapturedHooks = cur.ec;
                if (errorCapturedHooks) {
                    for (let i = 0; i < errorCapturedHooks.length; i++) {
                        if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) {
                            return;
                        }
                    }
                }
                cur = cur.parent;
            }
            // app-level handling
            const appErrorHandler = instance.appContext.config.errorHandler;
            if (appErrorHandler) {
                callWithErrorHandling(appErrorHandler, null, 10 /* APP_ERROR_HANDLER */, [err, exposedInstance, errorInfo]);
                return;
            }
        }
        logError(err, type, contextVNode, throwInDev);
    }
    function logError(err, type, contextVNode, throwInDev = true) {
        {
            const info = ErrorTypeStrings[type];
            if (contextVNode) {
                pushWarningContext(contextVNode);
            }
            warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`);
            if (contextVNode) {
                popWarningContext();
            }
            // crash in dev by default so it's more noticeable
            if (throwInDev) {
                throw err;
            }
            else {
                console.error(err);
            }
        }
    }

    let isFlushing = false;
    let isFlushPending = false;
    const queue = [];
    let flushIndex = 0;
    const pendingPreFlushCbs = [];
    let activePreFlushCbs = null;
    let preFlushIndex = 0;
    const pendingPostFlushCbs = [];
    let activePostFlushCbs = null;
    let postFlushIndex = 0;
    const resolvedPromise = /*#__PURE__*/ Promise.resolve();
    let currentFlushPromise = null;
    let currentPreFlushParentJob = null;
    const RECURSION_LIMIT = 100;
    function nextTick(fn) {
        const p = currentFlushPromise || resolvedPromise;
        return fn ? p.then(this ? fn.bind(this) : fn) : p;
    }
    // #2768
    // Use binary-search to find a suitable position in the queue,
    // so that the queue maintains the increasing order of job's id,
    // which can prevent the job from being skipped and also can avoid repeated patching.
    function findInsertionIndex(id) {
        // the start index should be `flushIndex + 1`
        let start = flushIndex + 1;
        let end = queue.length;
        while (start < end) {
            const middle = (start + end) >>> 1;
            const middleJobId = getId(queue[middle]);
            middleJobId < id ? (start = middle + 1) : (end = middle);
        }
        return start;
    }
    function queueJob(job) {
        // the dedupe search uses the startIndex argument of Array.includes()
        // by default the search index includes the current job that is being run
        // so it cannot recursively trigger itself again.
        // if the job is a watch() callback, the search will start with a +1 index to
        // allow it recursively trigger itself - it is the user's responsibility to
        // ensure it doesn't end up in an infinite loop.
        if ((!queue.length ||
            !queue.includes(job, isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex)) &&
            job !== currentPreFlushParentJob) {
            if (job.id == null) {
                queue.push(job);
            }
            else {
                queue.splice(findInsertionIndex(job.id), 0, job);
            }
            queueFlush();
        }
    }
    function queueFlush() {
        if (!isFlushing && !isFlushPending) {
            isFlushPending = true;
            currentFlushPromise = resolvedPromise.then(flushJobs);
        }
    }
    function invalidateJob(job) {
        const i = queue.indexOf(job);
        if (i > flushIndex) {
            queue.splice(i, 1);
        }
    }
    function queueCb(cb, activeQueue, pendingQueue, index) {
        if (!isArray(cb)) {
            if (!activeQueue ||
                !activeQueue.includes(cb, cb.allowRecurse ? index + 1 : index)) {
                pendingQueue.push(cb);
            }
        }
        else {
            // if cb is an array, it is a component lifecycle hook which can only be
            // triggered by a job, which is already deduped in the main queue, so
            // we can skip duplicate check here to improve perf
            pendingQueue.push(...cb);
        }
        queueFlush();
    }
    function queuePreFlushCb(cb) {
        queueCb(cb, activePreFlushCbs, pendingPreFlushCbs, preFlushIndex);
    }
    function queuePostFlushCb(cb) {
        queueCb(cb, activePostFlushCbs, pendingPostFlushCbs, postFlushIndex);
    }
    function flushPreFlushCbs(seen, parentJob = null) {
        if (pendingPreFlushCbs.length) {
            currentPreFlushParentJob = parentJob;
            activePreFlushCbs = [...new Set(pendingPreFlushCbs)];
            pendingPreFlushCbs.length = 0;
            {
                seen = seen || new Map();
            }
            for (preFlushIndex = 0; preFlushIndex < activePreFlushCbs.length; preFlushIndex++) {
                if (checkRecursiveUpdates(seen, activePreFlushCbs[preFlushIndex])) {
                    continue;
                }
                activePreFlushCbs[preFlushIndex]();
            }
            activePreFlushCbs = null;
            preFlushIndex = 0;
            currentPreFlushParentJob = null;
            // recursively flush until it drains
            flushPreFlushCbs(seen, parentJob);
        }
    }
    function flushPostFlushCbs(seen) {
        // flush any pre cbs queued during the flush (e.g. pre watchers)
        flushPreFlushCbs();
        if (pendingPostFlushCbs.length) {
            const deduped = [...new Set(pendingPostFlushCbs)];
            pendingPostFlushCbs.length = 0;
            // #1947 already has active queue, nested flushPostFlushCbs call
            if (activePostFlushCbs) {
                activePostFlushCbs.push(...deduped);
                return;
            }
            activePostFlushCbs = deduped;
            {
                seen = seen || new Map();
            }
            activePostFlushCbs.sort((a, b) => getId(a) - getId(b));
            for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) {
                if (checkRecursiveUpdates(seen, activePostFlushCbs[postFlushIndex])) {
                    continue;
                }
                activePostFlushCbs[postFlushIndex]();
            }
            activePostFlushCbs = null;
            postFlushIndex = 0;
        }
    }
    const getId = (job) => job.id == null ? Infinity : job.id;
    function flushJobs(seen) {
        isFlushPending = false;
        isFlushing = true;
        {
            seen = seen || new Map();
        }
        flushPreFlushCbs(seen);
        // Sort queue before flush.
        // This ensures that:
        // 1. Components are updated from parent to child. (because parent is always
        //    created before the child so its render effect will have smaller
        //    priority number)
        // 2. If a component is unmounted during a parent component's update,
        //    its update can be skipped.
        queue.sort((a, b) => getId(a) - getId(b));
        // conditional usage of checkRecursiveUpdate must be determined out of
        // try ... catch block since Rollup by default de-optimizes treeshaking
        // inside try-catch. This can leave all warning code unshaked. Although
        // they would get eventually shaken by a minifier like terser, some minifiers
        // would fail to do that (e.g. https://github.com/evanw/esbuild/issues/1610)
        const check = (job) => checkRecursiveUpdates(seen, job)
            ;
        try {
            for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
                const job = queue[flushIndex];
                if (job && job.active !== false) {
                    if (true && check(job)) {
                        continue;
                    }
                    // console.log(`running:`, job.id)
                    callWithErrorHandling(job, null, 14 /* SCHEDULER */);
                }
            }
        }
        finally {
            flushIndex = 0;
            queue.length = 0;
            flushPostFlushCbs(seen);
            isFlushing = false;
            currentFlushPromise = null;
            // some postFlushCb queued jobs!
            // keep flushing until it drains.
            if (queue.length ||
                pendingPreFlushCbs.length ||
                pendingPostFlushCbs.length) {
                flushJobs(seen);
            }
        }
    }
    function checkRecursiveUpdates(seen, fn) {
        if (!seen.has(fn)) {
            seen.set(fn, 1);
        }
        else {
            const count = seen.get(fn);
            if (count > RECURSION_LIMIT) {
                const instance = fn.ownerInstance;
                const componentName = instance && getComponentName(instance.type);
                warn$1(`Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. ` +
                    `This means you have a reactive effect that is mutating its own ` +
                    `dependencies and thus recursively triggering itself. Possible sources ` +
                    `include component template, render function, updated hook or ` +
                    `watcher source function.`);
                return true;
            }
            else {
                seen.set(fn, count + 1);
            }
        }
    }

    /* eslint-disable no-restricted-globals */
    let isHmrUpdating = false;
    const hmrDirtyComponents = new Set();
    // Expose the HMR runtime on the global object
    // This makes it entirely tree-shakable without polluting the exports and makes
    // it easier to be used in toolings like vue-loader
    // Note: for a component to be eligible for HMR it also needs the __hmrId option
    // to be set so that its instances can be registered / removed.
    {
        getGlobalThis().__VUE_HMR_RUNTIME__ = {
            createRecord: tryWrap(createRecord),
            rerender: tryWrap(rerender),
            reload: tryWrap(reload)
        };
    }
    const map = new Map();
    function registerHMR(instance) {
        const id = instance.type.__hmrId;
        let record = map.get(id);
        if (!record) {
            createRecord(id, instance.type);
            record = map.get(id);
        }
        record.instances.add(instance);
    }
    function unregisterHMR(instance) {
        map.get(instance.type.__hmrId).instances.delete(instance);
    }
    function createRecord(id, initialDef) {
        if (map.has(id)) {
            return false;
        }
        map.set(id, {
            initialDef: normalizeClassComponent(initialDef),
            instances: new Set()
        });
        return true;
    }
    function normalizeClassComponent(component) {
        return isClassComponent(component) ? component.__vccOpts : component;
    }
    function rerender(id, newRender) {
        const record = map.get(id);
        if (!record) {
            return;
        }
        // update initial record (for not-yet-rendered component)
        record.initialDef.render = newRender;
        [...record.instances].forEach(instance => {
            if (newRender) {
                instance.render = newRender;
                normalizeClassComponent(instance.type).render = newRender;
            }
            instance.renderCache = [];
            // this flag forces child components with slot content to update
            isHmrUpdating = true;
            instance.update();
            isHmrUpdating = false;
        });
    }
    function reload(id, newComp) {
        const record = map.get(id);
        if (!record)
            return;
        newComp = normalizeClassComponent(newComp);
        // update initial def (for not-yet-rendered components)
        updateComponentDef(record.initialDef, newComp);
        // create a snapshot which avoids the set being mutated during updates
        const instances = [...record.instances];
        for (const instance of instances) {
            const oldComp = normalizeClassComponent(instance.type);
            if (!hmrDirtyComponents.has(oldComp)) {
                // 1. Update existing comp definition to match new one
                if (oldComp !== record.initialDef) {
                    updateComponentDef(oldComp, newComp);
                }
                // 2. mark definition dirty. This forces the renderer to replace the
                // component on patch.
                hmrDirtyComponents.add(oldComp);
            }
            // 3. invalidate options resolution cache
            instance.appContext.optionsCache.delete(instance.type);
            // 4. actually update
            if (instance.ceReload) {
                // custom element
                hmrDirtyComponents.add(oldComp);
                instance.ceReload(newComp.styles);
                hmrDirtyComponents.delete(oldComp);
            }
            else if (instance.parent) {
                // 4. Force the parent instance to re-render. This will cause all updated
                // components to be unmounted and re-mounted. Queue the update so that we
                // don't end up forcing the same parent to re-render multiple times.
                queueJob(instance.parent.update);
                // instance is the inner component of an async custom element
                // invoke to reset styles
                if (instance.parent.type.__asyncLoader &&
                    instance.parent.ceReload) {
                    instance.parent.ceReload(newComp.styles);
                }
            }
            else if (instance.appContext.reload) {
                // root instance mounted via createApp() has a reload method
                instance.appContext.reload();
            }
            else if (typeof window !== 'undefined') {
                // root instance inside tree created via raw render(). Force reload.
                window.location.reload();
            }
            else {
                console.warn('[HMR] Root or manually mounted instance modified. Full reload required.');
            }
        }
        // 5. make sure to cleanup dirty hmr components after update
        queuePostFlushCb(() => {
            for (const instance of instances) {
                hmrDirtyComponents.delete(normalizeClassComponent(instance.type));
            }
        });
    }
    function updateComponentDef(oldComp, newComp) {
        extend(oldComp, newComp);
        for (const key in oldComp) {
            if (key !== '__file' && !(key in newComp)) {
                delete oldComp[key];
            }
        }
    }
    function tryWrap(fn) {
        return (id, arg) => {
            try {
                return fn(id, arg);
            }
            catch (e) {
                console.error(e);
                console.warn(`[HMR] Something went wrong during Vue component hot-reload. ` +
                    `Full reload required.`);
            }
        };
    }

    let devtools;
    let buffer = [];
    let devtoolsNotInstalled = false;
    function emit(event, ...args) {
        if (devtools) {
            devtools.emit(event, ...args);
        }
        else if (!devtoolsNotInstalled) {
            buffer.push({ event, args });
        }
    }
    function setDevtoolsHook(hook, target) {
        var _a, _b;
        devtools = hook;
        if (devtools) {
            devtools.enabled = true;
            buffer.forEach(({ event, args }) => devtools.emit(event, ...args));
            buffer = [];
        }
        else if (
        // handle late devtools injection - only do this if we are in an actual
        // browser environment to avoid the timer handle stalling test runner exit
        // (#4815)
        typeof window !== 'undefined' &&
            // some envs mock window but not fully
            window.HTMLElement &&
            // also exclude jsdom
            !((_b = (_a = window.navigator) === null || _a === void 0 ? void 0 : _a.userAgent) === null || _b === void 0 ? void 0 : _b.includes('jsdom'))) {
            const replay = (target.__VUE_DEVTOOLS_HOOK_REPLAY__ =
                target.__VUE_DEVTOOLS_HOOK_REPLAY__ || []);
            replay.push((newHook) => {
                setDevtoolsHook(newHook, target);
            });
            // clear buffer after 3s - the user probably doesn't have devtools installed
            // at all, and keeping the buffer will cause memory leaks (#4738)
            setTimeout(() => {
                if (!devtools) {
                    target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null;
                    devtoolsNotInstalled = true;
                    buffer = [];
                }
            }, 3000);
        }
        else {
            // non-browser env, assume not installed
            devtoolsNotInstalled = true;
            buffer = [];
        }
    }
    function devtoolsInitApp(app, version) {
        emit("app:init" /* APP_INIT */, app, version, {
            Fragment,
            Text,
            Comment,
            Static
        });
    }
    function devtoolsUnmountApp(app) {
        emit("app:unmount" /* APP_UNMOUNT */, app);
    }
    const devtoolsComponentAdded = /*#__PURE__*/ createDevtoolsComponentHook("component:added" /* COMPONENT_ADDED */);
    const devtoolsComponentUpdated = 
    /*#__PURE__*/ createDevtoolsComponentHook("component:updated" /* COMPONENT_UPDATED */);
    const devtoolsComponentRemoved = 
    /*#__PURE__*/ createDevtoolsComponentHook("component:removed" /* COMPONENT_REMOVED */);
    function createDevtoolsComponentHook(hook) {
        return (component) => {
            emit(hook, component.appContext.app, component.uid, component.parent ? component.parent.uid : undefined, component);
        };
    }
    const devtoolsPerfStart = /*#__PURE__*/ createDevtoolsPerformanceHook("perf:start" /* PERFORMANCE_START */);
    const devtoolsPerfEnd = /*#__PURE__*/ createDevtoolsPerformanceHook("perf:end" /* PERFORMANCE_END */);
    function createDevtoolsPerformanceHook(hook) {
        return (component, type, time) => {
            emit(hook, component.appContext.app, component.uid, component, type, time);
        };
    }
    function devtoolsComponentEmit(component, event, params) {
        emit("component:emit" /* COMPONENT_EMIT */, component.appContext.app, component, event, params);
    }

    function emit$1(instance, event, ...rawArgs) {
        if (instance.isUnmounted)
            return;
        const props = instance.vnode.props || EMPTY_OBJ;
        {
            const { emitsOptions, propsOptions: [propsOptions] } = instance;
            if (emitsOptions) {
                if (!(event in emitsOptions) &&
                    !(false )) {
                    if (!propsOptions || !(toHandlerKey(event) in propsOptions)) {
                        warn$1(`Component emitted event "${event}" but it is neither declared in ` +
                            `the emits option nor as an "${toHandlerKey(event)}" prop.`);
                    }
                }
                else {
                    const validator = emitsOptions[event];
                    if (isFunction(validator)) {
                        const isValid = validator(...rawArgs);
                        if (!isValid) {
                            warn$1(`Invalid event arguments: event validation failed for event "${event}".`);
                        }
                    }
                }
            }
        }
        let args = rawArgs;
        const isModelListener = event.startsWith('update:');
        // for v-model update:xxx events, apply modifiers on args
        const modelArg = isModelListener && event.slice(7);
        if (modelArg && modelArg in props) {
            const modifiersKey = `${modelArg === 'modelValue' ? 'model' : modelArg}Modifiers`;
            const { number, trim } = props[modifiersKey] || EMPTY_OBJ;
            if (trim) {
                args = rawArgs.map(a => a.trim());
            }
            if (number) {
                args = rawArgs.map(toNumber);
            }
        }
        {
            devtoolsComponentEmit(instance, event, args);
        }
        {
            const lowerCaseEvent = event.toLowerCase();
            if (lowerCaseEvent !== event && props[toHandlerKey(lowerCaseEvent)]) {
                warn$1(`Event "${lowerCaseEvent}" is emitted in component ` +
                    `${formatComponentName(instance, instance.type)} but the handler is registered for "${event}". ` +
                    `Note that HTML attributes are case-insensitive and you cannot use ` +
                    `v-on to listen to camelCase events when using in-DOM templates. ` +
                    `You should probably use "${hyphenate(event)}" instead of "${event}".`);
            }
        }
        let handlerName;
        let handler = props[(handlerName = toHandlerKey(event))] ||
            // also try camelCase event handler (#2249)
            props[(handlerName = toHandlerKey(camelize(event)))];
        // for v-model update:xxx events, also trigger kebab-case equivalent
        // for props passed via kebab-case
        if (!handler && isModelListener) {
            handler = props[(handlerName = toHandlerKey(hyphenate(event)))];
        }
        if (handler) {
            callWithAsyncErrorHandling(handler, instance, 6 /* COMPONENT_EVENT_HANDLER */, args);
        }
        const onceHandler = props[handlerName + `Once`];
        if (onceHandler) {
            if (!instance.emitted) {
                instance.emitted = {};
            }
            else if (instance.emitted[handlerName]) {
                return;
            }
            instance.emitted[handlerName] = true;
            callWithAsyncErrorHandling(onceHandler, instance, 6 /* COMPONENT_EVENT_HANDLER */, args);
        }
    }
    function normalizeEmitsOptions(comp, appContext, asMixin = false) {
        const cache = appContext.emitsCache;
        const cached = cache.get(comp);
        if (cached !== undefined) {
            return cached;
        }
        const raw = comp.emits;
        let normalized = {};
        // apply mixin/extends props
        let hasExtends = false;
        if (!isFunction(comp)) {
            const extendEmits = (raw) => {
                const normalizedFromExtend = normalizeEmitsOptions(raw, appContext, true);
                if (normalizedFromExtend) {
                    hasExtends = true;
                    extend(normalized, normalizedFromExtend);
                }
            };
            if (!asMixin && appContext.mixins.length) {
                appContext.mixins.forEach(extendEmits);
            }
            if (comp.extends) {
                extendEmits(comp.extends);
            }
            if (comp.mixins) {
                comp.mixins.forEach(extendEmits);
            }
        }
        if (!raw && !hasExtends) {
            cache.set(comp, null);
            return null;
        }
        if (isArray(raw)) {
            raw.forEach(key => (normalized[key] = null));
        }
        else {
            extend(normalized, raw);
        }
        cache.set(comp, normalized);
        return normalized;
    }
    // Check if an incoming prop key is a declared emit event listener.
    // e.g. With `emits: { click: null }`, props named `onClick` and `onclick` are
    // both considered matched listeners.
    function isEmitListener(options, key) {
        if (!options || !isOn(key)) {
            return false;
        }
        key = key.slice(2).replace(/Once$/, '');
        return (hasOwn(options, key[0].toLowerCase() + key.slice(1)) ||
            hasOwn(options, hyphenate(key)) ||
            hasOwn(options, key));
    }

    /**
     * mark the current rendering instance for asset resolution (e.g.
     * resolveComponent, resolveDirective) during render
     */
    let currentRenderingInstance = null;
    let currentScopeId = null;
    /**
     * Note: rendering calls maybe nested. The function returns the parent rendering
     * instance if present, which should be restored after the render is done:
     *
     * ```js
     * const prev = setCurrentRenderingInstance(i)
     * // ...render
     * setCurrentRenderingInstance(prev)
     * ```
     */
    function setCurrentRenderingInstance(instance) {
        const prev = currentRenderingInstance;
        currentRenderingInstance = instance;
        currentScopeId = (instance && instance.type.__scopeId) || null;
        return prev;
    }
    /**
     * Set scope id when creating hoisted vnodes.
     * @private compiler helper
     */
    function pushScopeId(id) {
        currentScopeId = id;
    }
    /**
     * Technically we no longer need this after 3.0.8 but we need to keep the same
     * API for backwards compat w/ code generated by compilers.
     * @private
     */
    function popScopeId() {
        currentScopeId = null;
    }
    /**
     * Only for backwards compat
     * @private
     */
    const withScopeId = (_id) => withCtx;
    /**
     * Wrap a slot function to memoize current rendering instance
     * @private compiler helper
     */
    function withCtx(fn, ctx = currentRenderingInstance, isNonScopedSlot // false only
    ) {
        if (!ctx)
            return fn;
        // already normalized
        if (fn._n) {
            return fn;
        }
        const renderFnWithContext = (...args) => {
            // If a user calls a compiled slot inside a template expression (#1745), it
            // can mess up block tracking, so by default we disable block tracking and
            // force bail out when invoking a compiled slot (indicated by the ._d flag).
            // This isn't necessary if rendering a compiled `<slot>`, so we flip the
            // ._d flag off when invoking the wrapped fn inside `renderSlot`.
            if (renderFnWithContext._d) {
                setBlockTracking(-1);
            }
            const prevInstance = setCurrentRenderingInstance(ctx);
            const res = fn(...args);
            setCurrentRenderingInstance(prevInstance);
            if (renderFnWithContext._d) {
                setBlockTracking(1);
            }
            {
                devtoolsComponentUpdated(ctx);
            }
            return res;
        };
        // mark normalized to avoid duplicated wrapping
        renderFnWithContext._n = true;
        // mark this as compiled by default
        // this is used in vnode.ts -> normalizeChildren() to set the slot
        // rendering flag.
        renderFnWithContext._c = true;
        // disable block tracking by default
        renderFnWithContext._d = true;
        return renderFnWithContext;
    }

    /**
     * dev only flag to track whether $attrs was used during render.
     * If $attrs was used during render then the warning for failed attrs
     * fallthrough can be suppressed.
     */
    let accessedAttrs = false;
    function markAttrsAccessed() {
        accessedAttrs = true;
    }
    function renderComponentRoot(instance) {
        const { type: Component, vnode, proxy, withProxy, props, propsOptions: [propsOptions], slots, attrs, emit, render, renderCache, data, setupState, ctx, inheritAttrs } = instance;
        let result;
        let fallthroughAttrs;
        const prev = setCurrentRenderingInstance(instance);
        {
            accessedAttrs = false;
        }
        try {
            if (vnode.shapeFlag & 4 /* STATEFUL_COMPONENT */) {
                // withProxy is a proxy with a different `has` trap only for
                // runtime-compiled render functions using `with` block.
                const proxyToUse = withProxy || proxy;
                result = normalizeVNode(render.call(proxyToUse, proxyToUse, renderCache, props, setupState, data, ctx));
                fallthroughAttrs = attrs;
            }
            else {
                // functional
                const render = Component;
                // in dev, mark attrs accessed if optional props (attrs === props)
                if (true && attrs === props) {
                    markAttrsAccessed();
                }
                result = normalizeVNode(render.length > 1
                    ? render(props, true
                        ? {
                            get attrs() {
                                markAttrsAccessed();
                                return attrs;
                            },
                            slots,
                            emit
                        }
                        : { attrs, slots, emit })
                    : render(props, null /* we know it doesn't need it */));
                fallthroughAttrs = Component.props
                    ? attrs
                    : getFunctionalFallthrough(attrs);
            }
        }
        catch (err) {
            blockStack.length = 0;
            handleError(err, instance, 1 /* RENDER_FUNCTION */);
            result = createVNode(Comment);
        }
        // attr merging
        // in dev mode, comments are preserved, and it's possible for a template
        // to have comments along side the root element which makes it a fragment
        let root = result;
        let setRoot = undefined;
        if (result.patchFlag > 0 &&
            result.patchFlag & 2048 /* DEV_ROOT_FRAGMENT */) {
            [root, setRoot] = getChildRoot(result);
        }
        if (fallthroughAttrs && inheritAttrs !== false) {
            const keys = Object.keys(fallthroughAttrs);
            const { shapeFlag } = root;
            if (keys.length) {
                if (shapeFlag & (1 /* ELEMENT */ | 6 /* COMPONENT */)) {
                    if (propsOptions && keys.some(isModelListener)) {
                        // If a v-model listener (onUpdate:xxx) has a corresponding declared
                        // prop, it indicates this component expects to handle v-model and
                        // it should not fallthrough.
                        // related: #1543, #1643, #1989
                        fallthroughAttrs = filterModelListeners(fallthroughAttrs, propsOptions);
                    }
                    root = cloneVNode(root, fallthroughAttrs);
                }
                else if (!accessedAttrs && root.type !== Comment) {
                    const allAttrs = Object.keys(attrs);
                    const eventAttrs = [];
                    const extraAttrs = [];
                    for (let i = 0, l = allAttrs.length; i < l; i++) {
                        const key = allAttrs[i];
                        if (isOn(key)) {
                            // ignore v-model handlers when they fail to fallthrough
                            if (!isModelListener(key)) {
                                // remove `on`, lowercase first letter to reflect event casing
                                // accurately
                                eventAttrs.push(key[2].toLowerCase() + key.slice(3));
                            }
                        }
                        else {
                            extraAttrs.push(key);
                        }
                    }
                    if (extraAttrs.length) {
                        warn$1(`Extraneous non-props attributes (` +
                            `${extraAttrs.join(', ')}) ` +
                            `were passed to component but could not be automatically inherited ` +
                            `because component renders fragment or text root nodes.`);
                    }
                    if (eventAttrs.length) {
                        warn$1(`Extraneous non-emits event listeners (` +
                            `${eventAttrs.join(', ')}) ` +
                            `were passed to component but could not be automatically inherited ` +
                            `because component renders fragment or text root nodes. ` +
                            `If the listener is intended to be a component custom event listener only, ` +
                            `declare it using the "emits" option.`);
                    }
                }
            }
        }
        // inherit directives
        if (vnode.dirs) {
            if (!isElementRoot(root)) {
                warn$1(`Runtime directive used on component with non-element root node. ` +
                    `The directives will not function as intended.`);
            }
            // clone before mutating since the root may be a hoisted vnode
            root = cloneVNode(root);
            root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs;
        }
        // inherit transition data
        if (vnode.transition) {
            if (!isElementRoot(root)) {
                warn$1(`Component inside <Transition> renders non-element root node ` +
                    `that cannot be animated.`);
            }
            root.transition = vnode.transition;
        }
        if (setRoot) {
            setRoot(root);
        }
        else {
            result = root;
        }
        setCurrentRenderingInstance(prev);
        return result;
    }
    /**
     * dev only
     * In dev mode, template root level comments are rendered, which turns the
     * template into a fragment root, but we need to locate the single element
     * root for attrs and scope id processing.
     */
    const getChildRoot = (vnode) => {
        const rawChildren = vnode.children;
        const dynamicChildren = vnode.dynamicChildren;
        const childRoot = filterSingleRoot(rawChildren);
        if (!childRoot) {
            return [vnode, undefined];
        }
        const index = rawChildren.indexOf(childRoot);
        const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1;
        const setRoot = (updatedRoot) => {
            rawChildren[index] = updatedRoot;
            if (dynamicChildren) {
                if (dynamicIndex > -1) {
                    dynamicChildren[dynamicIndex] = updatedRoot;
                }
                else if (updatedRoot.patchFlag > 0) {
                    vnode.dynamicChildren = [...dynamicChildren, updatedRoot];
                }
            }
        };
        return [normalizeVNode(childRoot), setRoot];
    };
    function filterSingleRoot(children) {
        let singleRoot;
        for (let i = 0; i < children.length; i++) {
            const child = children[i];
            if (isVNode(child)) {
                // ignore user comment
                if (child.type !== Comment || child.children === 'v-if') {
                    if (singleRoot) {
                        // has more than 1 non-comment child, return now
                        return;
                    }
                    else {
                        singleRoot = child;
                    }
                }
            }
            else {
                return;
            }
        }
        return singleRoot;
    }
    const getFunctionalFallthrough = (attrs) => {
        let res;
        for (const key in attrs) {
            if (key === 'class' || key === 'style' || isOn(key)) {
                (res || (res = {}))[key] = attrs[key];
            }
        }
        return res;
    };
    const filterModelListeners = (attrs, props) => {
        const res = {};
        for (const key in attrs) {
            if (!isModelListener(key) || !(key.slice(9) in props)) {
                res[key] = attrs[key];
            }
        }
        return res;
    };
    const isElementRoot = (vnode) => {
        return (vnode.shapeFlag & (6 /* COMPONENT */ | 1 /* ELEMENT */) ||
            vnode.type === Comment // potential v-if branch switch
        );
    };
    function shouldUpdateComponent(prevVNode, nextVNode, optimized) {
        const { props: prevProps, children: prevChildren, component } = prevVNode;
        const { props: nextProps, children: nextChildren, patchFlag } = nextVNode;
        const emits = component.emitsOptions;
        // Parent component's render function was hot-updated. Since this may have
        // caused the child component's slots content to have changed, we need to
        // force the child to update as well.
        if ((prevChildren || nextChildren) && isHmrUpdating) {
            return true;
        }
        // force child update for runtime directive or transition on component vnode.
        if (nextVNode.dirs || nextVNode.transition) {
            return true;
        }
        if (optimized && patchFlag >= 0) {
            if (patchFlag & 1024 /* DYNAMIC_SLOTS */) {
                // slot content that references values that might have changed,
                // e.g. in a v-for
                return true;
            }
            if (patchFlag & 16 /* FULL_PROPS */) {
                if (!prevProps) {
                    return !!nextProps;
                }
                // presence of this flag indicates props are always non-null
                return hasPropsChanged(prevProps, nextProps, emits);
            }
            else if (patchFlag & 8 /* PROPS */) {
                const dynamicProps = nextVNode.dynamicProps;
                for (let i = 0; i < dynamicProps.length; i++) {
                    const key = dynamicProps[i];
                    if (nextProps[key] !== prevProps[key] &&
                        !isEmitListener(emits, key)) {
                        return true;
                    }
                }
            }
        }
        else {
            // this path is only taken by manually written render functions
            // so presence of any children leads to a forced update
            if (prevChildren || nextChildren) {
                if (!nextChildren || !nextChildren.$stable) {
                    return true;
                }
            }
            if (prevProps === nextProps) {
                return false;
            }
            if (!prevProps) {
                return !!nextProps;
            }
            if (!nextProps) {
                return true;
            }
            return hasPropsChanged(prevProps, nextProps, emits);
        }
        return false;
    }
    function hasPropsChanged(prevProps, nextProps, emitsOptions) {
        const nextKeys = Object.keys(nextProps);
        if (nextKeys.length !== Object.keys(prevProps).length) {
            return true;
        }
        for (let i = 0; i < nextKeys.length; i++) {
            const key = nextKeys[i];
            if (nextProps[key] !== prevProps[key] &&
                !isEmitListener(emitsOptions, key)) {
                return true;
            }
        }
        return false;
    }
    function updateHOCHostEl({ vnode, parent }, el // HostNode
    ) {
        while (parent && parent.subTree === vnode) {
            (vnode = parent.vnode).el = el;
            parent = parent.parent;
        }
    }

    const isSuspense = (type) => type.__isSuspense;
    // Suspense exposes a component-like API, and is treated like a component
    // in the compiler, but internally it's a special built-in type that hooks
    // directly into the renderer.
    const SuspenseImpl = {
        name: 'Suspense',
        // In order to make Suspense tree-shakable, we need to avoid importing it
        // directly in the renderer. The renderer checks for the __isSuspense flag
        // on a vnode's type and calls the `process` method, passing in renderer
        // internals.
        __isSuspense: true,
        process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, 
        // platform-specific impl passed from renderer
        rendererInternals) {
            if (n1 == null) {
                mountSuspense(n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, rendererInternals);
            }
            else {
                patchSuspense(n1, n2, container, anchor, parentComponent, isSVG, slotScopeIds, optimized, rendererInternals);
            }
        },
        hydrate: hydrateSuspense,
        create: createSuspenseBoundary,
        normalize: normalizeSuspenseChildren
    };
    // Force-casted public typing for h and TSX props inference
    const Suspense = (SuspenseImpl );
    function triggerEvent(vnode, name) {
        const eventListener = vnode.props && vnode.props[name];
        if (isFunction(eventListener)) {
            eventListener();
        }
    }
    function mountSuspense(vnode, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, rendererInternals) {
        const { p: patch, o: { createElement } } = rendererInternals;
        const hiddenContainer = createElement('div');
        const suspense = (vnode.suspense = createSuspenseBoundary(vnode, parentSuspense, parentComponent, container, hiddenContainer, anchor, isSVG, slotScopeIds, optimized, rendererInternals));
        // start mounting the content subtree in an off-dom container
        patch(null, (suspense.pendingBranch = vnode.ssContent), hiddenContainer, null, parentComponent, suspense, isSVG, slotScopeIds);
        // now check if we have encountered any async deps
        if (suspense.deps > 0) {
            // has async
            // invoke @fallback event
            triggerEvent(vnode, 'onPending');
            triggerEvent(vnode, 'onFallback');
            // mount the fallback tree
            patch(null, vnode.ssFallback, container, anchor, parentComponent, null, // fallback tree will not have suspense context
            isSVG, slotScopeIds);
            setActiveBranch(suspense, vnode.ssFallback);
        }
        else {
            // Suspense has no async deps. Just resolve.
            suspense.resolve();
        }
    }
    function patchSuspense(n1, n2, container, anchor, parentComponent, isSVG, slotScopeIds, optimized, { p: patch, um: unmount, o: { createElement } }) {
        const suspense = (n2.suspense = n1.suspense);
        suspense.vnode = n2;
        n2.el = n1.el;
        const newBranch = n2.ssContent;
        const newFallback = n2.ssFallback;
        const { activeBranch, pendingBranch, isInFallback, isHydrating } = suspense;
        if (pendingBranch) {
            suspense.pendingBranch = newBranch;
            if (isSameVNodeType(newBranch, pendingBranch)) {
                // same root type but content may have changed.
                patch(pendingBranch, newBranch, suspense.hiddenContainer, null, parentComponent, suspense, isSVG, slotScopeIds, optimized);
                if (suspense.deps <= 0) {
                    suspense.resolve();
                }
                else if (isInFallback) {
                    patch(activeBranch, newFallback, container, anchor, parentComponent, null, // fallback tree will not have suspense context
                    isSVG, slotScopeIds, optimized);
                    setActiveBranch(suspense, newFallback);
                }
            }
            else {
                // toggled before pending tree is resolved
                suspense.pendingId++;
                if (isHydrating) {
                    // if toggled before hydration is finished, the current DOM tree is
                    // no longer valid. set it as the active branch so it will be unmounted
                    // when resolved
                    suspense.isHydrating = false;
                    suspense.activeBranch = pendingBranch;
                }
                else {
                    unmount(pendingBranch, parentComponent, suspense);
                }
                // increment pending ID. this is used to invalidate async callbacks
                // reset suspense state
                suspense.deps = 0;
                // discard effects from pending branch
                suspense.effects.length = 0;
                // discard previous container
                suspense.hiddenContainer = createElement('div');
                if (isInFallback) {
                    // already in fallback state
                    patch(null, newBranch, suspense.hiddenContainer, null, parentComponent, suspense, isSVG, slotScopeIds, optimized);
                    if (suspense.deps <= 0) {
                        suspense.resolve();
                    }
                    else {
                        patch(activeBranch, newFallback, container, anchor, parentComponent, null, // fallback tree will not have suspense context
                        isSVG, slotScopeIds, optimized);
                        setActiveBranch(suspense, newFallback);
                    }
                }
                else if (activeBranch && isSameVNodeType(newBranch, activeBranch)) {
                    // toggled "back" to current active branch
                    patch(activeBranch, newBranch, container, anchor, parentComponent, suspense, isSVG, slotScopeIds, optimized);
                    // force resolve
                    suspense.resolve(true);
                }
                else {
                    // switched to a 3rd branch
                    patch(null, newBranch, suspense.hiddenContainer, null, parentComponent, suspense, isSVG, slotScopeIds, optimized);
                    if (suspense.deps <= 0) {
                        suspense.resolve();
                    }
                }
            }
        }
        else {
            if (activeBranch && isSameVNodeType(newBranch, activeBranch)) {
                // root did not change, just normal patch
                patch(activeBranch, newBranch, container, anchor, parentComponent, suspense, isSVG, slotScopeIds, optimized);
                setActiveBranch(suspense, newBranch);
            }
            else {
                // root node toggled
                // invoke @pending event
                triggerEvent(n2, 'onPending');
                // mount pending branch in off-dom container
                suspense.pendingBranch = newBranch;
                suspense.pendingId++;
                patch(null, newBranch, suspense.hiddenContainer, null, parentComponent, suspense, isSVG, slotScopeIds, optimized);
                if (suspense.deps <= 0) {
                    // incoming branch has no async deps, resolve now.
                    suspense.resolve();
                }
                else {
                    const { timeout, pendingId } = suspense;
                    if (timeout > 0) {
                        setTimeout(() => {
                            if (suspense.pendingId === pendingId) {
                                suspense.fallback(newFallback);
                            }
                        }, timeout);
                    }
                    else if (timeout === 0) {
                        suspense.fallback(newFallback);
                    }
                }
            }
        }
    }
    let hasWarned = false;
    function createSuspenseBoundary(vnode, parent, parentComponent, container, hiddenContainer, anchor, isSVG, slotScopeIds, optimized, rendererInternals, isHydrating = false) {
        /* istanbul ignore if */
        if (!hasWarned) {
            hasWarned = true;
            // @ts-ignore `console.info` cannot be null error
            console[console.info ? 'info' : 'log'](`<Suspense> is an experimental feature and its API will likely change.`);
        }
        const { p: patch, m: move, um: unmount, n: next, o: { parentNode, remove } } = rendererInternals;
        const timeout = toNumber(vnode.props && vnode.props.timeout);
        const suspense = {
            vnode,
            parent,
            parentComponent,
            isSVG,
            container,
            hiddenContainer,
            anchor,
            deps: 0,
            pendingId: 0,
            timeout: typeof timeout === 'number' ? timeout : -1,
            activeBranch: null,
            pendingBranch: null,
            isInFallback: true,
            isHydrating,
            isUnmounted: false,
            effects: [],
            resolve(resume = false) {
                {
                    if (!resume && !suspense.pendingBranch) {
                        throw new Error(`suspense.resolve() is called without a pending branch.`);
                    }
                    if (suspense.isUnmounted) {
                        throw new Error(`suspense.resolve() is called on an already unmounted suspense boundary.`);
                    }
                }
                const { vnode, activeBranch, pendingBranch, pendingId, effects, parentComponent, container } = suspense;
                if (suspense.isHydrating) {
                    suspense.isHydrating = false;
                }
                else if (!resume) {
                    const delayEnter = activeBranch &&
                        pendingBranch.transition &&
                        pendingBranch.transition.mode === 'out-in';
                    if (delayEnter) {
                        activeBranch.transition.afterLeave = () => {
                            if (pendingId === suspense.pendingId) {
                                move(pendingBranch, container, anchor, 0 /* ENTER */);
                            }
                        };
                    }
                    // this is initial anchor on mount
                    let { anchor } = suspense;
                    // unmount current active tree
                    if (activeBranch) {
                        // if the fallback tree was mounted, it may have been moved
                        // as part of a parent suspense. get the latest anchor for insertion
                        anchor = next(activeBranch);
                        unmount(activeBranch, parentComponent, suspense, true);
                    }
                    if (!delayEnter) {
                        // move content from off-dom container to actual container
                        move(pendingBranch, container, anchor, 0 /* ENTER */);
                    }
                }
                setActiveBranch(suspense, pendingBranch);
                suspense.pendingBranch = null;
                suspense.isInFallback = false;
                // flush buffered effects
                // check if there is a pending parent suspense
                let parent = suspense.parent;
                let hasUnresolvedAncestor = false;
                while (parent) {
                    if (parent.pendingBranch) {
                        // found a pending parent suspense, merge buffered post jobs
                        // into that parent
                        parent.effects.push(...effects);
                        hasUnresolvedAncestor = true;
                        break;
                    }
                    parent = parent.parent;
                }
                // no pending parent suspense, flush all jobs
                if (!hasUnresolvedAncestor) {
                    queuePostFlushCb(effects);
                }
                suspense.effects = [];
                // invoke @resolve event
                triggerEvent(vnode, 'onResolve');
            },
            fallback(fallbackVNode) {
                if (!suspense.pendingBranch) {
                    return;
                }
                const { vnode, activeBranch, parentComponent, container, isSVG } = suspense;
                // invoke @fallback event
                triggerEvent(vnode, 'onFallback');
                const anchor = next(activeBranch);
                const mountFallback = () => {
                    if (!suspense.isInFallback) {
                        return;
                    }
                    // mount the fallback tree
                    patch(null, fallbackVNode, container, anchor, parentComponent, null, // fallback tree will not have suspense context
                    isSVG, slotScopeIds, optimized);
                    setActiveBranch(suspense, fallbackVNode);
                };
                const delayEnter = fallbackVNode.transition && fallbackVNode.transition.mode === 'out-in';
                if (delayEnter) {
                    activeBranch.transition.afterLeave = mountFallback;
                }
                suspense.isInFallback = true;
                // unmount current active branch
                unmount(activeBranch, parentComponent, null, // no suspense so unmount hooks fire now
                true // shouldRemove
                );
                if (!delayEnter) {
                    mountFallback();
                }
            },
            move(container, anchor, type) {
                suspense.activeBranch &&
                    move(suspense.activeBranch, container, anchor, type);
                suspense.container = container;
            },
            next() {
                return suspense.activeBranch && next(suspense.activeBranch);
            },
            registerDep(instance, setupRenderEffect) {
                const isInPendingSuspense = !!suspense.pendingBranch;
                if (isInPendingSuspense) {
                    suspense.deps++;
                }
                const hydratedEl = instance.vnode.el;
                instance
                    .asyncDep.catch(err => {
                    handleError(err, instance, 0 /* SETUP_FUNCTION */);
                })
                    .then(asyncSetupResult => {
                    // retry when the setup() promise resolves.
                    // component may have been unmounted before resolve.
                    if (instance.isUnmounted ||
                        suspense.isUnmounted ||
                        suspense.pendingId !== instance.suspenseId) {
                        return;
                    }
                    // retry from this component
                    instance.asyncResolved = true;
                    const { vnode } = instance;
                    {
                        pushWarningContext(vnode);
                    }
                    handleSetupResult(instance, asyncSetupResult, false);
                    if (hydratedEl) {
                        // vnode may have been replaced if an update happened before the
                        // async dep is resolved.
                        vnode.el = hydratedEl;
                    }
                    const placeholder = !hydratedEl && instance.subTree.el;
                    setupRenderEffect(instance, vnode, 
                    // component may have been moved before resolve.
                    // if this is not a hydration, instance.subTree will be the comment
                    // placeholder.
                    parentNode(hydratedEl || instance.subTree.el), 
                    // anchor will not be used if this is hydration, so only need to
                    // consider the comment placeholder case.
                    hydratedEl ? null : next(instance.subTree), suspense, isSVG, optimized);
                    if (placeholder) {
                        remove(placeholder);
                    }
                    updateHOCHostEl(instance, vnode.el);
                    {
                        popWarningContext();
                    }
                    // only decrease deps count if suspense is not already resolved
                    if (isInPendingSuspense && --suspense.deps === 0) {
                        suspense.resolve();
                    }
                });
            },
            unmount(parentSuspense, doRemove) {
                suspense.isUnmounted = true;
                if (suspense.activeBranch) {
                    unmount(suspense.activeBranch, parentComponent, parentSuspense, doRemove);
                }
                if (suspense.pendingBranch) {
                    unmount(suspense.pendingBranch, parentComponent, parentSuspense, doRemove);
                }
            }
        };
        return suspense;
    }
    function hydrateSuspense(node, vnode, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, rendererInternals, hydrateNode) {
        /* eslint-disable no-restricted-globals */
        const suspense = (vnode.suspense = createSuspenseBoundary(vnode, parentSuspense, parentComponent, node.parentNode, document.createElement('div'), null, isSVG, slotScopeIds, optimized, rendererInternals, true /* hydrating */));
        // there are two possible scenarios for server-rendered suspense:
        // - success: ssr content should be fully resolved
        // - failure: ssr content should be the fallback branch.
        // however, on the client we don't really know if it has failed or not
        // attempt to hydrate the DOM assuming it has succeeded, but we still
        // need to construct a suspense boundary first
        const result = hydrateNode(node, (suspense.pendingBranch = vnode.ssContent), parentComponent, suspense, slotScopeIds, optimized);
        if (suspense.deps === 0) {
            suspense.resolve();
        }
        return result;
        /* eslint-enable no-restricted-globals */
    }
    function normalizeSuspenseChildren(vnode) {
        const { shapeFlag, children } = vnode;
        const isSlotChildren = shapeFlag & 32 /* SLOTS_CHILDREN */;
        vnode.ssContent = normalizeSuspenseSlot(isSlotChildren ? children.default : children);
        vnode.ssFallback = isSlotChildren
            ? normalizeSuspenseSlot(children.fallback)
            : createVNode(Comment);
    }
    function normalizeSuspenseSlot(s) {
        let block;
        if (isFunction(s)) {
            const trackBlock = isBlockTreeEnabled && s._c;
            if (trackBlock) {
                // disableTracking: false
                // allow block tracking for compiled slots
                // (see ./componentRenderContext.ts)
                s._d = false;
                openBlock();
            }
            s = s();
            if (trackBlock) {
                s._d = true;
                block = currentBlock;
                closeBlock();
            }
        }
        if (isArray(s)) {
            const singleChild = filterSingleRoot(s);
            if (!singleChild) {
                warn$1(`<Suspense> slots expect a single root node.`);
            }
            s = singleChild;
        }
        s = normalizeVNode(s);
        if (block && !s.dynamicChildren) {
            s.dynamicChildren = block.filter(c => c !== s);
        }
        return s;
    }
    function queueEffectWithSuspense(fn, suspense) {
        if (suspense && suspense.pendingBranch) {
            if (isArray(fn)) {
                suspense.effects.push(...fn);
            }
            else {
                suspense.effects.push(fn);
            }
        }
        else {
            queuePostFlushCb(fn);
        }
    }
    function setActiveBranch(suspense, branch) {
        suspense.activeBranch = branch;
        const { vnode, parentComponent } = suspense;
        const el = (vnode.el = branch.el);
        // in case suspense is the root node of a component,
        // recursively update the HOC el
        if (parentComponent && parentComponent.subTree === vnode) {
            parentComponent.vnode.el = el;
            updateHOCHostEl(parentComponent, el);
        }
    }

    function provide(key, value) {
        if (!currentInstance) {
            {
                warn$1(`provide() can only be used inside setup().`);
            }
        }
        else {
            let provides = currentInstance.provides;
            // by default an instance inherits its parent's provides object
            // but when it needs to provide values of its own, it creates its
            // own provides object using parent provides object as prototype.
            // this way in `inject` we can simply look up injections from direct
            // parent and let the prototype chain do the work.
            const parentProvides = currentInstance.parent && currentInstance.parent.provides;
            if (parentProvides === provides) {
                provides = currentInstance.provides = Object.create(parentProvides);
            }
            // TS doesn't allow symbol as index type
            provides[key] = value;
        }
    }
    function inject(key, defaultValue, treatDefaultAsFactory = false) {
        // fallback to `currentRenderingInstance` so that this can be called in
        // a functional component
        const instance = currentInstance || currentRenderingInstance;
        if (instance) {
            // #2400
            // to support `app.use` plugins,
            // fallback to appContext's `provides` if the instance is at root
            const provides = instance.parent == null
                ? instance.vnode.appContext && instance.vnode.appContext.provides
                : instance.parent.provides;
            if (provides && key in provides) {
                // TS doesn't allow symbol as index type
                return provides[key];
            }
            else if (arguments.length > 1) {
                return treatDefaultAsFactory && isFunction(defaultValue)
                    ? defaultValue.call(instance.proxy)
                    : defaultValue;
            }
            else {
                warn$1(`injection "${String(key)}" not found.`);
            }
        }
        else {
            warn$1(`inject() can only be used inside setup() or functional components.`);
        }
    }

    // Simple effect.
    function watchEffect(effect, options) {
        return doWatch(effect, null, options);
    }
    function watchPostEffect(effect, options) {
        return doWatch(effect, null, (Object.assign(Object.assign({}, options), { flush: 'post' }) ));
    }
    function watchSyncEffect(effect, options) {
        return doWatch(effect, null, (Object.assign(Object.assign({}, options), { flush: 'sync' }) ));
    }
    // initial value for watchers to trigger on undefined initial values
    const INITIAL_WATCHER_VALUE = {};
    // implementation
    function watch(source, cb, options) {
        if (!isFunction(cb)) {
            warn$1(`\`watch(fn, options?)\` signature has been moved to a separate API. ` +
                `Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
                `supports \`watch(source, cb, options?) signature.`);
        }
        return doWatch(source, cb, options);
    }
    function doWatch(source, cb, { immediate, deep, flush, onTrack, onTrigger } = EMPTY_OBJ) {
        if (!cb) {
            if (immediate !== undefined) {
                warn$1(`watch() "immediate" option is only respected when using the ` +
                    `watch(source, callback, options?) signature.`);
            }
            if (deep !== undefined) {
                warn$1(`watch() "deep" option is only respected when using the ` +
                    `watch(source, callback, options?) signature.`);
            }
        }
        const warnInvalidSource = (s) => {
            warn$1(`Invalid watch source: `, s, `A watch source can only be a getter/effect function, a ref, ` +
                `a reactive object, or an array of these types.`);
        };
        const instance = currentInstance;
        let getter;
        let forceTrigger = false;
        let isMultiSource = false;
        if (isRef(source)) {
            getter = () => source.value;
            forceTrigger = isShallow(source);
        }
        else if (isReactive(source)) {
            getter = () => source;
            deep = true;
        }
        else if (isArray(source)) {
            isMultiSource = true;
            forceTrigger = source.some(s => isReactive(s) || isShallow(s));
            getter = () => source.map(s => {
                if (isRef(s)) {
                    return s.value;
                }
                else if (isReactive(s)) {
                    return traverse(s);
                }
                else if (isFunction(s)) {
                    return callWithErrorHandling(s, instance, 2 /* WATCH_GETTER */);
                }
                else {
                    warnInvalidSource(s);
                }
            });
        }
        else if (isFunction(source)) {
            if (cb) {
                // getter with cb
                getter = () => callWithErrorHandling(source, instance, 2 /* WATCH_GETTER */);
            }
            else {
                // no cb -> simple effect
                getter = () => {
                    if (instance && instance.isUnmounted) {
                        return;
                    }
                    if (cleanup) {
                        cleanup();
                    }
                    return callWithAsyncErrorHandling(source, instance, 3 /* WATCH_CALLBACK */, [onCleanup]);
                };
            }
        }
        else {
            getter = NOOP;
            warnInvalidSource(source);
        }
        if (cb && deep) {
            const baseGetter = getter;
            getter = () => traverse(baseGetter());
        }
        let cleanup;
        let onCleanup = (fn) => {
            cleanup = effect.onStop = () => {
                callWithErrorHandling(fn, instance, 4 /* WATCH_CLEANUP */);
            };
        };
        let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE;
        const job = () => {
            if (!effect.active) {
                return;
            }
            if (cb) {
                // watch(source, cb)
                const newValue = effect.run();
                if (deep ||
                    forceTrigger ||
                    (isMultiSource
                        ? newValue.some((v, i) => hasChanged(v, oldValue[i]))
                        : hasChanged(newValue, oldValue)) ||
                    (false  )) {
                    // cleanup before running cb again
                    if (cleanup) {
                        cleanup();
                    }
                    callWithAsyncErrorHandling(cb, instance, 3 /* WATCH_CALLBACK */, [
                        newValue,
                        // pass undefined as the old value when it's changed for the first time
                        oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
                        onCleanup
                    ]);
                    oldValue = newValue;
                }
            }
            else {
                // watchEffect
                effect.run();
            }
        };
        // important: mark the job as a watcher callback so that scheduler knows
        // it is allowed to self-trigger (#1727)
        job.allowRecurse = !!cb;
        let scheduler;
        if (flush === 'sync') {
            scheduler = job; // the scheduler function gets called directly
        }
        else if (flush === 'post') {
            scheduler = () => queuePostRenderEffect(job, instance && instance.suspense);
        }
        else {
            // default: 'pre'
            scheduler = () => queuePreFlushCb(job);
        }
        const effect = new ReactiveEffect(getter, scheduler);
        {
            effect.onTrack = onTrack;
            effect.onTrigger = onTrigger;
        }
        // initial run
        if (cb) {
            if (immediate) {
                job();
            }
            else {
                oldValue = effect.run();
            }
        }
        else if (flush === 'post') {
            queuePostRenderEffect(effect.run.bind(effect), instance && instance.suspense);
        }
        else {
            effect.run();
        }
        return () => {
            effect.stop();
            if (instance && instance.scope) {
                remove(instance.scope.effects, effect);
            }
        };
    }
    // this.$watch
    function instanceWatch(source, value, options) {
        const publicThis = this.proxy;
        const getter = isString(source)
            ? source.includes('.')
                ? createPathGetter(publicThis, source)
                : () => publicThis[source]
            : source.bind(publicThis, publicThis);
        let cb;
        if (isFunction(value)) {
            cb = value;
        }
        else {
            cb = value.handler;
            options = value;
        }
        const cur = currentInstance;
        setCurrentInstance(this);
        const res = doWatch(getter, cb.bind(publicThis), options);
        if (cur) {
            setCurrentInstance(cur);
        }
        else {
            unsetCurrentInstance();
        }
        return res;
    }
    function createPathGetter(ctx, path) {
        const segments = path.split('.');
        return () => {
            let cur = ctx;
            for (let i = 0; i < segments.length && cur; i++) {
                cur = cur[segments[i]];
            }
            return cur;
        };
    }
    function traverse(value, seen) {
        if (!isObject(value) || value["__v_skip" /* SKIP */]) {
            return value;
        }
        seen = seen || new Set();
        if (seen.has(value)) {
            return value;
        }
        seen.add(value);
        if (isRef(value)) {
            traverse(value.value, seen);
        }
        else if (isArray(value)) {
            for (let i = 0; i < value.length; i++) {
                traverse(value[i], seen);
            }
        }
        else if (isSet(value) || isMap(value)) {
            value.forEach((v) => {
                traverse(v, seen);
            });
        }
        else if (isPlainObject(value)) {
            for (const key in value) {
                traverse(value[key], seen);
            }
        }
        return value;
    }

    function useTransitionState() {
        const state = {
            isMounted: false,
            isLeaving: false,
            isUnmounting: false,
            leavingVNodes: new Map()
        };
        onMounted(() => {
            state.isMounted = true;
        });
        onBeforeUnmount(() => {
            state.isUnmounting = true;
        });
        return state;
    }
    const TransitionHookValidator = [Function, Array];
    const BaseTransitionImpl = {
        name: `BaseTransition`,
        props: {
            mode: String,
            appear: Boolean,
            persisted: Boolean,
            // enter
            onBeforeEnter: TransitionHookValidator,
            onEnter: TransitionHookValidator,
            onAfterEnter: TransitionHookValidator,
            onEnterCancelled: TransitionHookValidator,
            // leave
            onBeforeLeave: TransitionHookValidator,
            onLeave: TransitionHookValidator,
            onAfterLeave: TransitionHookValidator,
            onLeaveCancelled: TransitionHookValidator,
            // appear
            onBeforeAppear: TransitionHookValidator,
            onAppear: TransitionHookValidator,
            onAfterAppear: TransitionHookValidator,
            onAppearCancelled: TransitionHookValidator
        },
        setup(props, { slots }) {
            const instance = getCurrentInstance();
            const state = useTransitionState();
            let prevTransitionKey;
            return () => {
                const children = slots.default && getTransitionRawChildren(slots.default(), true);
                if (!children || !children.length) {
                    return;
                }
                let child = children[0];
                if (children.length > 1) {
                    let hasFound = false;
                    // locate first non-comment child
                    for (const c of children) {
                        if (c.type !== Comment) {
                            if (hasFound) {
                                // warn more than one non-comment child
                                warn$1('<transition> can only be used on a single element or component. ' +
                                    'Use <transition-group> for lists.');
                                break;
                            }
                            child = c;
                            hasFound = true;
                        }
                    }
                }
                // there's no need to track reactivity for these props so use the raw
                // props for a bit better perf
                const rawProps = toRaw(props);
                const { mode } = rawProps;
                // check mode
                if (mode &&
                    mode !== 'in-out' &&
                    mode !== 'out-in' &&
                    mode !== 'default') {
                    warn$1(`invalid <transition> mode: ${mode}`);
                }
                if (state.isLeaving) {
                    return emptyPlaceholder(child);
                }
                // in the case of <transition><keep-alive/></transition>, we need to
                // compare the type of the kept-alive children.
                const innerChild = getKeepAliveChild(child);
                if (!innerChild) {
                    return emptyPlaceholder(child);
                }
                const enterHooks = resolveTransitionHooks(innerChild, rawProps, state, instance);
                setTransitionHooks(innerChild, enterHooks);
                const oldChild = instance.subTree;
                const oldInnerChild = oldChild && getKeepAliveChild(oldChild);
                let transitionKeyChanged = false;
                const { getTransitionKey } = innerChild.type;
                if (getTransitionKey) {
                    const key = getTransitionKey();
                    if (prevTransitionKey === undefined) {
                        prevTransitionKey = key;
                    }
                    else if (key !== prevTransitionKey) {
                        prevTransitionKey = key;
                        transitionKeyChanged = true;
                    }
                }
                // handle mode
                if (oldInnerChild &&
                    oldInnerChild.type !== Comment &&
                    (!isSameVNodeType(innerChild, oldInnerChild) || transitionKeyChanged)) {
                    const leavingHooks = resolveTransitionHooks(oldInnerChild, rawProps, state, instance);
                    // update old tree's hooks in case of dynamic transition
                    setTransitionHooks(oldInnerChild, leavingHooks);
                    // switching between different views
                    if (mode === 'out-in') {
                        state.isLeaving = true;
                        // return placeholder node and queue update when leave finishes
                        leavingHooks.afterLeave = () => {
                            state.isLeaving = false;
                            instance.update();
                        };
                        return emptyPlaceholder(child);
                    }
                    else if (mode === 'in-out' && innerChild.type !== Comment) {
                        leavingHooks.delayLeave = (el, earlyRemove, delayedLeave) => {
                            const leavingVNodesCache = getLeavingNodesForType(state, oldInnerChild);
                            leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild;
                            // early removal callback
                            el._leaveCb = () => {
                                earlyRemove();
                                el._leaveCb = undefined;
                                delete enterHooks.delayedLeave;
                            };
                            enterHooks.delayedLeave = delayedLeave;
                        };
                    }
                }
                return child;
            };
        }
    };
    // export the public type for h/tsx inference
    // also to avoid inline import() in generated d.ts files
    const BaseTransition = BaseTransitionImpl;
    function getLeavingNodesForType(state, vnode) {
        const { leavingVNodes } = state;
        let leavingVNodesCache = leavingVNodes.get(vnode.type);
        if (!leavingVNodesCache) {
            leavingVNodesCache = Object.create(null);
            leavingVNodes.set(vnode.type, leavingVNodesCache);
        }
        return leavingVNodesCache;
    }
    // The transition hooks are attached to the vnode as vnode.transition
    // and will be called at appropriate timing in the renderer.
    function resolveTransitionHooks(vnode, props, state, instance) {
        const { appear, mode, persisted = false, onBeforeEnter, onEnter, onAfterEnter, onEnterCancelled, onBeforeLeave, onLeave, onAfterLeave, onLeaveCancelled, onBeforeAppear, onAppear, onAfterAppear, onAppearCancelled } = props;
        const key = String(vnode.key);
        const leavingVNodesCache = getLeavingNodesForType(state, vnode);
        const callHook = (hook, args) => {
            hook &&
                callWithAsyncErrorHandling(hook, instance, 9 /* TRANSITION_HOOK */, args);
        };
        const callAsyncHook = (hook, args) => {
            const done = args[1];
            callHook(hook, args);
            if (isArray(hook)) {
                if (hook.every(hook => hook.length <= 1))
                    done();
            }
            else if (hook.length <= 1) {
                done();
            }
        };
        const hooks = {
            mode,
            persisted,
            beforeEnter(el) {
                let hook = onBeforeEnter;
                if (!state.isMounted) {
                    if (appear) {
                        hook = onBeforeAppear || onBeforeEnter;
                    }
                    else {
                        return;
                    }
                }
                // for same element (v-show)
                if (el._leaveCb) {
                    el._leaveCb(true /* cancelled */);
                }
                // for toggled element with same key (v-if)
                const leavingVNode = leavingVNodesCache[key];
                if (leavingVNode &&
                    isSameVNodeType(vnode, leavingVNode) &&
                    leavingVNode.el._leaveCb) {
                    // force early removal (not cancelled)
                    leavingVNode.el._leaveCb();
                }
                callHook(hook, [el]);
            },
            enter(el) {
                let hook = onEnter;
                let afterHook = onAfterEnter;
                let cancelHook = onEnterCancelled;
                if (!state.isMounted) {
                    if (appear) {
                        hook = onAppear || onEnter;
                        afterHook = onAfterAppear || onAfterEnter;
                        cancelHook = onAppearCancelled || onEnterCancelled;
                    }
                    else {
                        return;
                    }
                }
                let called = false;
                const done = (el._enterCb = (cancelled) => {
                    if (called)
                        return;
                    called = true;
                    if (cancelled) {
                        callHook(cancelHook, [el]);
                    }
                    else {
                        callHook(afterHook, [el]);
                    }
                    if (hooks.delayedLeave) {
                        hooks.delayedLeave();
                    }
                    el._enterCb = undefined;
                });
                if (hook) {
                    callAsyncHook(hook, [el, done]);
                }
                else {
                    done();
                }
            },
            leave(el, remove) {
                const key = String(vnode.key);
                if (el._enterCb) {
                    el._enterCb(true /* cancelled */);
                }
                if (state.isUnmounting) {
                    return remove();
                }
                callHook(onBeforeLeave, [el]);
                let called = false;
                const done = (el._leaveCb = (cancelled) => {
                    if (called)
                        return;
                    called = true;
                    remove();
                    if (cancelled) {
                        callHook(onLeaveCancelled, [el]);
                    }
                    else {
                        callHook(onAfterLeave, [el]);
                    }
                    el._leaveCb = undefined;
                    if (leavingVNodesCache[key] === vnode) {
                        delete leavingVNodesCache[key];
                    }
                });
                leavingVNodesCache[key] = vnode;
                if (onLeave) {
                    callAsyncHook(onLeave, [el, done]);
                }
                else {
                    done();
                }
            },
            clone(vnode) {
                return resolveTransitionHooks(vnode, props, state, instance);
            }
        };
        return hooks;
    }
    // the placeholder really only handles one special case: KeepAlive
    // in the case of a KeepAlive in a leave phase we need to return a KeepAlive
    // placeholder with empty content to avoid the KeepAlive instance from being
    // unmounted.
    function emptyPlaceholder(vnode) {
        if (isKeepAlive(vnode)) {
            vnode = cloneVNode(vnode);
            vnode.children = null;
            return vnode;
        }
    }
    function getKeepAliveChild(vnode) {
        return isKeepAlive(vnode)
            ? vnode.children
                ? vnode.children[0]
                : undefined
            : vnode;
    }
    function setTransitionHooks(vnode, hooks) {
        if (vnode.shapeFlag & 6 /* COMPONENT */ && vnode.component) {
            setTransitionHooks(vnode.component.subTree, hooks);
        }
        else if (vnode.shapeFlag & 128 /* SUSPENSE */) {
            vnode.ssContent.transition = hooks.clone(vnode.ssContent);
            vnode.ssFallback.transition = hooks.clone(vnode.ssFallback);
        }
        else {
            vnode.transition = hooks;
        }
    }
    function getTransitionRawChildren(children, keepComment = false, parentKey) {
        let ret = [];
        let keyedFragmentCount = 0;
        for (let i = 0; i < children.length; i++) {
            let child = children[i];
            // #5360 inherit parent key in case of <template v-for>
            const key = parentKey == null
                ? child.key
                : String(parentKey) + String(child.key != null ? child.key : i);
            // handle fragment children case, e.g. v-for
            if (child.type === Fragment) {
                if (child.patchFlag & 128 /* KEYED_FRAGMENT */)
                    keyedFragmentCount++;
                ret = ret.concat(getTransitionRawChildren(child.children, keepComment, key));
            }
            // comment placeholders should be skipped, e.g. v-if
            else if (keepComment || child.type !== Comment) {
                ret.push(key != null ? cloneVNode(child, { key }) : child);
            }
        }
        // #1126 if a transition children list contains multiple sub fragments, these
        // fragments will be merged into a flat children array. Since each v-for
        // fragment may contain different static bindings inside, we need to de-op
        // these children to force full diffs to ensure correct behavior.
        if (keyedFragmentCount > 1) {
            for (let i = 0; i < ret.length; i++) {
                ret[i].patchFlag = -2 /* BAIL */;
            }
        }
        return ret;
    }

    // implementation, close to no-op
    function defineComponent(options) {
        return isFunction(options) ? { setup: options, name: options.name } : options;
    }

    const isAsyncWrapper = (i) => !!i.type.__asyncLoader;
    function defineAsyncComponent(source) {
        if (isFunction(source)) {
            source = { loader: source };
        }
        const { loader, loadingComponent, errorComponent, delay = 200, timeout, // undefined = never times out
        suspensible = true, onError: userOnError } = source;
        let pendingRequest = null;
        let resolvedComp;
        let retries = 0;
        const retry = () => {
            retries++;
            pendingRequest = null;
            return load();
        };
        const load = () => {
            let thisRequest;
            return (pendingRequest ||
                (thisRequest = pendingRequest =
                    loader()
                        .catch(err => {
                        err = err instanceof Error ? err : new Error(String(err));
                        if (userOnError) {
                            return new Promise((resolve, reject) => {
                                const userRetry = () => resolve(retry());
                                const userFail = () => reject(err);
                                userOnError(err, userRetry, userFail, retries + 1);
                            });
                        }
                        else {
                            throw err;
                        }
                    })
                        .then((comp) => {
                        if (thisRequest !== pendingRequest && pendingRequest) {
                            return pendingRequest;
                        }
                        if (!comp) {
                            warn$1(`Async component loader resolved to undefined. ` +
                                `If you are using retry(), make sure to return its return value.`);
                        }
                        // interop module default
                        if (comp &&
                            (comp.__esModule || comp[Symbol.toStringTag] === 'Module')) {
                            comp = comp.default;
                        }
                        if (comp && !isObject(comp) && !isFunction(comp)) {
                            throw new Error(`Invalid async component load result: ${comp}`);
                        }
                        resolvedComp = comp;
                        return comp;
                    })));
        };
        return defineComponent({
            name: 'AsyncComponentWrapper',
            __asyncLoader: load,
            get __asyncResolved() {
                return resolvedComp;
            },
            setup() {
                const instance = currentInstance;
                // already resolved
                if (resolvedComp) {
                    return () => createInnerComp(resolvedComp, instance);
                }
                const onError = (err) => {
                    pendingRequest = null;
                    handleError(err, instance, 13 /* ASYNC_COMPONENT_LOADER */, !errorComponent /* do not throw in dev if user provided error component */);
                };
                // suspense-controlled or SSR.
                if ((suspensible && instance.suspense) ||
                    (false )) {
                    return load()
                        .then(comp => {
                        return () => createInnerComp(comp, instance);
                    })
                        .catch(err => {
                        onError(err);
                        return () => errorComponent
                            ? createVNode(errorComponent, {
                                error: err
                            })
                            : null;
                    });
                }
                const loaded = ref(false);
                const error = ref();
                const delayed = ref(!!delay);
                if (delay) {
                    setTimeout(() => {
                        delayed.value = false;
                    }, delay);
                }
                if (timeout != null) {
                    setTimeout(() => {
                        if (!loaded.value && !error.value) {
                            const err = new Error(`Async component timed out after ${timeout}ms.`);
                            onError(err);
                            error.value = err;
                        }
                    }, timeout);
                }
                load()
                    .then(() => {
                    loaded.value = true;
                    if (instance.parent && isKeepAlive(instance.parent.vnode)) {
                        // parent is keep-alive, force update so the loaded component's
                        // name is taken into account
                        queueJob(instance.parent.update);
                    }
                })
                    .catch(err => {
                    onError(err);
                    error.value = err;
                });
                return () => {
                    if (loaded.value && resolvedComp) {
                        return createInnerComp(resolvedComp, instance);
                    }
                    else if (error.value && errorComponent) {
                        return createVNode(errorComponent, {
                            error: error.value
                        });
                    }
                    else if (loadingComponent && !delayed.value) {
                        return createVNode(loadingComponent);
                    }
                };
            }
        });
    }
    function createInnerComp(comp, { vnode: { ref, props, children, shapeFlag }, parent }) {
        const vnode = createVNode(comp, props, children);
        // ensure inner component inherits the async wrapper's ref owner
        vnode.ref = ref;
        return vnode;
    }

    const isKeepAlive = (vnode) => vnode.type.__isKeepAlive;
    const KeepAliveImpl = {
        name: `KeepAlive`,
        // Marker for special handling inside the renderer. We are not using a ===
        // check directly on KeepAlive in the renderer, because importing it directly
        // would prevent it from being tree-shaken.
        __isKeepAlive: true,
        props: {
            include: [String, RegExp, Array],
            exclude: [String, RegExp, Array],
            max: [String, Number]
        },
        setup(props, { slots }) {
            const instance = getCurrentInstance();
            // KeepAlive communicates with the instantiated renderer via the
            // ctx where the renderer passes in its internals,
            // and the KeepAlive instance exposes activate/deactivate implementations.
            // The whole point of this is to avoid importing KeepAlive directly in the
            // renderer to facilitate tree-shaking.
            const sharedContext = instance.ctx;
            const cache = new Map();
            const keys = new Set();
            let current = null;
            {
                instance.__v_cache = cache;
            }
            const parentSuspense = instance.suspense;
            const { renderer: { p: patch, m: move, um: _unmount, o: { createElement } } } = sharedContext;
            const storageContainer = createElement('div');
            sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
                const instance = vnode.component;
                move(vnode, container, anchor, 0 /* ENTER */, parentSuspense);
                // in case props have changed
                patch(instance.vnode, vnode, container, anchor, instance, parentSuspense, isSVG, vnode.slotScopeIds, optimized);
                queuePostRenderEffect(() => {
                    instance.isDeactivated = false;
                    if (instance.a) {
                        invokeArrayFns(instance.a);
                    }
                    const vnodeHook = vnode.props && vnode.props.onVnodeMounted;
                    if (vnodeHook) {
                        invokeVNodeHook(vnodeHook, instance.parent, vnode);
                    }
                }, parentSuspense);
                {
                    // Update components tree
                    devtoolsComponentAdded(instance);
                }
            };
            sharedContext.deactivate = (vnode) => {
                const instance = vnode.component;
                move(vnode, storageContainer, null, 1 /* LEAVE */, parentSuspense);
                queuePostRenderEffect(() => {
                    if (instance.da) {
                        invokeArrayFns(instance.da);
                    }
                    const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted;
                    if (vnodeHook) {
                        invokeVNodeHook(vnodeHook, instance.parent, vnode);
                    }
                    instance.isDeactivated = true;
                }, parentSuspense);
                {
                    // Update components tree
                    devtoolsComponentAdded(instance);
                }
            };
            function unmount(vnode) {
                // reset the shapeFlag so it can be properly unmounted
                resetShapeFlag(vnode);
                _unmount(vnode, instance, parentSuspense, true);
            }
            function pruneCache(filter) {
                cache.forEach((vnode, key) => {
                    const name = getComponentName(vnode.type);
                    if (name && (!filter || !filter(name))) {
                        pruneCacheEntry(key);
                    }
                });
            }
            function pruneCacheEntry(key) {
                const cached = cache.get(key);
                if (!current || cached.type !== current.type) {
                    unmount(cached);
                }
                else if (current) {
                    // current active instance should no longer be kept-alive.
                    // we can't unmount it now but it might be later, so reset its flag now.
                    resetShapeFlag(current);
                }
                cache.delete(key);
                keys.delete(key);
            }
            // prune cache on include/exclude prop change
            watch(() => [props.include, props.exclude], ([include, exclude]) => {
                include && pruneCache(name => matches(include, name));
                exclude && pruneCache(name => !matches(exclude, name));
            }, 
            // prune post-render after `current` has been updated
            { flush: 'post', deep: true });
            // cache sub tree after render
            let pendingCacheKey = null;
            const cacheSubtree = () => {
                // fix #1621, the pendingCacheKey could be 0
                if (pendingCacheKey != null) {
                    cache.set(pendingCacheKey, getInnerChild(instance.subTree));
                }
            };
            onMounted(cacheSubtree);
            onUpdated(cacheSubtree);
            onBeforeUnmount(() => {
                cache.forEach(cached => {
                    const { subTree, suspense } = instance;
                    const vnode = getInnerChild(subTree);
                    if (cached.type === vnode.type) {
                        // current instance will be unmounted as part of keep-alive's unmount
                        resetShapeFlag(vnode);
                        // but invoke its deactivated hook here
                        const da = vnode.component.da;
                        da && queuePostRenderEffect(da, suspense);
                        return;
                    }
                    unmount(cached);
                });
            });
            return () => {
                pendingCacheKey = null;
                if (!slots.default) {
                    return null;
                }
                const children = slots.default();
                const rawVNode = children[0];
                if (children.length > 1) {
                    {
                        warn$1(`KeepAlive should contain exactly one component child.`);
                    }
                    current = null;
                    return children;
                }
                else if (!isVNode(rawVNode) ||
                    (!(rawVNode.shapeFlag & 4 /* STATEFUL_COMPONENT */) &&
                        !(rawVNode.shapeFlag & 128 /* SUSPENSE */))) {
                    current = null;
                    return rawVNode;
                }
                let vnode = getInnerChild(rawVNode);
                const comp = vnode.type;
                // for async components, name check should be based in its loaded
                // inner component if available
                const name = getComponentName(isAsyncWrapper(vnode)
                    ? vnode.type.__asyncResolved || {}
                    : comp);
                const { include, exclude, max } = props;
                if ((include && (!name || !matches(include, name))) ||
                    (exclude && name && matches(exclude, name))) {
                    current = vnode;
                    return rawVNode;
                }
                const key = vnode.key == null ? comp : vnode.key;
                const cachedVNode = cache.get(key);
                // clone vnode if it's reused because we are going to mutate it
                if (vnode.el) {
                    vnode = cloneVNode(vnode);
                    if (rawVNode.shapeFlag & 128 /* SUSPENSE */) {
                        rawVNode.ssContent = vnode;
                    }
                }
                // #1513 it's possible for the returned vnode to be cloned due to attr
                // fallthrough or scopeId, so the vnode here may not be the final vnode
                // that is mounted. Instead of caching it directly, we store the pending
                // key and cache `instance.subTree` (the normalized vnode) in
                // beforeMount/beforeUpdate hooks.
                pendingCacheKey = key;
                if (cachedVNode) {
                    // copy over mounted state
                    vnode.el = cachedVNode.el;
                    vnode.component = cachedVNode.component;
                    if (vnode.transition) {
                        // recursively update transition hooks on subTree
                        setTransitionHooks(vnode, vnode.transition);
                    }
                    // avoid vnode being mounted as fresh
                    vnode.shapeFlag |= 512 /* COMPONENT_KEPT_ALIVE */;
                    // make this key the freshest
                    keys.delete(key);
                    keys.add(key);
                }
                else {
                    keys.add(key);
                    // prune oldest entry
                    if (max && keys.size > parseInt(max, 10)) {
                        pruneCacheEntry(keys.values().next().value);
                    }
                }
                // avoid vnode being unmounted
                vnode.shapeFlag |= 256 /* COMPONENT_SHOULD_KEEP_ALIVE */;
                current = vnode;
                return isSuspense(rawVNode.type) ? rawVNode : vnode;
            };
        }
    };
    // export the public type for h/tsx inference
    // also to avoid inline import() in generated d.ts files
    const KeepAlive = KeepAliveImpl;
    function matches(pattern, name) {
        if (isArray(pattern)) {
            return pattern.some((p) => matches(p, name));
        }
        else if (isString(pattern)) {
            return pattern.split(',').includes(name);
        }
        else if (pattern.test) {
            return pattern.test(name);
        }
        /* istanbul ignore next */
        return false;
    }
    function onActivated(hook, target) {
        registerKeepAliveHook(hook, "a" /* ACTIVATED */, target);
    }
    function onDeactivated(hook, target) {
        registerKeepAliveHook(hook, "da" /* DEACTIVATED */, target);
    }
    function registerKeepAliveHook(hook, type, target = currentInstance) {
        // cache the deactivate branch check wrapper for injected hooks so the same
        // hook can be properly deduped by the scheduler. "__wdc" stands for "with
        // deactivation check".
        const wrappedHook = hook.__wdc ||
            (hook.__wdc = () => {
                // only fire the hook if the target instance is NOT in a deactivated branch.
                let current = target;
                while (current) {
                    if (current.isDeactivated) {
                        return;
                    }
                    current = current.parent;
                }
                return hook();
            });
        injectHook(type, wrappedHook, target);
        // In addition to registering it on the target instance, we walk up the parent
        // chain and register it on all ancestor instances that are keep-alive roots.
        // This avoids the need to walk the entire component tree when invoking these
        // hooks, and more importantly, avoids the need to track child components in
        // arrays.
        if (target) {
            let current = target.parent;
            while (current && current.parent) {
                if (isKeepAlive(current.parent.vnode)) {
                    injectToKeepAliveRoot(wrappedHook, type, target, current);
                }
                current = current.parent;
            }
        }
    }
    function injectToKeepAliveRoot(hook, type, target, keepAliveRoot) {
        // injectHook wraps the original for error handling, so make sure to remove
        // the wrapped version.
        const injected = injectHook(type, hook, keepAliveRoot, true /* prepend */);
        onUnmounted(() => {
            remove(keepAliveRoot[type], injected);
        }, target);
    }
    function resetShapeFlag(vnode) {
        let shapeFlag = vnode.shapeFlag;
        if (shapeFlag & 256 /* COMPONENT_SHOULD_KEEP_ALIVE */) {
            shapeFlag -= 256 /* COMPONENT_SHOULD_KEEP_ALIVE */;
        }
        if (shapeFlag & 512 /* COMPONENT_KEPT_ALIVE */) {
            shapeFlag -= 512 /* COMPONENT_KEPT_ALIVE */;
        }
        vnode.shapeFlag = shapeFlag;
    }
    function getInnerChild(vnode) {
        return vnode.shapeFlag & 128 /* SUSPENSE */ ? vnode.ssContent : vnode;
    }

    function injectHook(type, hook, target = currentInstance, prepend = false) {
        if (target) {
            const hooks = target[type] || (target[type] = []);
            // cache the error handling wrapper for injected hooks so the same hook
            // can be properly deduped by the scheduler. "__weh" stands for "with error
            // handling".
            const wrappedHook = hook.__weh ||
                (hook.__weh = (...args) => {
                    if (target.isUnmounted) {
                        return;
                    }
                    // disable tracking inside all lifecycle hooks
                    // since they can potentially be called inside effects.
                    pauseTracking();
                    // Set currentInstance during hook invocation.
                    // This assumes the hook does not synchronously trigger other hooks, which
                    // can only be false when the user does something really funky.
                    setCurrentInstance(target);
                    const res = callWithAsyncErrorHandling(hook, target, type, args);
                    unsetCurrentInstance();
                    resetTracking();
                    return res;
                });
            if (prepend) {
                hooks.unshift(wrappedHook);
            }
            else {
                hooks.push(wrappedHook);
            }
            return wrappedHook;
        }
        else {
            const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/, ''));
            warn$1(`${apiName} is called when there is no active component instance to be ` +
                `associated with. ` +
                `Lifecycle injection APIs can only be used during execution of setup().` +
                (` If you are using async setup(), make sure to register lifecycle ` +
                        `hooks before the first await statement.`
                    ));
        }
    }
    const createHook = (lifecycle) => (hook, target = currentInstance) => 
    // post-create lifecycle registrations are noops during SSR (except for serverPrefetch)
    (!isInSSRComponentSetup || lifecycle === "sp" /* SERVER_PREFETCH */) &&
        injectHook(lifecycle, hook, target);
    const onBeforeMount = createHook("bm" /* BEFORE_MOUNT */);
    const onMounted = createHook("m" /* MOUNTED */);
    const onBeforeUpdate = createHook("bu" /* BEFORE_UPDATE */);
    const onUpdated = createHook("u" /* UPDATED */);
    const onBeforeUnmount = createHook("bum" /* BEFORE_UNMOUNT */);
    const onUnmounted = createHook("um" /* UNMOUNTED */);
    const onServerPrefetch = createHook("sp" /* SERVER_PREFETCH */);
    const onRenderTriggered = createHook("rtg" /* RENDER_TRIGGERED */);
    const onRenderTracked = createHook("rtc" /* RENDER_TRACKED */);
    function onErrorCaptured(hook, target = currentInstance) {
        injectHook("ec" /* ERROR_CAPTURED */, hook, target);
    }

    /**
    Runtime helper for applying directives to a vnode. Example usage:

    const comp = resolveComponent('comp')
    const foo = resolveDirective('foo')
    const bar = resolveDirective('bar')

    return withDirectives(h(comp), [
      [foo, this.x],
      [bar, this.y]
    ])
    */
    function validateDirectiveName(name) {
        if (isBuiltInDirective(name)) {
            warn$1('Do not use built-in directive ids as custom directive id: ' + name);
        }
    }
    /**
     * Adds directives to a VNode.
     */
    function withDirectives(vnode, directives) {
        const internalInstance = currentRenderingInstance;
        if (internalInstance === null) {
            warn$1(`withDirectives can only be used inside render functions.`);
            return vnode;
        }
        const instance = getExposeProxy(internalInstance) ||
            internalInstance.proxy;
        const bindings = vnode.dirs || (vnode.dirs = []);
        for (let i = 0; i < directives.length; i++) {
            let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i];
            if (isFunction(dir)) {
                dir = {
                    mounted: dir,
                    updated: dir
                };
            }
            if (dir.deep) {
                traverse(value);
            }
            bindings.push({
                dir,
                instance,
                value,
                oldValue: void 0,
                arg,
                modifiers
            });
        }
        return vnode;
    }
    function invokeDirectiveHook(vnode, prevVNode, instance, name) {
        const bindings = vnode.dirs;
        const oldBindings = prevVNode && prevVNode.dirs;
        for (let i = 0; i < bindings.length; i++) {
            const binding = bindings[i];
            if (oldBindings) {
                binding.oldValue = oldBindings[i].value;
            }
            let hook = binding.dir[name];
            if (hook) {
                // disable tracking inside all lifecycle hooks
                // since they can potentially be called inside effects.
                pauseTracking();
                callWithAsyncErrorHandling(hook, instance, 8 /* DIRECTIVE_HOOK */, [
                    vnode.el,
                    binding,
                    vnode,
                    prevVNode
                ]);
                resetTracking();
            }
        }
    }

    const COMPONENTS = 'components';
    const DIRECTIVES = 'directives';
    /**
     * @private
     */
    function resolveComponent(name, maybeSelfReference) {
        return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name;
    }
    const NULL_DYNAMIC_COMPONENT = Symbol();
    /**
     * @private
     */
    function resolveDynamicComponent(component) {
        if (isString(component)) {
            return resolveAsset(COMPONENTS, component, false) || component;
        }
        else {
            // invalid types will fallthrough to createVNode and raise warning
            return (component || NULL_DYNAMIC_COMPONENT);
        }
    }
    /**
     * @private
     */
    function resolveDirective(name) {
        return resolveAsset(DIRECTIVES, name);
    }
    // implementation
    function resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) {
        const instance = currentRenderingInstance || currentInstance;
        if (instance) {
            const Component = instance.type;
            // explicit self name has highest priority
            if (type === COMPONENTS) {
                const selfName = getComponentName(Component, false /* do not include inferred name to avoid breaking existing code */);
                if (selfName &&
                    (selfName === name ||
                        selfName === camelize(name) ||
                        selfName === capitalize(camelize(name)))) {
                    return Component;
                }
            }
            const res = 
            // local registration
            // check instance[type] first which is resolved for options API
            resolve(instance[type] || Component[type], name) ||
                // global registration
                resolve(instance.appContext[type], name);
            if (!res && maybeSelfReference) {
                // fallback to implicit self-reference
                return Component;
            }
            if (warnMissing && !res) {
                const extra = type === COMPONENTS
                    ? `\nIf this is a native custom element, make sure to exclude it from ` +
                        `component resolution via compilerOptions.isCustomElement.`
                    : ``;
                warn$1(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`);
            }
            return res;
        }
        else {
            warn$1(`resolve${capitalize(type.slice(0, -1))} ` +
                `can only be used in render() or setup().`);
        }
    }
    function resolve(registry, name) {
        return (registry &&
            (registry[name] ||
                registry[camelize(name)] ||
                registry[capitalize(camelize(name))]));
    }

    /**
     * Actual implementation
     */
    function renderList(source, renderItem, cache, index) {
        let ret;
        const cached = (cache && cache[index]);
        if (isArray(source) || isString(source)) {
            ret = new Array(source.length);
            for (let i = 0, l = source.length; i < l; i++) {
                ret[i] = renderItem(source[i], i, undefined, cached && cached[i]);
            }
        }
        else if (typeof source === 'number') {
            if (!Number.isInteger(source)) {
                warn$1(`The v-for range expect an integer value but got ${source}.`);
            }
            ret = new Array(source);
            for (let i = 0; i < source; i++) {
                ret[i] = renderItem(i + 1, i, undefined, cached && cached[i]);
            }
        }
        else if (isObject(source)) {
            if (source[Symbol.iterator]) {
                ret = Array.from(source, (item, i) => renderItem(item, i, undefined, cached && cached[i]));
            }
            else {
                const keys = Object.keys(source);
                ret = new Array(keys.length);
                for (let i = 0, l = keys.length; i < l; i++) {
                    const key = keys[i];
                    ret[i] = renderItem(source[key], key, i, cached && cached[i]);
                }
            }
        }
        else {
            ret = [];
        }
        if (cache) {
            cache[index] = ret;
        }
        return ret;
    }

    /**
     * Compiler runtime helper for creating dynamic slots object
     * @private
     */
    function createSlots(slots, dynamicSlots) {
        for (let i = 0; i < dynamicSlots.length; i++) {
            const slot = dynamicSlots[i];
            // array of dynamic slot generated by <template v-for="..." #[...]>
            if (isArray(slot)) {
                for (let j = 0; j < slot.length; j++) {
                    slots[slot[j].name] = slot[j].fn;
                }
            }
            else if (slot) {
                // conditional single slot generated by <template v-if="..." #foo>
                slots[slot.name] = slot.fn;
            }
        }
        return slots;
    }

    /**
     * Compiler runtime helper for rendering `<slot/>`
     * @private
     */
    function renderSlot(slots, name, props = {}, 
    // this is not a user-facing function, so the fallback is always generated by
    // the compiler and guaranteed to be a function returning an array
    fallback, noSlotted) {
        if (currentRenderingInstance.isCE ||
            (currentRenderingInstance.parent &&
                isAsyncWrapper(currentRenderingInstance.parent) &&
                currentRenderingInstance.parent.isCE)) {
            return createVNode('slot', name === 'default' ? null : { name }, fallback && fallback());
        }
        let slot = slots[name];
        if (slot && slot.length > 1) {
            warn$1(`SSR-optimized slot function detected in a non-SSR-optimized render ` +
                `function. You need to mark this component with $dynamic-slots in the ` +
                `parent template.`);
            slot = () => [];
        }
        // a compiled slot disables block tracking by default to avoid manual
        // invocation interfering with template-based block tracking, but in
        // `renderSlot` we can be sure that it's template-based so we can force
        // enable it.
        if (slot && slot._c) {
            slot._d = false;
        }
        openBlock();
        const validSlotContent = slot && ensureValidVNode(slot(props));
        const rendered = createBlock(Fragment, { key: props.key || `_${name}` }, validSlotContent || (fallback ? fallback() : []), validSlotContent && slots._ === 1 /* STABLE */
            ? 64 /* STABLE_FRAGMENT */
            : -2 /* BAIL */);
        if (!noSlotted && rendered.scopeId) {
            rendered.slotScopeIds = [rendered.scopeId + '-s'];
        }
        if (slot && slot._c) {
            slot._d = true;
        }
        return rendered;
    }
    function ensureValidVNode(vnodes) {
        return vnodes.some(child => {
            if (!isVNode(child))
                return true;
            if (child.type === Comment)
                return false;
            if (child.type === Fragment &&
                !ensureValidVNode(child.children))
                return false;
            return true;
        })
            ? vnodes
            : null;
    }

    /**
     * For prefixing keys in v-on="obj" with "on"
     * @private
     */
    function toHandlers(obj) {
        const ret = {};
        if (!isObject(obj)) {
            warn$1(`v-on with no argument expects an object value.`);
            return ret;
        }
        for (const key in obj) {
            ret[toHandlerKey(key)] = obj[key];
        }
        return ret;
    }

    /**
     * #2437 In Vue 3, functional components do not have a public instance proxy but
     * they exist in the internal parent chain. For code that relies on traversing
     * public $parent chains, skip functional ones and go to the parent instead.
     */
    const getPublicInstance = (i) => {
        if (!i)
            return null;
        if (isStatefulComponent(i))
            return getExposeProxy(i) || i.proxy;
        return getPublicInstance(i.parent);
    };
    const publicPropertiesMap = 
    // Move PURE marker to new line to workaround compiler discarding it
    // due to type annotation
    /*#__PURE__*/ extend(Object.create(null), {
        $: i => i,
        $el: i => i.vnode.el,
        $data: i => i.data,
        $props: i => (shallowReadonly(i.props) ),
        $attrs: i => (shallowReadonly(i.attrs) ),
        $slots: i => (shallowReadonly(i.slots) ),
        $refs: i => (shallowReadonly(i.refs) ),
        $parent: i => getPublicInstance(i.parent),
        $root: i => getPublicInstance(i.root),
        $emit: i => i.emit,
        $options: i => (resolveMergedOptions(i) ),
        $forceUpdate: i => i.f || (i.f = () => queueJob(i.update)),
        $nextTick: i => i.n || (i.n = nextTick.bind(i.proxy)),
        $watch: i => (instanceWatch.bind(i) )
    });
    const isReservedPrefix = (key) => key === '_' || key === '$';
    const PublicInstanceProxyHandlers = {
        get({ _: instance }, key) {
            const { ctx, setupState, data, props, accessCache, type, appContext } = instance;
            // for internal formatters to know that this is a Vue instance
            if (key === '__isVue') {
                return true;
            }
            // prioritize <script setup> bindings during dev.
            // this allows even properties that start with _ or $ to be used - so that
            // it aligns with the production behavior where the render fn is inlined and
            // indeed has access to all declared variables.
            if (setupState !== EMPTY_OBJ &&
                setupState.__isScriptSetup &&
                hasOwn(setupState, key)) {
                return setupState[key];
            }
            // data / props / ctx
            // This getter gets called for every property access on the render context
            // during render and is a major hotspot. The most expensive part of this
            // is the multiple hasOwn() calls. It's much faster to do a simple property
            // access on a plain object, so we use an accessCache object (with null
            // prototype) to memoize what access type a key corresponds to.
            let normalizedProps;
            if (key[0] !== '$') {
                const n = accessCache[key];
                if (n !== undefined) {
                    switch (n) {
                        case 1 /* SETUP */:
                            return setupState[key];
                        case 2 /* DATA */:
                            return data[key];
                        case 4 /* CONTEXT */:
                            return ctx[key];
                        case 3 /* PROPS */:
                            return props[key];
                        // default: just fallthrough
                    }
                }
                else if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
                    accessCache[key] = 1 /* SETUP */;
                    return setupState[key];
                }
                else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
                    accessCache[key] = 2 /* DATA */;
                    return data[key];
                }
                else if (
                // only cache other properties when instance has declared (thus stable)
                // props
                (normalizedProps = instance.propsOptions[0]) &&
                    hasOwn(normalizedProps, key)) {
                    accessCache[key] = 3 /* PROPS */;
                    return props[key];
                }
                else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {
                    accessCache[key] = 4 /* CONTEXT */;
                    return ctx[key];
                }
                else if (shouldCacheAccess) {
                    accessCache[key] = 0 /* OTHER */;
                }
            }
            const publicGetter = publicPropertiesMap[key];
            let cssModule, globalProperties;
            // public $xxx properties
            if (publicGetter) {
                if (key === '$attrs') {
                    track(instance, "get" /* GET */, key);
                    markAttrsAccessed();
                }
                return publicGetter(instance);
            }
            else if (
            // css module (injected by vue-loader)
            (cssModule = type.__cssModules) &&
                (cssModule = cssModule[key])) {
                return cssModule;
            }
            else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {
                // user may set custom properties to `this` that start with `$`
                accessCache[key] = 4 /* CONTEXT */;
                return ctx[key];
            }
            else if (
            // global properties
            ((globalProperties = appContext.config.globalProperties),
                hasOwn(globalProperties, key))) {
                {
                    return globalProperties[key];
                }
            }
            else if (currentRenderingInstance &&
                (!isString(key) ||
                    // #1091 avoid internal isRef/isVNode checks on component instance leading
                    // to infinite warning loop
                    key.indexOf('__v') !== 0)) {
                if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn(data, key)) {
                    warn$1(`Property ${JSON.stringify(key)} must be accessed via $data because it starts with a reserved ` +
                        `character ("$" or "_") and is not proxied on the render context.`);
                }
                else if (instance === currentRenderingInstance) {
                    warn$1(`Property ${JSON.stringify(key)} was accessed during render ` +
                        `but is not defined on instance.`);
                }
            }
        },
        set({ _: instance }, key, value) {
            const { data, setupState, ctx } = instance;
            if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
                setupState[key] = value;
                return true;
            }
            else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
                data[key] = value;
                return true;
            }
            else if (hasOwn(instance.props, key)) {
                warn$1(`Attempting to mutate prop "${key}". Props are readonly.`, instance);
                return false;
            }
            if (key[0] === '$' && key.slice(1) in instance) {
                warn$1(`Attempting to mutate public property "${key}". ` +
                        `Properties starting with $ are reserved and readonly.`, instance);
                return false;
            }
            else {
                if (key in instance.appContext.config.globalProperties) {
                    Object.defineProperty(ctx, key, {
                        enumerable: true,
                        configurable: true,
                        value
                    });
                }
                else {
                    ctx[key] = value;
                }
            }
            return true;
        },
        has({ _: { data, setupState, accessCache, ctx, appContext, propsOptions } }, key) {
            let normalizedProps;
            return (!!accessCache[key] ||
                (data !== EMPTY_OBJ && hasOwn(data, key)) ||
                (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
                ((normalizedProps = propsOptions[0]) && hasOwn(normalizedProps, key)) ||
                hasOwn(ctx, key) ||
                hasOwn(publicPropertiesMap, key) ||
                hasOwn(appContext.config.globalProperties, key));
        },
        defineProperty(target, key, descriptor) {
            if (descriptor.get != null) {
                // invalidate key cache of a getter based property #5417
                target._.accessCache[key] = 0;
            }
            else if (hasOwn(descriptor, 'value')) {
                this.set(target, key, descriptor.value, null);
            }
            return Reflect.defineProperty(target, key, descriptor);
        }
    };
    {
        PublicInstanceProxyHandlers.ownKeys = (target) => {
            warn$1(`Avoid app logic that relies on enumerating keys on a component instance. ` +
                `The keys will be empty in production mode to avoid performance overhead.`);
            return Reflect.ownKeys(target);
        };
    }
    const RuntimeCompiledPublicInstanceProxyHandlers = /*#__PURE__*/ extend({}, PublicInstanceProxyHandlers, {
        get(target, key) {
            // fast path for unscopables when using `with` block
            if (key === Symbol.unscopables) {
                return;
            }
            return PublicInstanceProxyHandlers.get(target, key, target);
        },
        has(_, key) {
            const has = key[0] !== '_' && !isGloballyWhitelisted(key);
            if (!has && PublicInstanceProxyHandlers.has(_, key)) {
                warn$1(`Property ${JSON.stringify(key)} should not start with _ which is a reserved prefix for Vue internals.`);
            }
            return has;
        }
    });
    // dev only
    // In dev mode, the proxy target exposes the same properties as seen on `this`
    // for easier console inspection. In prod mode it will be an empty object so
    // these properties definitions can be skipped.
    function createDevRenderContext(instance) {
        const target = {};
        // expose internal instance for proxy handlers
        Object.defineProperty(target, `_`, {
            configurable: true,
            enumerable: false,
            get: () => instance
        });
        // expose public properties
        Object.keys(publicPropertiesMap).forEach(key => {
            Object.defineProperty(target, key, {
                configurable: true,
                enumerable: false,
                get: () => publicPropertiesMap[key](instance),
                // intercepted by the proxy so no need for implementation,
                // but needed to prevent set errors
                set: NOOP
            });
        });
        return target;
    }
    // dev only
    function exposePropsOnRenderContext(instance) {
        const { ctx, propsOptions: [propsOptions] } = instance;
        if (propsOptions) {
            Object.keys(propsOptions).forEach(key => {
                Object.defineProperty(ctx, key, {
                    enumerable: true,
                    configurable: true,
                    get: () => instance.props[key],
                    set: NOOP
                });
            });
        }
    }
    // dev only
    function exposeSetupStateOnRenderContext(instance) {
        const { ctx, setupState } = instance;
        Object.keys(toRaw(setupState)).forEach(key => {
            if (!setupState.__isScriptSetup) {
                if (isReservedPrefix(key[0])) {
                    warn$1(`setup() return property ${JSON.stringify(key)} should not start with "$" or "_" ` +
                        `which are reserved prefixes for Vue internals.`);
                    return;
                }
                Object.defineProperty(ctx, key, {
                    enumerable: true,
                    configurable: true,
                    get: () => setupState[key],
                    set: NOOP
                });
            }
        });
    }

    function createDuplicateChecker() {
        const cache = Object.create(null);
        return (type, key) => {
            if (cache[key]) {
                warn$1(`${type} property "${key}" is already defined in ${cache[key]}.`);
            }
            else {
                cache[key] = type;
            }
        };
    }
    let shouldCacheAccess = true;
    function applyOptions(instance) {
        const options = resolveMergedOptions(instance);
        const publicThis = instance.proxy;
        const ctx = instance.ctx;
        // do not cache property access on public proxy during state initialization
        shouldCacheAccess = false;
        // call beforeCreate first before accessing other options since
        // the hook may mutate resolved options (#2791)
        if (options.beforeCreate) {
            callHook(options.beforeCreate, instance, "bc" /* BEFORE_CREATE */);
        }
        const { 
        // state
        data: dataOptions, computed: computedOptions, methods, watch: watchOptions, provide: provideOptions, inject: injectOptions, 
        // lifecycle
        created, beforeMount, mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy, beforeUnmount, destroyed, unmounted, render, renderTracked, renderTriggered, errorCaptured, serverPrefetch, 
        // public API
        expose, inheritAttrs, 
        // assets
        components, directives, filters } = options;
        const checkDuplicateProperties = createDuplicateChecker() ;
        {
            const [propsOptions] = instance.propsOptions;
            if (propsOptions) {
                for (const key in propsOptions) {
                    checkDuplicateProperties("Props" /* PROPS */, key);
                }
            }
        }
        // options initialization order (to be consistent with Vue 2):
        // - props (already done outside of this function)
        // - inject
        // - methods
        // - data (deferred since it relies on `this` access)
        // - computed
        // - watch (deferred since it relies on `this` access)
        if (injectOptions) {
            resolveInjections(injectOptions, ctx, checkDuplicateProperties, instance.appContext.config.unwrapInjectedRef);
        }
        if (methods) {
            for (const key in methods) {
                const methodHandler = methods[key];
                if (isFunction(methodHandler)) {
                    // In dev mode, we use the `createRenderContext` function to define
                    // methods to the proxy target, and those are read-only but
                    // reconfigurable, so it needs to be redefined here
                    {
                        Object.defineProperty(ctx, key, {
                            value: methodHandler.bind(publicThis),
                            configurable: true,
                            enumerable: true,
                            writable: true
                        });
                    }
                    {
                        checkDuplicateProperties("Methods" /* METHODS */, key);
                    }
                }
                else {
                    warn$1(`Method "${key}" has type "${typeof methodHandler}" in the component definition. ` +
                        `Did you reference the function correctly?`);
                }
            }
        }
        if (dataOptions) {
            if (!isFunction(dataOptions)) {
                warn$1(`The data option must be a function. ` +
                    `Plain object usage is no longer supported.`);
            }
            const data = dataOptions.call(publicThis, publicThis);
            if (isPromise(data)) {
                warn$1(`data() returned a Promise - note data() cannot be async; If you ` +
                    `intend to perform data fetching before component renders, use ` +
                    `async setup() + <Suspense>.`);
            }
            if (!isObject(data)) {
                warn$1(`data() should return an object.`);
            }
            else {
                instance.data = reactive(data);
                {
                    for (const key in data) {
                        checkDuplicateProperties("Data" /* DATA */, key);
                        // expose data on ctx during dev
                        if (!isReservedPrefix(key[0])) {
                            Object.defineProperty(ctx, key, {
                                configurable: true,
                                enumerable: true,
                                get: () => data[key],
                                set: NOOP
                            });
                        }
                    }
                }
            }
        }
        // state initialization complete at this point - start caching access
        shouldCacheAccess = true;
        if (computedOptions) {
            for (const key in computedOptions) {
                const opt = computedOptions[key];
                const get = isFunction(opt)
                    ? opt.bind(publicThis, publicThis)
                    : isFunction(opt.get)
                        ? opt.get.bind(publicThis, publicThis)
                        : NOOP;
                if (get === NOOP) {
                    warn$1(`Computed property "${key}" has no getter.`);
                }
                const set = !isFunction(opt) && isFunction(opt.set)
                    ? opt.set.bind(publicThis)
                    : () => {
                            warn$1(`Write operation failed: computed property "${key}" is readonly.`);
                        }
                        ;
                const c = computed$1({
                    get,
                    set
                });
                Object.defineProperty(ctx, key, {
                    enumerable: true,
                    configurable: true,
                    get: () => c.value,
                    set: v => (c.value = v)
                });
                {
                    checkDuplicateProperties("Computed" /* COMPUTED */, key);
                }
            }
        }
        if (watchOptions) {
            for (const key in watchOptions) {
                createWatcher(watchOptions[key], ctx, publicThis, key);
            }
        }
        if (provideOptions) {
            const provides = isFunction(provideOptions)
                ? provideOptions.call(publicThis)
                : provideOptions;
            Reflect.ownKeys(provides).forEach(key => {
                provide(key, provides[key]);
            });
        }
        if (created) {
            callHook(created, instance, "c" /* CREATED */);
        }
        function registerLifecycleHook(register, hook) {
            if (isArray(hook)) {
                hook.forEach(_hook => register(_hook.bind(publicThis)));
            }
            else if (hook) {
                register(hook.bind(publicThis));
            }
        }
        registerLifecycleHook(onBeforeMount, beforeMount);
        registerLifecycleHook(onMounted, mounted);
        registerLifecycleHook(onBeforeUpdate, beforeUpdate);
        registerLifecycleHook(onUpdated, updated);
        registerLifecycleHook(onActivated, activated);
        registerLifecycleHook(onDeactivated, deactivated);
        registerLifecycleHook(onErrorCaptured, errorCaptured);
        registerLifecycleHook(onRenderTracked, renderTracked);
        registerLifecycleHook(onRenderTriggered, renderTriggered);
        registerLifecycleHook(onBeforeUnmount, beforeUnmount);
        registerLifecycleHook(onUnmounted, unmounted);
        registerLifecycleHook(onServerPrefetch, serverPrefetch);
        if (isArray(expose)) {
            if (expose.length) {
                const exposed = instance.exposed || (instance.exposed = {});
                expose.forEach(key => {
                    Object.defineProperty(exposed, key, {
                        get: () => publicThis[key],
                        set: val => (publicThis[key] = val)
                    });
                });
            }
            else if (!instance.exposed) {
                instance.exposed = {};
            }
        }
        // options that are handled when creating the instance but also need to be
        // applied from mixins
        if (render && instance.render === NOOP) {
            instance.render = render;
        }
        if (inheritAttrs != null) {
            instance.inheritAttrs = inheritAttrs;
        }
        // asset options.
        if (components)
            instance.components = components;
        if (directives)
            instance.directives = directives;
    }
    function resolveInjections(injectOptions, ctx, checkDuplicateProperties = NOOP, unwrapRef = false) {
        if (isArray(injectOptions)) {
            injectOptions = normalizeInject(injectOptions);
        }
        for (const key in injectOptions) {
            const opt = injectOptions[key];
            let injected;
            if (isObject(opt)) {
                if ('default' in opt) {
                    injected = inject(opt.from || key, opt.default, true /* treat default function as factory */);
                }
                else {
                    injected = inject(opt.from || key);
                }
            }
            else {
                injected = inject(opt);
            }
            if (isRef(injected)) {
                // TODO remove the check in 3.3
                if (unwrapRef) {
                    Object.defineProperty(ctx, key, {
                        enumerable: true,
                        configurable: true,
                        get: () => injected.value,
                        set: v => (injected.value = v)
                    });
                }
                else {
                    {
                        warn$1(`injected property "${key}" is a ref and will be auto-unwrapped ` +
                            `and no longer needs \`.value\` in the next minor release. ` +
                            `To opt-in to the new behavior now, ` +
                            `set \`app.config.unwrapInjectedRef = true\` (this config is ` +
                            `temporary and will not be needed in the future.)`);
                    }
                    ctx[key] = injected;
                }
            }
            else {
                ctx[key] = injected;
            }
            {
                checkDuplicateProperties("Inject" /* INJECT */, key);
            }
        }
    }
    function callHook(hook, instance, type) {
        callWithAsyncErrorHandling(isArray(hook)
            ? hook.map(h => h.bind(instance.proxy))
            : hook.bind(instance.proxy), instance, type);
    }
    function createWatcher(raw, ctx, publicThis, key) {
        const getter = key.includes('.')
            ? createPathGetter(publicThis, key)
            : () => publicThis[key];
        if (isString(raw)) {
            const handler = ctx[raw];
            if (isFunction(handler)) {
                watch(getter, handler);
            }
            else {
                warn$1(`Invalid watch handler specified by key "${raw}"`, handler);
            }
        }
        else if (isFunction(raw)) {
            watch(getter, raw.bind(publicThis));
        }
        else if (isObject(raw)) {
            if (isArray(raw)) {
                raw.forEach(r => createWatcher(r, ctx, publicThis, key));
            }
            else {
                const handler = isFunction(raw.handler)
                    ? raw.handler.bind(publicThis)
                    : ctx[raw.handler];
                if (isFunction(handler)) {
                    watch(getter, handler, raw);
                }
                else {
                    warn$1(`Invalid watch handler specified by key "${raw.handler}"`, handler);
                }
            }
        }
        else {
            warn$1(`Invalid watch option: "${key}"`, raw);
        }
    }
    /**
     * Resolve merged options and cache it on the component.
     * This is done only once per-component since the merging does not involve
     * instances.
     */
    function resolveMergedOptions(instance) {
        const base = instance.type;
        const { mixins, extends: extendsOptions } = base;
        const { mixins: globalMixins, optionsCache: cache, config: { optionMergeStrategies } } = instance.appContext;
        const cached = cache.get(base);
        let resolved;
        if (cached) {
            resolved = cached;
        }
        else if (!globalMixins.length && !mixins && !extendsOptions) {
            {
                resolved = base;
            }
        }
        else {
            resolved = {};
            if (globalMixins.length) {
                globalMixins.forEach(m => mergeOptions(resolved, m, optionMergeStrategies, true));
            }
            mergeOptions(resolved, base, optionMergeStrategies);
        }
        cache.set(base, resolved);
        return resolved;
    }
    function mergeOptions(to, from, strats, asMixin = false) {
        const { mixins, extends: extendsOptions } = from;
        if (extendsOptions) {
            mergeOptions(to, extendsOptions, strats, true);
        }
        if (mixins) {
            mixins.forEach((m) => mergeOptions(to, m, strats, true));
        }
        for (const key in from) {
            if (asMixin && key === 'expose') {
                warn$1(`"expose" option is ignored when declared in mixins or extends. ` +
                        `It should only be declared in the base component itself.`);
            }
            else {
                const strat = internalOptionMergeStrats[key] || (strats && strats[key]);
                to[key] = strat ? strat(to[key], from[key]) : from[key];
            }
        }
        return to;
    }
    const internalOptionMergeStrats = {
        data: mergeDataFn,
        props: mergeObjectOptions,
        emits: mergeObjectOptions,
        // objects
        methods: mergeObjectOptions,
        computed: mergeObjectOptions,
        // lifecycle
        beforeCreate: mergeAsArray,
        created: mergeAsArray,
        beforeMount: mergeAsArray,
        mounted: mergeAsArray,
        beforeUpdate: mergeAsArray,
        updated: mergeAsArray,
        beforeDestroy: mergeAsArray,
        beforeUnmount: mergeAsArray,
        destroyed: mergeAsArray,
        unmounted: mergeAsArray,
        activated: mergeAsArray,
        deactivated: mergeAsArray,
        errorCaptured: mergeAsArray,
        serverPrefetch: mergeAsArray,
        // assets
        components: mergeObjectOptions,
        directives: mergeObjectOptions,
        // watch
        watch: mergeWatchOptions,
        // provide / inject
        provide: mergeDataFn,
        inject: mergeInject
    };
    function mergeDataFn(to, from) {
        if (!from) {
            return to;
        }
        if (!to) {
            return from;
        }
        return function mergedDataFn() {
            return (extend)(isFunction(to) ? to.call(this, this) : to, isFunction(from) ? from.call(this, this) : from);
        };
    }
    function mergeInject(to, from) {
        return mergeObjectOptions(normalizeInject(to), normalizeInject(from));
    }
    function normalizeInject(raw) {
        if (isArray(raw)) {
            const res = {};
            for (let i = 0; i < raw.length; i++) {
                res[raw[i]] = raw[i];
            }
            return res;
        }
        return raw;
    }
    function mergeAsArray(to, from) {
        return to ? [...new Set([].concat(to, from))] : from;
    }
    function mergeObjectOptions(to, from) {
        return to ? extend(extend(Object.create(null), to), from) : from;
    }
    function mergeWatchOptions(to, from) {
        if (!to)
            return from;
        if (!from)
            return to;
        const merged = extend(Object.create(null), to);
        for (const key in from) {
            merged[key] = mergeAsArray(to[key], from[key]);
        }
        return merged;
    }

    function initProps(instance, rawProps, isStateful, // result of bitwise flag comparison
    isSSR = false) {
        const props = {};
        const attrs = {};
        def(attrs, InternalObjectKey, 1);
        instance.propsDefaults = Object.create(null);
        setFullProps(instance, rawProps, props, attrs);
        // ensure all declared prop keys are present
        for (const key in instance.propsOptions[0]) {
            if (!(key in props)) {
                props[key] = undefined;
            }
        }
        // validation
        {
            validateProps(rawProps || {}, props, instance);
        }
        if (isStateful) {
            // stateful
            instance.props = isSSR ? props : shallowReactive(props);
        }
        else {
            if (!instance.type.props) {
                // functional w/ optional props, props === attrs
                instance.props = attrs;
            }
            else {
                // functional w/ declared props
                instance.props = props;
            }
        }
        instance.attrs = attrs;
    }
    function updateProps(instance, rawProps, rawPrevProps, optimized) {
        const { props, attrs, vnode: { patchFlag } } = instance;
        const rawCurrentProps = toRaw(props);
        const [options] = instance.propsOptions;
        let hasAttrsChanged = false;
        if (
        // always force full diff in dev
        // - #1942 if hmr is enabled with sfc component
        // - vite#872 non-sfc component used by sfc component
        !((instance.type.__hmrId ||
                (instance.parent && instance.parent.type.__hmrId))) &&
            (optimized || patchFlag > 0) &&
            !(patchFlag & 16 /* FULL_PROPS */)) {
            if (patchFlag & 8 /* PROPS */) {
                // Compiler-generated props & no keys change, just set the updated
                // the props.
                const propsToUpdate = instance.vnode.dynamicProps;
                for (let i = 0; i < propsToUpdate.length; i++) {
                    let key = propsToUpdate[i];
                    // skip if the prop key is a declared emit event listener
                    if (isEmitListener(instance.emitsOptions, key)) {
                        continue;
                    }
                    // PROPS flag guarantees rawProps to be non-null
                    const value = rawProps[key];
                    if (options) {
                        // attr / props separation was done on init and will be consistent
                        // in this code path, so just check if attrs have it.
                        if (hasOwn(attrs, key)) {
                            if (value !== attrs[key]) {
                                attrs[key] = value;
                                hasAttrsChanged = true;
                            }
                        }
                        else {
                            const camelizedKey = camelize(key);
                            props[camelizedKey] = resolvePropValue(options, rawCurrentProps, camelizedKey, value, instance, false /* isAbsent */);
                        }
                    }
                    else {
                        if (value !== attrs[key]) {
                            attrs[key] = value;
                            hasAttrsChanged = true;
                        }
                    }
                }
            }
        }
        else {
            // full props update.
            if (setFullProps(instance, rawProps, props, attrs)) {
                hasAttrsChanged = true;
            }
            // in case of dynamic props, check if we need to delete keys from
            // the props object
            let kebabKey;
            for (const key in rawCurrentProps) {
                if (!rawProps ||
                    // for camelCase
                    (!hasOwn(rawProps, key) &&
                        // it's possible the original props was passed in as kebab-case
                        // and converted to camelCase (#955)
                        ((kebabKey = hyphenate(key)) === key || !hasOwn(rawProps, kebabKey)))) {
                    if (options) {
                        if (rawPrevProps &&
                            // for camelCase
                            (rawPrevProps[key] !== undefined ||
                                // for kebab-case
                                rawPrevProps[kebabKey] !== undefined)) {
                            props[key] = resolvePropValue(options, rawCurrentProps, key, undefined, instance, true /* isAbsent */);
                        }
                    }
                    else {
                        delete props[key];
                    }
                }
            }
            // in the case of functional component w/o props declaration, props and
            // attrs point to the same object so it should already have been updated.
            if (attrs !== rawCurrentProps) {
                for (const key in attrs) {
                    if (!rawProps ||
                        (!hasOwn(rawProps, key) &&
                            (!false ))) {
                        delete attrs[key];
                        hasAttrsChanged = true;
                    }
                }
            }
        }
        // trigger updates for $attrs in case it's used in component slots
        if (hasAttrsChanged) {
            trigger(instance, "set" /* SET */, '$attrs');
        }
        {
            validateProps(rawProps || {}, props, instance);
        }
    }
    function setFullProps(instance, rawProps, props, attrs) {
        const [options, needCastKeys] = instance.propsOptions;
        let hasAttrsChanged = false;
        let rawCastValues;
        if (rawProps) {
            for (let key in rawProps) {
                // key, ref are reserved and never passed down
                if (isReservedProp(key)) {
                    continue;
                }
                const value = rawProps[key];
                // prop option names are camelized during normalization, so to support
                // kebab -> camel conversion here we need to camelize the key.
                let camelKey;
                if (options && hasOwn(options, (camelKey = camelize(key)))) {
                    if (!needCastKeys || !needCastKeys.includes(camelKey)) {
                        props[camelKey] = value;
                    }
                    else {
                        (rawCastValues || (rawCastValues = {}))[camelKey] = value;
                    }
                }
                else if (!isEmitListener(instance.emitsOptions, key)) {
                    if (!(key in attrs) || value !== attrs[key]) {
                        attrs[key] = value;
                        hasAttrsChanged = true;
                    }
                }
            }
        }
        if (needCastKeys) {
            const rawCurrentProps = toRaw(props);
            const castValues = rawCastValues || EMPTY_OBJ;
            for (let i = 0; i < needCastKeys.length; i++) {
                const key = needCastKeys[i];
                props[key] = resolvePropValue(options, rawCurrentProps, key, castValues[key], instance, !hasOwn(castValues, key));
            }
        }
        return hasAttrsChanged;
    }
    function resolvePropValue(options, props, key, value, instance, isAbsent) {
        const opt = options[key];
        if (opt != null) {
            const hasDefault = hasOwn(opt, 'default');
            // default values
            if (hasDefault && value === undefined) {
                const defaultValue = opt.default;
                if (opt.type !== Function && isFunction(defaultValue)) {
                    const { propsDefaults } = instance;
                    if (key in propsDefaults) {
                        value = propsDefaults[key];
                    }
                    else {
                        setCurrentInstance(instance);
                        value = propsDefaults[key] = defaultValue.call(null, props);
                        unsetCurrentInstance();
                    }
                }
                else {
                    value = defaultValue;
                }
            }
            // boolean casting
            if (opt[0 /* shouldCast */]) {
                if (isAbsent && !hasDefault) {
                    value = false;
                }
                else if (opt[1 /* shouldCastTrue */] &&
                    (value === '' || value === hyphenate(key))) {
                    value = true;
                }
            }
        }
        return value;
    }
    function normalizePropsOptions(comp, appContext, asMixin = false) {
        const cache = appContext.propsCache;
        const cached = cache.get(comp);
        if (cached) {
            return cached;
        }
        const raw = comp.props;
        const normalized = {};
        const needCastKeys = [];
        // apply mixin/extends props
        let hasExtends = false;
        if (!isFunction(comp)) {
            const extendProps = (raw) => {
                hasExtends = true;
                const [props, keys] = normalizePropsOptions(raw, appContext, true);
                extend(normalized, props);
                if (keys)
                    needCastKeys.push(...keys);
            };
            if (!asMixin && appContext.mixins.length) {
                appContext.mixins.forEach(extendProps);
            }
            if (comp.extends) {
                extendProps(comp.extends);
            }
            if (comp.mixins) {
                comp.mixins.forEach(extendProps);
            }
        }
        if (!raw && !hasExtends) {
            cache.set(comp, EMPTY_ARR);
            return EMPTY_ARR;
        }
        if (isArray(raw)) {
            for (let i = 0; i < raw.length; i++) {
                if (!isString(raw[i])) {
                    warn$1(`props must be strings when using array syntax.`, raw[i]);
                }
                const normalizedKey = camelize(raw[i]);
                if (validatePropName(normalizedKey)) {
                    normalized[normalizedKey] = EMPTY_OBJ;
                }
            }
        }
        else if (raw) {
            if (!isObject(raw)) {
                warn$1(`invalid props options`, raw);
            }
            for (const key in raw) {
                const normalizedKey = camelize(key);
                if (validatePropName(normalizedKey)) {
                    const opt = raw[key];
                    const prop = (normalized[normalizedKey] =
                        isArray(opt) || isFunction(opt) ? { type: opt } : opt);
                    if (prop) {
                        const booleanIndex = getTypeIndex(Boolean, prop.type);
                        const stringIndex = getTypeIndex(String, prop.type);
                        prop[0 /* shouldCast */] = booleanIndex > -1;
                        prop[1 /* shouldCastTrue */] =
                            stringIndex < 0 || booleanIndex < stringIndex;
                        // if the prop needs boolean casting or default value
                        if (booleanIndex > -1 || hasOwn(prop, 'default')) {
                            needCastKeys.push(normalizedKey);
                        }
                    }
                }
            }
        }
        const res = [normalized, needCastKeys];
        cache.set(comp, res);
        return res;
    }
    function validatePropName(key) {
        if (key[0] !== '$') {
            return true;
        }
        else {
            warn$1(`Invalid prop name: "${key}" is a reserved property.`);
        }
        return false;
    }
    // use function string name to check type constructors
    // so that it works across vms / iframes.
    function getType(ctor) {
        const match = ctor && ctor.toString().match(/^\s*function (\w+)/);
        return match ? match[1] : ctor === null ? 'null' : '';
    }
    function isSameType(a, b) {
        return getType(a) === getType(b);
    }
    function getTypeIndex(type, expectedTypes) {
        if (isArray(expectedTypes)) {
            return expectedTypes.findIndex(t => isSameType(t, type));
        }
        else if (isFunction(expectedTypes)) {
            return isSameType(expectedTypes, type) ? 0 : -1;
        }
        return -1;
    }
    /**
     * dev only
     */
    function validateProps(rawProps, props, instance) {
        const resolvedValues = toRaw(props);
        const options = instance.propsOptions[0];
        for (const key in options) {
            let opt = options[key];
            if (opt == null)
                continue;
            validateProp(key, resolvedValues[key], opt, !hasOwn(rawProps, key) && !hasOwn(rawProps, hyphenate(key)));
        }
    }
    /**
     * dev only
     */
    function validateProp(name, value, prop, isAbsent) {
        const { type, required, validator } = prop;
        // required!
        if (required && isAbsent) {
            warn$1('Missing required prop: "' + name + '"');
            return;
        }
        // missing but optional
        if (value == null && !prop.required) {
            return;
        }
        // type check
        if (type != null && type !== true) {
            let isValid = false;
            const types = isArray(type) ? type : [type];
            const expectedTypes = [];
            // value is valid as long as one of the specified types match
            for (let i = 0; i < types.length && !isValid; i++) {
                const { valid, expectedType } = assertType(value, types[i]);
                expectedTypes.push(expectedType || '');
                isValid = valid;
            }
            if (!isValid) {
                warn$1(getInvalidTypeMessage(name, value, expectedTypes));
                return;
            }
        }
        // custom validator
        if (validator && !validator(value)) {
            warn$1('Invalid prop: custom validator check failed for prop "' + name + '".');
        }
    }
    const isSimpleType = /*#__PURE__*/ makeMap('String,Number,Boolean,Function,Symbol,BigInt');
    /**
     * dev only
     */
    function assertType(value, type) {
        let valid;
        const expectedType = getType(type);
        if (isSimpleType(expectedType)) {
            const t = typeof value;
            valid = t === expectedType.toLowerCase();
            // for primitive wrapper objects
            if (!valid && t === 'object') {
                valid = value instanceof type;
            }
        }
        else if (expectedType === 'Object') {
            valid = isObject(value);
        }
        else if (expectedType === 'Array') {
            valid = isArray(value);
        }
        else if (expectedType === 'null') {
            valid = value === null;
        }
        else {
            valid = value instanceof type;
        }
        return {
            valid,
            expectedType
        };
    }
    /**
     * dev only
     */
    function getInvalidTypeMessage(name, value, expectedTypes) {
        let message = `Invalid prop: type check failed for prop "${name}".` +
            ` Expected ${expectedTypes.map(capitalize).join(' | ')}`;
        const expectedType = expectedTypes[0];
        const receivedType = toRawType(value);
        const expectedValue = styleValue(value, expectedType);
        const receivedValue = styleValue(value, receivedType);
        // check if we need to specify expected value
        if (expectedTypes.length === 1 &&
            isExplicable(expectedType) &&
            !isBoolean(expectedType, receivedType)) {
            message += ` with value ${expectedValue}`;
        }
        message += `, got ${receivedType} `;
        // check if we need to specify received value
        if (isExplicable(receivedType)) {
            message += `with value ${receivedValue}.`;
        }
        return message;
    }
    /**
     * dev only
     */
    function styleValue(value, type) {
        if (type === 'String') {
            return `"${value}"`;
        }
        else if (type === 'Number') {
            return `${Number(value)}`;
        }
        else {
            return `${value}`;
        }
    }
    /**
     * dev only
     */
    function isExplicable(type) {
        const explicitTypes = ['string', 'number', 'boolean'];
        return explicitTypes.some(elem => type.toLowerCase() === elem);
    }
    /**
     * dev only
     */
    function isBoolean(...args) {
        return args.some(elem => elem.toLowerCase() === 'boolean');
    }

    const isInternalKey = (key) => key[0] === '_' || key === '$stable';
    const normalizeSlotValue = (value) => isArray(value)
        ? value.map(normalizeVNode)
        : [normalizeVNode(value)];
    const normalizeSlot = (key, rawSlot, ctx) => {
        if (rawSlot._n) {
            // already normalized - #5353
            return rawSlot;
        }
        const normalized = withCtx((...args) => {
            if (currentInstance) {
                warn$1(`Slot "${key}" invoked outside of the render function: ` +
                    `this will not track dependencies used in the slot. ` +
                    `Invoke the slot function inside the render function instead.`);
            }
            return normalizeSlotValue(rawSlot(...args));
        }, ctx);
        normalized._c = false;
        return normalized;
    };
    const normalizeObjectSlots = (rawSlots, slots, instance) => {
        const ctx = rawSlots._ctx;
        for (const key in rawSlots) {
            if (isInternalKey(key))
                continue;
            const value = rawSlots[key];
            if (isFunction(value)) {
                slots[key] = normalizeSlot(key, value, ctx);
            }
            else if (value != null) {
                {
                    warn$1(`Non-function value encountered for slot "${key}". ` +
                        `Prefer function slots for better performance.`);
                }
                const normalized = normalizeSlotValue(value);
                slots[key] = () => normalized;
            }
        }
    };
    const normalizeVNodeSlots = (instance, children) => {
        if (!isKeepAlive(instance.vnode) &&
            !(false )) {
            warn$1(`Non-function value encountered for default slot. ` +
                `Prefer function slots for better performance.`);
        }
        const normalized = normalizeSlotValue(children);
        instance.slots.default = () => normalized;
    };
    const initSlots = (instance, children) => {
        if (instance.vnode.shapeFlag & 32 /* SLOTS_CHILDREN */) {
            const type = children._;
            if (type) {
                // users can get the shallow readonly version of the slots object through `this.$slots`,
                // we should avoid the proxy object polluting the slots of the internal instance
                instance.slots = toRaw(children);
                // make compiler marker non-enumerable
                def(children, '_', type);
            }
            else {
                normalizeObjectSlots(children, (instance.slots = {}));
            }
        }
        else {
            instance.slots = {};
            if (children) {
                normalizeVNodeSlots(instance, children);
            }
        }
        def(instance.slots, InternalObjectKey, 1);
    };
    const updateSlots = (instance, children, optimized) => {
        const { vnode, slots } = instance;
        let needDeletionCheck = true;
        let deletionComparisonTarget = EMPTY_OBJ;
        if (vnode.shapeFlag & 32 /* SLOTS_CHILDREN */) {
            const type = children._;
            if (type) {
                // compiled slots.
                if (isHmrUpdating) {
                    // Parent was HMR updated so slot content may have changed.
                    // force update slots and mark instance for hmr as well
                    extend(slots, children);
                }
                else if (optimized && type === 1 /* STABLE */) {
                    // compiled AND stable.
                    // no need to update, and skip stale slots removal.
                    needDeletionCheck = false;
                }
                else {
                    // compiled but dynamic (v-if/v-for on slots) - update slots, but skip
                    // normalization.
                    extend(slots, children);
                    // #2893
                    // when rendering the optimized slots by manually written render function,
                    // we need to delete the `slots._` flag if necessary to make subsequent updates reliable,
                    // i.e. let the `renderSlot` create the bailed Fragment
                    if (!optimized && type === 1 /* STABLE */) {
                        delete slots._;
                    }
                }
            }
            else {
                needDeletionCheck = !children.$stable;
                normalizeObjectSlots(children, slots);
            }
            deletionComparisonTarget = children;
        }
        else if (children) {
            // non slot object children (direct value) passed to a component
            normalizeVNodeSlots(instance, children);
            deletionComparisonTarget = { default: 1 };
        }
        // delete stale slots
        if (needDeletionCheck) {
            for (const key in slots) {
                if (!isInternalKey(key) && !(key in deletionComparisonTarget)) {
                    delete slots[key];
                }
            }
        }
    };

    function createAppContext() {
        return {
            app: null,
            config: {
                isNativeTag: NO,
                performance: false,
                globalProperties: {},
                optionMergeStrategies: {},
                errorHandler: undefined,
                warnHandler: undefined,
                compilerOptions: {}
            },
            mixins: [],
            components: {},
            directives: {},
            provides: Object.create(null),
            optionsCache: new WeakMap(),
            propsCache: new WeakMap(),
            emitsCache: new WeakMap()
        };
    }
    let uid = 0;
    function createAppAPI(render, hydrate) {
        return function createApp(rootComponent, rootProps = null) {
            if (!isFunction(rootComponent)) {
                rootComponent = Object.assign({}, rootComponent);
            }
            if (rootProps != null && !isObject(rootProps)) {
                warn$1(`root props passed to app.mount() must be an object.`);
                rootProps = null;
            }
            const context = createAppContext();
            const installedPlugins = new Set();
            let isMounted = false;
            const app = (context.app = {
                _uid: uid++,
                _component: rootComponent,
                _props: rootProps,
                _container: null,
                _context: context,
                _instance: null,
                version,
                get config() {
                    return context.config;
                },
                set config(v) {
                    {
                        warn$1(`app.config cannot be replaced. Modify individual options instead.`);
                    }
                },
                use(plugin, ...options) {
                    if (installedPlugins.has(plugin)) {
                        warn$1(`Plugin has already been applied to target app.`);
                    }
                    else if (plugin && isFunction(plugin.install)) {
                        installedPlugins.add(plugin);
                        plugin.install(app, ...options);
                    }
                    else if (isFunction(plugin)) {
                        installedPlugins.add(plugin);
                        plugin(app, ...options);
                    }
                    else {
                        warn$1(`A plugin must either be a function or an object with an "install" ` +
                            `function.`);
                    }
                    return app;
                },
                mixin(mixin) {
                    {
                        if (!context.mixins.includes(mixin)) {
                            context.mixins.push(mixin);
                        }
                        else {
                            warn$1('Mixin has already been applied to target app' +
                                (mixin.name ? `: ${mixin.name}` : ''));
                        }
                    }
                    return app;
                },
                component(name, component) {
                    {
                        validateComponentName(name, context.config);
                    }
                    if (!component) {
                        return context.components[name];
                    }
                    if (context.components[name]) {
                        warn$1(`Component "${name}" has already been registered in target app.`);
                    }
                    context.components[name] = component;
                    return app;
                },
                directive(name, directive) {
                    {
                        validateDirectiveName(name);
                    }
                    if (!directive) {
                        return context.directives[name];
                    }
                    if (context.directives[name]) {
                        warn$1(`Directive "${name}" has already been registered in target app.`);
                    }
                    context.directives[name] = directive;
                    return app;
                },
                mount(rootContainer, isHydrate, isSVG) {
                    if (!isMounted) {
                        // #5571
                        if (rootContainer.__vue_app__) {
                            warn$1(`There is already an app instance mounted on the host container.\n` +
                                ` If you want to mount another app on the same host container,` +
                                ` you need to unmount the previous app by calling \`app.unmount()\` first.`);
                        }
                        const vnode = createVNode(rootComponent, rootProps);
                        // store app context on the root VNode.
                        // this will be set on the root instance on initial mount.
                        vnode.appContext = context;
                        // HMR root reload
                        {
                            context.reload = () => {
                                render(cloneVNode(vnode), rootContainer, isSVG);
                            };
                        }
                        if (isHydrate && hydrate) {
                            hydrate(vnode, rootContainer);
                        }
                        else {
                            render(vnode, rootContainer, isSVG);
                        }
                        isMounted = true;
                        app._container = rootContainer;
                        rootContainer.__vue_app__ = app;
                        {
                            app._instance = vnode.component;
                            devtoolsInitApp(app, version);
                        }
                        return getExposeProxy(vnode.component) || vnode.component.proxy;
                    }
                    else {
                        warn$1(`App has already been mounted.\n` +
                            `If you want to remount the same app, move your app creation logic ` +
                            `into a factory function and create fresh app instances for each ` +
                            `mount - e.g. \`const createMyApp = () => createApp(App)\``);
                    }
                },
                unmount() {
                    if (isMounted) {
                        render(null, app._container);
                        {
                            app._instance = null;
                            devtoolsUnmountApp(app);
                        }
                        delete app._container.__vue_app__;
                    }
                    else {
                        warn$1(`Cannot unmount an app that is not mounted.`);
                    }
                },
                provide(key, value) {
                    if (key in context.provides) {
                        warn$1(`App already provides property with key "${String(key)}". ` +
                            `It will be overwritten with the new value.`);
                    }
                    context.provides[key] = value;
                    return app;
                }
            });
            return app;
        };
    }

    /**
     * Function for handling a template ref
     */
    function setRef(rawRef, oldRawRef, parentSuspense, vnode, isUnmount = false) {
        if (isArray(rawRef)) {
            rawRef.forEach((r, i) => setRef(r, oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef), parentSuspense, vnode, isUnmount));
            return;
        }
        if (isAsyncWrapper(vnode) && !isUnmount) {
            // when mounting async components, nothing needs to be done,
            // because the template ref is forwarded to inner component
            return;
        }
        const refValue = vnode.shapeFlag & 4 /* STATEFUL_COMPONENT */
            ? getExposeProxy(vnode.component) || vnode.component.proxy
            : vnode.el;
        const value = isUnmount ? null : refValue;
        const { i: owner, r: ref } = rawRef;
        if (!owner) {
            warn$1(`Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
                `A vnode with ref must be created inside the render function.`);
            return;
        }
        const oldRef = oldRawRef && oldRawRef.r;
        const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs;
        const setupState = owner.setupState;
        // dynamic ref changed. unset old ref
        if (oldRef != null && oldRef !== ref) {
            if (isString(oldRef)) {
                refs[oldRef] = null;
                if (hasOwn(setupState, oldRef)) {
                    setupState[oldRef] = null;
                }
            }
            else if (isRef(oldRef)) {
                oldRef.value = null;
            }
        }
        if (isFunction(ref)) {
            callWithErrorHandling(ref, owner, 12 /* FUNCTION_REF */, [value, refs]);
        }
        else {
            const _isString = isString(ref);
            const _isRef = isRef(ref);
            if (_isString || _isRef) {
                const doSet = () => {
                    if (rawRef.f) {
                        const existing = _isString ? refs[ref] : ref.value;
                        if (isUnmount) {
                            isArray(existing) && remove(existing, refValue);
                        }
                        else {
                            if (!isArray(existing)) {
                                if (_isString) {
                                    refs[ref] = [refValue];
                                    if (hasOwn(setupState, ref)) {
                                        setupState[ref] = refs[ref];
                                    }
                                }
                                else {
                                    ref.value = [refValue];
                                    if (rawRef.k)
                                        refs[rawRef.k] = ref.value;
                                }
                            }
                            else if (!existing.includes(refValue)) {
                                existing.push(refValue);
                            }
                        }
                    }
                    else if (_isString) {
                        refs[ref] = value;
                        if (hasOwn(setupState, ref)) {
                            setupState[ref] = value;
                        }
                    }
                    else if (_isRef) {
                        ref.value = value;
                        if (rawRef.k)
                            refs[rawRef.k] = value;
                    }
                    else {
                        warn$1('Invalid template ref type:', ref, `(${typeof ref})`);
                    }
                };
                if (value) {
                    doSet.id = -1;
                    queuePostRenderEffect(doSet, parentSuspense);
                }
                else {
                    doSet();
                }
            }
            else {
                warn$1('Invalid template ref type:', ref, `(${typeof ref})`);
            }
        }
    }

    let hasMismatch = false;
    const isSVGContainer = (container) => /svg/.test(container.namespaceURI) && container.tagName !== 'foreignObject';
    const isComment = (node) => node.nodeType === 8 /* COMMENT */;
    // Note: hydration is DOM-specific
    // But we have to place it in core due to tight coupling with core - splitting
    // it out creates a ton of unnecessary complexity.
    // Hydration also depends on some renderer internal logic which needs to be
    // passed in via arguments.
    function createHydrationFunctions(rendererInternals) {
        const { mt: mountComponent, p: patch, o: { patchProp, createText, nextSibling, parentNode, remove, insert, createComment } } = rendererInternals;
        const hydrate = (vnode, container) => {
            if (!container.hasChildNodes()) {
                warn$1(`Attempting to hydrate existing markup but container is empty. ` +
                        `Performing full mount instead.`);
                patch(null, vnode, container);
                flushPostFlushCbs();
                container._vnode = vnode;
                return;
            }
            hasMismatch = false;
            hydrateNode(container.firstChild, vnode, null, null, null);
            flushPostFlushCbs();
            container._vnode = vnode;
            if (hasMismatch && !false) {
                // this error should show up in production
                console.error(`Hydration completed but contains mismatches.`);
            }
        };
        const hydrateNode = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized = false) => {
            const isFragmentStart = isComment(node) && node.data === '[';
            const onMismatch = () => handleMismatch(node, vnode, parentComponent, parentSuspense, slotScopeIds, isFragmentStart);
            const { type, ref, shapeFlag, patchFlag } = vnode;
            const domType = node.nodeType;
            vnode.el = node;
            if (patchFlag === -2 /* BAIL */) {
                optimized = false;
                vnode.dynamicChildren = null;
            }
            let nextNode = null;
            switch (type) {
                case Text:
                    if (domType !== 3 /* TEXT */) {
                        // #5728 empty text node inside a slot can cause hydration failure
                        // because the server rendered HTML won't contain a text node
                        if (vnode.children === '') {
                            insert((vnode.el = createText('')), parentNode(node), node);
                            nextNode = node;
                        }
                        else {
                            nextNode = onMismatch();
                        }
                    }
                    else {
                        if (node.data !== vnode.children) {
                            hasMismatch = true;
                            warn$1(`Hydration text mismatch:` +
                                    `\n- Client: ${JSON.stringify(node.data)}` +
                                    `\n- Server: ${JSON.stringify(vnode.children)}`);
                            node.data = vnode.children;
                        }
                        nextNode = nextSibling(node);
                    }
                    break;
                case Comment:
                    if (domType !== 8 /* COMMENT */ || isFragmentStart) {
                        nextNode = onMismatch();
                    }
                    else {
                        nextNode = nextSibling(node);
                    }
                    break;
                case Static:
                    if (domType !== 1 /* ELEMENT */ && domType !== 3 /* TEXT */) {
                        nextNode = onMismatch();
                    }
                    else {
                        // determine anchor, adopt content
                        nextNode = node;
                        // if the static vnode has its content stripped during build,
                        // adopt it from the server-rendered HTML.
                        const needToAdoptContent = !vnode.children.length;
                        for (let i = 0; i < vnode.staticCount; i++) {
                            if (needToAdoptContent)
                                vnode.children +=
                                    nextNode.nodeType === 1 /* ELEMENT */
                                        ? nextNode.outerHTML
                                        : nextNode.data;
                            if (i === vnode.staticCount - 1) {
                                vnode.anchor = nextNode;
                            }
                            nextNode = nextSibling(nextNode);
                        }
                        return nextNode;
                    }
                    break;
                case Fragment:
                    if (!isFragmentStart) {
                        nextNode = onMismatch();
                    }
                    else {
                        nextNode = hydrateFragment(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized);
                    }
                    break;
                default:
                    if (shapeFlag & 1 /* ELEMENT */) {
                        if (domType !== 1 /* ELEMENT */ ||
                            vnode.type.toLowerCase() !==
                                node.tagName.toLowerCase()) {
                            nextNode = onMismatch();
                        }
                        else {
                            nextNode = hydrateElement(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized);
                        }
                    }
                    else if (shapeFlag & 6 /* COMPONENT */) {
                        // when setting up the render effect, if the initial vnode already
                        // has .el set, the component will perform hydration instead of mount
                        // on its sub-tree.
                        vnode.slotScopeIds = slotScopeIds;
                        const container = parentNode(node);
                        mountComponent(vnode, container, null, parentComponent, parentSuspense, isSVGContainer(container), optimized);
                        // component may be async, so in the case of fragments we cannot rely
                        // on component's rendered output to determine the end of the fragment
                        // instead, we do a lookahead to find the end anchor node.
                        nextNode = isFragmentStart
                            ? locateClosingAsyncAnchor(node)
                            : nextSibling(node);
                        // #4293 teleport as component root
                        if (nextNode &&
                            isComment(nextNode) &&
                            nextNode.data === 'teleport end') {
                            nextNode = nextSibling(nextNode);
                        }
                        // #3787
                        // if component is async, it may get moved / unmounted before its
                        // inner component is loaded, so we need to give it a placeholder
                        // vnode that matches its adopted DOM.
                        if (isAsyncWrapper(vnode)) {
                            let subTree;
                            if (isFragmentStart) {
                                subTree = createVNode(Fragment);
                                subTree.anchor = nextNode
                                    ? nextNode.previousSibling
                                    : container.lastChild;
                            }
                            else {
                                subTree =
                                    node.nodeType === 3 ? createTextVNode('') : createVNode('div');
                            }
                            subTree.el = node;
                            vnode.component.subTree = subTree;
                        }
                    }
                    else if (shapeFlag & 64 /* TELEPORT */) {
                        if (domType !== 8 /* COMMENT */) {
                            nextNode = onMismatch();
                        }
                        else {
                            nextNode = vnode.type.hydrate(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized, rendererInternals, hydrateChildren);
                        }
                    }
                    else if (shapeFlag & 128 /* SUSPENSE */) {
                        nextNode = vnode.type.hydrate(node, vnode, parentComponent, parentSuspense, isSVGContainer(parentNode(node)), slotScopeIds, optimized, rendererInternals, hydrateNode);
                    }
                    else {
                        warn$1('Invalid HostVNode type:', type, `(${typeof type})`);
                    }
            }
            if (ref != null) {
                setRef(ref, null, parentSuspense, vnode);
            }
            return nextNode;
        };
        const hydrateElement = (el, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => {
            optimized = optimized || !!vnode.dynamicChildren;
            const { type, props, patchFlag, shapeFlag, dirs } = vnode;
            // #4006 for form elements with non-string v-model value bindings
            // e.g. <option :value="obj">, <input type="checkbox" :true-value="1">
            const forcePatchValue = (type === 'input' && dirs) || type === 'option';
            // skip props & children if this is hoisted static nodes
            // #5405 in dev, always hydrate children for HMR
            {
                if (dirs) {
                    invokeDirectiveHook(vnode, null, parentComponent, 'created');
                }
                // props
                if (props) {
                    if (forcePatchValue ||
                        !optimized ||
                        patchFlag & (16 /* FULL_PROPS */ | 32 /* HYDRATE_EVENTS */)) {
                        for (const key in props) {
                            if ((forcePatchValue && key.endsWith('value')) ||
                                (isOn(key) && !isReservedProp(key))) {
                                patchProp(el, key, null, props[key], false, undefined, parentComponent);
                            }
                        }
                    }
                    else if (props.onClick) {
                        // Fast path for click listeners (which is most often) to avoid
                        // iterating through props.
                        patchProp(el, 'onClick', null, props.onClick, false, undefined, parentComponent);
                    }
                }
                // vnode / directive hooks
                let vnodeHooks;
                if ((vnodeHooks = props && props.onVnodeBeforeMount)) {
                    invokeVNodeHook(vnodeHooks, parentComponent, vnode);
                }
                if (dirs) {
                    invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount');
                }
                if ((vnodeHooks = props && props.onVnodeMounted) || dirs) {
                    queueEffectWithSuspense(() => {
                        vnodeHooks && invokeVNodeHook(vnodeHooks, parentComponent, vnode);
                        dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted');
                    }, parentSuspense);
                }
                // children
                if (shapeFlag & 16 /* ARRAY_CHILDREN */ &&
                    // skip if element has innerHTML / textContent
                    !(props && (props.innerHTML || props.textContent))) {
                    let next = hydrateChildren(el.firstChild, vnode, el, parentComponent, parentSuspense, slotScopeIds, optimized);
                    let hasWarned = false;
                    while (next) {
                        hasMismatch = true;
                        if (!hasWarned) {
                            warn$1(`Hydration children mismatch in <${vnode.type}>: ` +
                                `server rendered element contains more child nodes than client vdom.`);
                            hasWarned = true;
                        }
                        // The SSRed DOM contains more nodes than it should. Remove them.
                        const cur = next;
                        next = next.nextSibling;
                        remove(cur);
                    }
                }
                else if (shapeFlag & 8 /* TEXT_CHILDREN */) {
                    if (el.textContent !== vnode.children) {
                        hasMismatch = true;
                        warn$1(`Hydration text content mismatch in <${vnode.type}>:\n` +
                                `- Client: ${el.textContent}\n` +
                                `- Server: ${vnode.children}`);
                        el.textContent = vnode.children;
                    }
                }
            }
            return el.nextSibling;
        };
        const hydrateChildren = (node, parentVNode, container, parentComponent, parentSuspense, slotScopeIds, optimized) => {
            optimized = optimized || !!parentVNode.dynamicChildren;
            const children = parentVNode.children;
            const l = children.length;
            let hasWarned = false;
            for (let i = 0; i < l; i++) {
                const vnode = optimized
                    ? children[i]
                    : (children[i] = normalizeVNode(children[i]));
                if (node) {
                    node = hydrateNode(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized);
                }
                else if (vnode.type === Text && !vnode.children) {
                    continue;
                }
                else {
                    hasMismatch = true;
                    if (!hasWarned) {
                        warn$1(`Hydration children mismatch in <${container.tagName.toLowerCase()}>: ` +
                            `server rendered element contains fewer child nodes than client vdom.`);
                        hasWarned = true;
                    }
                    // the SSRed DOM didn't contain enough nodes. Mount the missing ones.
                    patch(null, vnode, container, null, parentComponent, parentSuspense, isSVGContainer(container), slotScopeIds);
                }
            }
            return node;
        };
        const hydrateFragment = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => {
            const { slotScopeIds: fragmentSlotScopeIds } = vnode;
            if (fragmentSlotScopeIds) {
                slotScopeIds = slotScopeIds
                    ? slotScopeIds.concat(fragmentSlotScopeIds)
                    : fragmentSlotScopeIds;
            }
            const container = parentNode(node);
            const next = hydrateChildren(nextSibling(node), vnode, container, parentComponent, parentSuspense, slotScopeIds, optimized);
            if (next && isComment(next) && next.data === ']') {
                return nextSibling((vnode.anchor = next));
            }
            else {
                // fragment didn't hydrate successfully, since we didn't get a end anchor
                // back. This should have led to node/children mismatch warnings.
                hasMismatch = true;
                // since the anchor is missing, we need to create one and insert it
                insert((vnode.anchor = createComment(`]`)), container, next);
                return next;
            }
        };
        const handleMismatch = (node, vnode, parentComponent, parentSuspense, slotScopeIds, isFragment) => {
            hasMismatch = true;
            warn$1(`Hydration node mismatch:\n- Client vnode:`, vnode.type, `\n- Server rendered DOM:`, node, node.nodeType === 3 /* TEXT */
                    ? `(text)`
                    : isComment(node) && node.data === '['
                        ? `(start of fragment)`
                        : ``);
            vnode.el = null;
            if (isFragment) {
                // remove excessive fragment nodes
                const end = locateClosingAsyncAnchor(node);
                while (true) {
                    const next = nextSibling(node);
                    if (next && next !== end) {
                        remove(next);
                    }
                    else {
                        break;
                    }
                }
            }
            const next = nextSibling(node);
            const container = parentNode(node);
            remove(node);
            patch(null, vnode, container, next, parentComponent, parentSuspense, isSVGContainer(container), slotScopeIds);
            return next;
        };
        const locateClosingAsyncAnchor = (node) => {
            let match = 0;
            while (node) {
                node = nextSibling(node);
                if (node && isComment(node)) {
                    if (node.data === '[')
                        match++;
                    if (node.data === ']') {
                        if (match === 0) {
                            return nextSibling(node);
                        }
                        else {
                            match--;
                        }
                    }
                }
            }
            return node;
        };
        return [hydrate, hydrateNode];
    }

    /* eslint-disable no-restricted-globals */
    let supported;
    let perf;
    function startMeasure(instance, type) {
        if (instance.appContext.config.performance && isSupported()) {
            perf.mark(`vue-${type}-${instance.uid}`);
        }
        {
            devtoolsPerfStart(instance, type, isSupported() ? perf.now() : Date.now());
        }
    }
    function endMeasure(instance, type) {
        if (instance.appContext.config.performance && isSupported()) {
            const startTag = `vue-${type}-${instance.uid}`;
            const endTag = startTag + `:end`;
            perf.mark(endTag);
            perf.measure(`<${formatComponentName(instance, instance.type)}> ${type}`, startTag, endTag);
            perf.clearMarks(startTag);
            perf.clearMarks(endTag);
        }
        {
            devtoolsPerfEnd(instance, type, isSupported() ? perf.now() : Date.now());
        }
    }
    function isSupported() {
        if (supported !== undefined) {
            return supported;
        }
        if (typeof window !== 'undefined' && window.performance) {
            supported = true;
            perf = window.performance;
        }
        else {
            supported = false;
        }
        return supported;
    }

    const queuePostRenderEffect = queueEffectWithSuspense
        ;
    /**
     * The createRenderer function accepts two generic arguments:
     * HostNode and HostElement, corresponding to Node and Element types in the
     * host environment. For example, for runtime-dom, HostNode would be the DOM
     * `Node` interface and HostElement would be the DOM `Element` interface.
     *
     * Custom renderers can pass in the platform specific types like this:
     *
     * ``` js
     * const { render, createApp } = createRenderer<Node, Element>({
     *   patchProp,
     *   ...nodeOps
     * })
     * ```
     */
    function createRenderer(options) {
        return baseCreateRenderer(options);
    }
    // Separate API for creating hydration-enabled renderer.
    // Hydration logic is only used when calling this function, making it
    // tree-shakable.
    function createHydrationRenderer(options) {
        return baseCreateRenderer(options, createHydrationFunctions);
    }
    // implementation
    function baseCreateRenderer(options, createHydrationFns) {
        const target = getGlobalThis();
        target.__VUE__ = true;
        {
            setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__, target);
        }
        const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, setScopeId: hostSetScopeId = NOOP, cloneNode: hostCloneNode, insertStaticContent: hostInsertStaticContent } = options;
        // Note: functions inside this closure should use `const xxx = () => {}`
        // style in order to prevent being inlined by minifiers.
        const patch = (n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, isSVG = false, slotScopeIds = null, optimized = isHmrUpdating ? false : !!n2.dynamicChildren) => {
            if (n1 === n2) {
                return;
            }
            // patching & not same type, unmount old tree
            if (n1 && !isSameVNodeType(n1, n2)) {
                anchor = getNextHostNode(n1);
                unmount(n1, parentComponent, parentSuspense, true);
                n1 = null;
            }
            if (n2.patchFlag === -2 /* BAIL */) {
                optimized = false;
                n2.dynamicChildren = null;
            }
            const { type, ref, shapeFlag } = n2;
            switch (type) {
                case Text:
                    processText(n1, n2, container, anchor);
                    break;
                case Comment:
                    processCommentNode(n1, n2, container, anchor);
                    break;
                case Static:
                    if (n1 == null) {
                        mountStaticNode(n2, container, anchor, isSVG);
                    }
                    else {
                        patchStaticNode(n1, n2, container, isSVG);
                    }
                    break;
                case Fragment:
                    processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    break;
                default:
                    if (shapeFlag & 1 /* ELEMENT */) {
                        processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    }
                    else if (shapeFlag & 6 /* COMPONENT */) {
                        processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    }
                    else if (shapeFlag & 64 /* TELEPORT */) {
                        type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, internals);
                    }
                    else if (shapeFlag & 128 /* SUSPENSE */) {
                        type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, internals);
                    }
                    else {
                        warn$1('Invalid VNode type:', type, `(${typeof type})`);
                    }
            }
            // set ref
            if (ref != null && parentComponent) {
                setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2);
            }
        };
        const processText = (n1, n2, container, anchor) => {
            if (n1 == null) {
                hostInsert((n2.el = hostCreateText(n2.children)), container, anchor);
            }
            else {
                const el = (n2.el = n1.el);
                if (n2.children !== n1.children) {
                    hostSetText(el, n2.children);
                }
            }
        };
        const processCommentNode = (n1, n2, container, anchor) => {
            if (n1 == null) {
                hostInsert((n2.el = hostCreateComment(n2.children || '')), container, anchor);
            }
            else {
                // there's no support for dynamic comments
                n2.el = n1.el;
            }
        };
        const mountStaticNode = (n2, container, anchor, isSVG) => {
            [n2.el, n2.anchor] = hostInsertStaticContent(n2.children, container, anchor, isSVG, n2.el, n2.anchor);
        };
        /**
         * Dev / HMR only
         */
        const patchStaticNode = (n1, n2, container, isSVG) => {
            // static nodes are only patched during dev for HMR
            if (n2.children !== n1.children) {
                const anchor = hostNextSibling(n1.anchor);
                // remove existing
                removeStaticNode(n1);
                [n2.el, n2.anchor] = hostInsertStaticContent(n2.children, container, anchor, isSVG);
            }
            else {
                n2.el = n1.el;
                n2.anchor = n1.anchor;
            }
        };
        const moveStaticNode = ({ el, anchor }, container, nextSibling) => {
            let next;
            while (el && el !== anchor) {
                next = hostNextSibling(el);
                hostInsert(el, container, nextSibling);
                el = next;
            }
            hostInsert(anchor, container, nextSibling);
        };
        const removeStaticNode = ({ el, anchor }) => {
            let next;
            while (el && el !== anchor) {
                next = hostNextSibling(el);
                hostRemove(el);
                el = next;
            }
            hostRemove(anchor);
        };
        const processElement = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
            isSVG = isSVG || n2.type === 'svg';
            if (n1 == null) {
                mountElement(n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
            }
            else {
                patchElement(n1, n2, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
            }
        };
        const mountElement = (vnode, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
            let el;
            let vnodeHook;
            const { type, props, shapeFlag, transition, patchFlag, dirs } = vnode;
            {
                el = vnode.el = hostCreateElement(vnode.type, isSVG, props && props.is, props);
                // mount children first, since some props may rely on child content
                // being already rendered, e.g. `<select value>`
                if (shapeFlag & 8 /* TEXT_CHILDREN */) {
                    hostSetElementText(el, vnode.children);
                }
                else if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
                    mountChildren(vnode.children, el, null, parentComponent, parentSuspense, isSVG && type !== 'foreignObject', slotScopeIds, optimized);
                }
                if (dirs) {
                    invokeDirectiveHook(vnode, null, parentComponent, 'created');
                }
                // props
                if (props) {
                    for (const key in props) {
                        if (key !== 'value' && !isReservedProp(key)) {
                            hostPatchProp(el, key, null, props[key], isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
                        }
                    }
                    /**
                     * Special case for setting value on DOM elements:
                     * - it can be order-sensitive (e.g. should be set *after* min/max, #2325, #4024)
                     * - it needs to be forced (#1471)
                     * #2353 proposes adding another renderer option to configure this, but
                     * the properties affects are so finite it is worth special casing it
                     * here to reduce the complexity. (Special casing it also should not
                     * affect non-DOM renderers)
                     */
                    if ('value' in props) {
                        hostPatchProp(el, 'value', null, props.value);
                    }
                    if ((vnodeHook = props.onVnodeBeforeMount)) {
                        invokeVNodeHook(vnodeHook, parentComponent, vnode);
                    }
                }
                // scopeId
                setScopeId(el, vnode, vnode.scopeId, slotScopeIds, parentComponent);
            }
            {
                Object.defineProperty(el, '__vnode', {
                    value: vnode,
                    enumerable: false
                });
                Object.defineProperty(el, '__vueParentComponent', {
                    value: parentComponent,
                    enumerable: false
                });
            }
            if (dirs) {
                invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount');
            }
            // #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved
            // #1689 For inside suspense + suspense resolved case, just call it
            const needCallTransitionHooks = (!parentSuspense || (parentSuspense && !parentSuspense.pendingBranch)) &&
                transition &&
                !transition.persisted;
            if (needCallTransitionHooks) {
                transition.beforeEnter(el);
            }
            hostInsert(el, container, anchor);
            if ((vnodeHook = props && props.onVnodeMounted) ||
                needCallTransitionHooks ||
                dirs) {
                queuePostRenderEffect(() => {
                    vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode);
                    needCallTransitionHooks && transition.enter(el);
                    dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted');
                }, parentSuspense);
            }
        };
        const setScopeId = (el, vnode, scopeId, slotScopeIds, parentComponent) => {
            if (scopeId) {
                hostSetScopeId(el, scopeId);
            }
            if (slotScopeIds) {
                for (let i = 0; i < slotScopeIds.length; i++) {
                    hostSetScopeId(el, slotScopeIds[i]);
                }
            }
            if (parentComponent) {
                let subTree = parentComponent.subTree;
                if (subTree.patchFlag > 0 &&
                    subTree.patchFlag & 2048 /* DEV_ROOT_FRAGMENT */) {
                    subTree =
                        filterSingleRoot(subTree.children) || subTree;
                }
                if (vnode === subTree) {
                    const parentVNode = parentComponent.vnode;
                    setScopeId(el, parentVNode, parentVNode.scopeId, parentVNode.slotScopeIds, parentComponent.parent);
                }
            }
        };
        const mountChildren = (children, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, start = 0) => {
            for (let i = start; i < children.length; i++) {
                const child = (children[i] = optimized
                    ? cloneIfMounted(children[i])
                    : normalizeVNode(children[i]));
                patch(null, child, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
            }
        };
        const patchElement = (n1, n2, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
            const el = (n2.el = n1.el);
            let { patchFlag, dynamicChildren, dirs } = n2;
            // #1426 take the old vnode's patch flag into account since user may clone a
            // compiler-generated vnode, which de-opts to FULL_PROPS
            patchFlag |= n1.patchFlag & 16 /* FULL_PROPS */;
            const oldProps = n1.props || EMPTY_OBJ;
            const newProps = n2.props || EMPTY_OBJ;
            let vnodeHook;
            // disable recurse in beforeUpdate hooks
            parentComponent && toggleRecurse(parentComponent, false);
            if ((vnodeHook = newProps.onVnodeBeforeUpdate)) {
                invokeVNodeHook(vnodeHook, parentComponent, n2, n1);
            }
            if (dirs) {
                invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate');
            }
            parentComponent && toggleRecurse(parentComponent, true);
            if (isHmrUpdating) {
                // HMR updated, force full diff
                patchFlag = 0;
                optimized = false;
                dynamicChildren = null;
            }
            const areChildrenSVG = isSVG && n2.type !== 'foreignObject';
            if (dynamicChildren) {
                patchBlockChildren(n1.dynamicChildren, dynamicChildren, el, parentComponent, parentSuspense, areChildrenSVG, slotScopeIds);
                if (parentComponent && parentComponent.type.__hmrId) {
                    traverseStaticChildren(n1, n2);
                }
            }
            else if (!optimized) {
                // full diff
                patchChildren(n1, n2, el, null, parentComponent, parentSuspense, areChildrenSVG, slotScopeIds, false);
            }
            if (patchFlag > 0) {
                // the presence of a patchFlag means this element's render code was
                // generated by the compiler and can take the fast path.
                // in this path old node and new node are guaranteed to have the same shape
                // (i.e. at the exact same position in the source template)
                if (patchFlag & 16 /* FULL_PROPS */) {
                    // element props contain dynamic keys, full diff needed
                    patchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG);
                }
                else {
                    // class
                    // this flag is matched when the element has dynamic class bindings.
                    if (patchFlag & 2 /* CLASS */) {
                        if (oldProps.class !== newProps.class) {
                            hostPatchProp(el, 'class', null, newProps.class, isSVG);
                        }
                    }
                    // style
                    // this flag is matched when the element has dynamic style bindings
                    if (patchFlag & 4 /* STYLE */) {
                        hostPatchProp(el, 'style', oldProps.style, newProps.style, isSVG);
                    }
                    // props
                    // This flag is matched when the element has dynamic prop/attr bindings
                    // other than class and style. The keys of dynamic prop/attrs are saved for
                    // faster iteration.
                    // Note dynamic keys like :[foo]="bar" will cause this optimization to
                    // bail out and go through a full diff because we need to unset the old key
                    if (patchFlag & 8 /* PROPS */) {
                        // if the flag is present then dynamicProps must be non-null
                        const propsToUpdate = n2.dynamicProps;
                        for (let i = 0; i < propsToUpdate.length; i++) {
                            const key = propsToUpdate[i];
                            const prev = oldProps[key];
                            const next = newProps[key];
                            // #1471 force patch value
                            if (next !== prev || key === 'value') {
                                hostPatchProp(el, key, prev, next, isSVG, n1.children, parentComponent, parentSuspense, unmountChildren);
                            }
                        }
                    }
                }
                // text
                // This flag is matched when the element has only dynamic text children.
                if (patchFlag & 1 /* TEXT */) {
                    if (n1.children !== n2.children) {
                        hostSetElementText(el, n2.children);
                    }
                }
            }
            else if (!optimized && dynamicChildren == null) {
                // unoptimized, full diff
                patchProps(el, n2, oldProps, newProps, parentComponent, parentSuspense, isSVG);
            }
            if ((vnodeHook = newProps.onVnodeUpdated) || dirs) {
                queuePostRenderEffect(() => {
                    vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1);
                    dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated');
                }, parentSuspense);
            }
        };
        // The fast path for blocks.
        const patchBlockChildren = (oldChildren, newChildren, fallbackContainer, parentComponent, parentSuspense, isSVG, slotScopeIds) => {
            for (let i = 0; i < newChildren.length; i++) {
                const oldVNode = oldChildren[i];
                const newVNode = newChildren[i];
                // Determine the container (parent element) for the patch.
                const container = 
                // oldVNode may be an errored async setup() component inside Suspense
                // which will not have a mounted element
                oldVNode.el &&
                    // - In the case of a Fragment, we need to provide the actual parent
                    // of the Fragment itself so it can move its children.
                    (oldVNode.type === Fragment ||
                        // - In the case of different nodes, there is going to be a replacement
                        // which also requires the correct parent container
                        !isSameVNodeType(oldVNode, newVNode) ||
                        // - In the case of a component, it could contain anything.
                        oldVNode.shapeFlag & (6 /* COMPONENT */ | 64 /* TELEPORT */))
                    ? hostParentNode(oldVNode.el)
                    : // In other cases, the parent container is not actually used so we
                        // just pass the block element here to avoid a DOM parentNode call.
                        fallbackContainer;
                patch(oldVNode, newVNode, container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, true);
            }
        };
        const patchProps = (el, vnode, oldProps, newProps, parentComponent, parentSuspense, isSVG) => {
            if (oldProps !== newProps) {
                for (const key in newProps) {
                    // empty string is not valid prop
                    if (isReservedProp(key))
                        continue;
                    const next = newProps[key];
                    const prev = oldProps[key];
                    // defer patching value
                    if (next !== prev && key !== 'value') {
                        hostPatchProp(el, key, prev, next, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
                    }
                }
                if (oldProps !== EMPTY_OBJ) {
                    for (const key in oldProps) {
                        if (!isReservedProp(key) && !(key in newProps)) {
                            hostPatchProp(el, key, oldProps[key], null, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
                        }
                    }
                }
                if ('value' in newProps) {
                    hostPatchProp(el, 'value', oldProps.value, newProps.value);
                }
            }
        };
        const processFragment = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
            const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateText(''));
            const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText(''));
            let { patchFlag, dynamicChildren, slotScopeIds: fragmentSlotScopeIds } = n2;
            if (// #5523 dev root fragment may inherit directives
                (isHmrUpdating || patchFlag & 2048 /* DEV_ROOT_FRAGMENT */)) {
                // HMR updated / Dev root fragment (w/ comments), force full diff
                patchFlag = 0;
                optimized = false;
                dynamicChildren = null;
            }
            // check if this is a slot fragment with :slotted scope ids
            if (fragmentSlotScopeIds) {
                slotScopeIds = slotScopeIds
                    ? slotScopeIds.concat(fragmentSlotScopeIds)
                    : fragmentSlotScopeIds;
            }
            if (n1 == null) {
                hostInsert(fragmentStartAnchor, container, anchor);
                hostInsert(fragmentEndAnchor, container, anchor);
                // a fragment can only have array children
                // since they are either generated by the compiler, or implicitly created
                // from arrays.
                mountChildren(n2.children, container, fragmentEndAnchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
            }
            else {
                if (patchFlag > 0 &&
                    patchFlag & 64 /* STABLE_FRAGMENT */ &&
                    dynamicChildren &&
                    // #2715 the previous fragment could've been a BAILed one as a result
                    // of renderSlot() with no valid children
                    n1.dynamicChildren) {
                    // a stable fragment (template root or <template v-for>) doesn't need to
                    // patch children order, but it may contain dynamicChildren.
                    patchBlockChildren(n1.dynamicChildren, dynamicChildren, container, parentComponent, parentSuspense, isSVG, slotScopeIds);
                    if (parentComponent && parentComponent.type.__hmrId) {
                        traverseStaticChildren(n1, n2);
                    }
                    else if (
                    // #2080 if the stable fragment has a key, it's a <template v-for> that may
                    //  get moved around. Make sure all root level vnodes inherit el.
                    // #2134 or if it's a component root, it may also get moved around
                    // as the component is being moved.
                    n2.key != null ||
                        (parentComponent && n2 === parentComponent.subTree)) {
                        traverseStaticChildren(n1, n2, true /* shallow */);
                    }
                }
                else {
                    // keyed / unkeyed, or manual fragments.
                    // for keyed & unkeyed, since they are compiler generated from v-for,
                    // each child is guaranteed to be a block so the fragment will never
                    // have dynamicChildren.
                    patchChildren(n1, n2, container, fragmentEndAnchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                }
            }
        };
        const processComponent = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
            n2.slotScopeIds = slotScopeIds;
            if (n1 == null) {
                if (n2.shapeFlag & 512 /* COMPONENT_KEPT_ALIVE */) {
                    parentComponent.ctx.activate(n2, container, anchor, isSVG, optimized);
                }
                else {
                    mountComponent(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
                }
            }
            else {
                updateComponent(n1, n2, optimized);
            }
        };
        const mountComponent = (initialVNode, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {
            const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent, parentSuspense));
            if (instance.type.__hmrId) {
                registerHMR(instance);
            }
            {
                pushWarningContext(initialVNode);
                startMeasure(instance, `mount`);
            }
            // inject renderer internals for keepAlive
            if (isKeepAlive(initialVNode)) {
                instance.ctx.renderer = internals;
            }
            // resolve props and slots for setup context
            {
                {
                    startMeasure(instance, `init`);
                }
                setupComponent(instance);
                {
                    endMeasure(instance, `init`);
                }
            }
            // setup() is async. This component relies on async logic to be resolved
            // before proceeding
            if (instance.asyncDep) {
                parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect);
                // Give it a placeholder if this is not hydration
                // TODO handle self-defined fallback
                if (!initialVNode.el) {
                    const placeholder = (instance.subTree = createVNode(Comment));
                    processCommentNode(null, placeholder, container, anchor);
                }
                return;
            }
            setupRenderEffect(instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized);
            {
                popWarningContext();
                endMeasure(instance, `mount`);
            }
        };
        const updateComponent = (n1, n2, optimized) => {
            const instance = (n2.component = n1.component);
            if (shouldUpdateComponent(n1, n2, optimized)) {
                if (instance.asyncDep &&
                    !instance.asyncResolved) {
                    // async & still pending - just update props and slots
                    // since the component's reactive effect for render isn't set-up yet
                    {
                        pushWarningContext(n2);
                    }
                    updateComponentPreRender(instance, n2, optimized);
                    {
                        popWarningContext();
                    }
                    return;
                }
                else {
                    // normal update
                    instance.next = n2;
                    // in case the child component is also queued, remove it to avoid
                    // double updating the same child component in the same flush.
                    invalidateJob(instance.update);
                    // instance.update is the reactive effect.
                    instance.update();
                }
            }
            else {
                // no update needed. just copy over properties
                n2.el = n1.el;
                instance.vnode = n2;
            }
        };
        const setupRenderEffect = (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) => {
            const componentUpdateFn = () => {
                if (!instance.isMounted) {
                    let vnodeHook;
                    const { el, props } = initialVNode;
                    const { bm, m, parent } = instance;
                    const isAsyncWrapperVNode = isAsyncWrapper(initialVNode);
                    toggleRecurse(instance, false);
                    // beforeMount hook
                    if (bm) {
                        invokeArrayFns(bm);
                    }
                    // onVnodeBeforeMount
                    if (!isAsyncWrapperVNode &&
                        (vnodeHook = props && props.onVnodeBeforeMount)) {
                        invokeVNodeHook(vnodeHook, parent, initialVNode);
                    }
                    toggleRecurse(instance, true);
                    if (el && hydrateNode) {
                        // vnode has adopted host node - perform hydration instead of mount.
                        const hydrateSubTree = () => {
                            {
                                startMeasure(instance, `render`);
                            }
                            instance.subTree = renderComponentRoot(instance);
                            {
                                endMeasure(instance, `render`);
                            }
                            {
                                startMeasure(instance, `hydrate`);
                            }
                            hydrateNode(el, instance.subTree, instance, parentSuspense, null);
                            {
                                endMeasure(instance, `hydrate`);
                            }
                        };
                        if (isAsyncWrapperVNode) {
                            initialVNode.type.__asyncLoader().then(
                            // note: we are moving the render call into an async callback,
                            // which means it won't track dependencies - but it's ok because
                            // a server-rendered async wrapper is already in resolved state
                            // and it will never need to change.
                            () => !instance.isUnmounted && hydrateSubTree());
                        }
                        else {
                            hydrateSubTree();
                        }
                    }
                    else {
                        {
                            startMeasure(instance, `render`);
                        }
                        const subTree = (instance.subTree = renderComponentRoot(instance));
                        {
                            endMeasure(instance, `render`);
                        }
                        {
                            startMeasure(instance, `patch`);
                        }
                        patch(null, subTree, container, anchor, instance, parentSuspense, isSVG);
                        {
                            endMeasure(instance, `patch`);
                        }
                        initialVNode.el = subTree.el;
                    }
                    // mounted hook
                    if (m) {
                        queuePostRenderEffect(m, parentSuspense);
                    }
                    // onVnodeMounted
                    if (!isAsyncWrapperVNode &&
                        (vnodeHook = props && props.onVnodeMounted)) {
                        const scopedInitialVNode = initialVNode;
                        queuePostRenderEffect(() => invokeVNodeHook(vnodeHook, parent, scopedInitialVNode), parentSuspense);
                    }
                    // activated hook for keep-alive roots.
                    // #1742 activated hook must be accessed after first render
                    // since the hook may be injected by a child keep-alive
                    if (initialVNode.shapeFlag & 256 /* COMPONENT_SHOULD_KEEP_ALIVE */ ||
                        (parent &&
                            isAsyncWrapper(parent.vnode) &&
                            parent.vnode.shapeFlag & 256 /* COMPONENT_SHOULD_KEEP_ALIVE */)) {
                        instance.a && queuePostRenderEffect(instance.a, parentSuspense);
                    }
                    instance.isMounted = true;
                    {
                        devtoolsComponentAdded(instance);
                    }
                    // #2458: deference mount-only object parameters to prevent memleaks
                    initialVNode = container = anchor = null;
                }
                else {
                    // updateComponent
                    // This is triggered by mutation of component's own state (next: null)
                    // OR parent calling processComponent (next: VNode)
                    let { next, bu, u, parent, vnode } = instance;
                    let originNext = next;
                    let vnodeHook;
                    {
                        pushWarningContext(next || instance.vnode);
                    }
                    // Disallow component effect recursion during pre-lifecycle hooks.
                    toggleRecurse(instance, false);
                    if (next) {
                        next.el = vnode.el;
                        updateComponentPreRender(instance, next, optimized);
                    }
                    else {
                        next = vnode;
                    }
                    // beforeUpdate hook
                    if (bu) {
                        invokeArrayFns(bu);
                    }
                    // onVnodeBeforeUpdate
                    if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
                        invokeVNodeHook(vnodeHook, parent, next, vnode);
                    }
                    toggleRecurse(instance, true);
                    // render
                    {
                        startMeasure(instance, `render`);
                    }
                    const nextTree = renderComponentRoot(instance);
                    {
                        endMeasure(instance, `render`);
                    }
                    const prevTree = instance.subTree;
                    instance.subTree = nextTree;
                    {
                        startMeasure(instance, `patch`);
                    }
                    patch(prevTree, nextTree, 
                    // parent may have changed if it's in a teleport
                    hostParentNode(prevTree.el), 
                    // anchor may have changed if it's in a fragment
                    getNextHostNode(prevTree), instance, parentSuspense, isSVG);
                    {
                        endMeasure(instance, `patch`);
                    }
                    next.el = nextTree.el;
                    if (originNext === null) {
                        // self-triggered update. In case of HOC, update parent component
                        // vnode el. HOC is indicated by parent instance's subTree pointing
                        // to child component's vnode
                        updateHOCHostEl(instance, nextTree.el);
                    }
                    // updated hook
                    if (u) {
                        queuePostRenderEffect(u, parentSuspense);
                    }
                    // onVnodeUpdated
                    if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
                        queuePostRenderEffect(() => invokeVNodeHook(vnodeHook, parent, next, vnode), parentSuspense);
                    }
                    {
                        devtoolsComponentUpdated(instance);
                    }
                    {
                        popWarningContext();
                    }
                }
            };
            // create reactive effect for rendering
            const effect = (instance.effect = new ReactiveEffect(componentUpdateFn, () => queueJob(update), instance.scope // track it in component's effect scope
            ));
            const update = (instance.update = () => effect.run());
            update.id = instance.uid;
            // allowRecurse
            // #1801, #2043 component render effects should allow recursive updates
            toggleRecurse(instance, true);
            {
                effect.onTrack = instance.rtc
                    ? e => invokeArrayFns(instance.rtc, e)
                    : void 0;
                effect.onTrigger = instance.rtg
                    ? e => invokeArrayFns(instance.rtg, e)
                    : void 0;
                update.ownerInstance = instance;
            }
            update();
        };
        const updateComponentPreRender = (instance, nextVNode, optimized) => {
            nextVNode.component = instance;
            const prevProps = instance.vnode.props;
            instance.vnode = nextVNode;
            instance.next = null;
            updateProps(instance, nextVNode.props, prevProps, optimized);
            updateSlots(instance, nextVNode.children, optimized);
            pauseTracking();
            // props update may have triggered pre-flush watchers.
            // flush them before the render update.
            flushPreFlushCbs(undefined, instance.update);
            resetTracking();
        };
        const patchChildren = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized = false) => {
            const c1 = n1 && n1.children;
            const prevShapeFlag = n1 ? n1.shapeFlag : 0;
            const c2 = n2.children;
            const { patchFlag, shapeFlag } = n2;
            // fast path
            if (patchFlag > 0) {
                if (patchFlag & 128 /* KEYED_FRAGMENT */) {
                    // this could be either fully-keyed or mixed (some keyed some not)
                    // presence of patchFlag means children are guaranteed to be arrays
                    patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    return;
                }
                else if (patchFlag & 256 /* UNKEYED_FRAGMENT */) {
                    // unkeyed
                    patchUnkeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    return;
                }
            }
            // children has 3 possibilities: text, array or no children.
            if (shapeFlag & 8 /* TEXT_CHILDREN */) {
                // text children fast path
                if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
                    unmountChildren(c1, parentComponent, parentSuspense);
                }
                if (c2 !== c1) {
                    hostSetElementText(container, c2);
                }
            }
            else {
                if (prevShapeFlag & 16 /* ARRAY_CHILDREN */) {
                    // prev children was array
                    if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
                        // two arrays, cannot assume anything, do full diff
                        patchKeyedChildren(c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    }
                    else {
                        // no new children, just unmount old
                        unmountChildren(c1, parentComponent, parentSuspense, true);
                    }
                }
                else {
                    // prev children was text OR null
                    // new children is array OR null
                    if (prevShapeFlag & 8 /* TEXT_CHILDREN */) {
                        hostSetElementText(container, '');
                    }
                    // mount new if array
                    if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
                        mountChildren(c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    }
                }
            }
        };
        const patchUnkeyedChildren = (c1, c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
            c1 = c1 || EMPTY_ARR;
            c2 = c2 || EMPTY_ARR;
            const oldLength = c1.length;
            const newLength = c2.length;
            const commonLength = Math.min(oldLength, newLength);
            let i;
            for (i = 0; i < commonLength; i++) {
                const nextChild = (c2[i] = optimized
                    ? cloneIfMounted(c2[i])
                    : normalizeVNode(c2[i]));
                patch(c1[i], nextChild, container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
            }
            if (oldLength > newLength) {
                // remove old
                unmountChildren(c1, parentComponent, parentSuspense, true, false, commonLength);
            }
            else {
                // mount new
                mountChildren(c2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, commonLength);
            }
        };
        // can be all-keyed or mixed
        const patchKeyedChildren = (c1, c2, container, parentAnchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
            let i = 0;
            const l2 = c2.length;
            let e1 = c1.length - 1; // prev ending index
            let e2 = l2 - 1; // next ending index
            // 1. sync from start
            // (a b) c
            // (a b) d e
            while (i <= e1 && i <= e2) {
                const n1 = c1[i];
                const n2 = (c2[i] = optimized
                    ? cloneIfMounted(c2[i])
                    : normalizeVNode(c2[i]));
                if (isSameVNodeType(n1, n2)) {
                    patch(n1, n2, container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                }
                else {
                    break;
                }
                i++;
            }
            // 2. sync from end
            // a (b c)
            // d e (b c)
            while (i <= e1 && i <= e2) {
                const n1 = c1[e1];
                const n2 = (c2[e2] = optimized
                    ? cloneIfMounted(c2[e2])
                    : normalizeVNode(c2[e2]));
                if (isSameVNodeType(n1, n2)) {
                    patch(n1, n2, container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                }
                else {
                    break;
                }
                e1--;
                e2--;
            }
            // 3. common sequence + mount
            // (a b)
            // (a b) c
            // i = 2, e1 = 1, e2 = 2
            // (a b)
            // c (a b)
            // i = 0, e1 = -1, e2 = 0
            if (i > e1) {
                if (i <= e2) {
                    const nextPos = e2 + 1;
                    const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;
                    while (i <= e2) {
                        patch(null, (c2[i] = optimized
                            ? cloneIfMounted(c2[i])
                            : normalizeVNode(c2[i])), container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                        i++;
                    }
                }
            }
            // 4. common sequence + unmount
            // (a b) c
            // (a b)
            // i = 2, e1 = 2, e2 = 1
            // a (b c)
            // (b c)
            // i = 0, e1 = 0, e2 = -1
            else if (i > e2) {
                while (i <= e1) {
                    unmount(c1[i], parentComponent, parentSuspense, true);
                    i++;
                }
            }
            // 5. unknown sequence
            // [i ... e1 + 1]: a b [c d e] f g
            // [i ... e2 + 1]: a b [e d c h] f g
            // i = 2, e1 = 4, e2 = 5
            else {
                const s1 = i; // prev starting index
                const s2 = i; // next starting index
                // 5.1 build key:index map for newChildren
                const keyToNewIndexMap = new Map();
                for (i = s2; i <= e2; i++) {
                    const nextChild = (c2[i] = optimized
                        ? cloneIfMounted(c2[i])
                        : normalizeVNode(c2[i]));
                    if (nextChild.key != null) {
                        if (keyToNewIndexMap.has(nextChild.key)) {
                            warn$1(`Duplicate keys found during update:`, JSON.stringify(nextChild.key), `Make sure keys are unique.`);
                        }
                        keyToNewIndexMap.set(nextChild.key, i);
                    }
                }
                // 5.2 loop through old children left to be patched and try to patch
                // matching nodes & remove nodes that are no longer present
                let j;
                let patched = 0;
                const toBePatched = e2 - s2 + 1;
                let moved = false;
                // used to track whether any node has moved
                let maxNewIndexSoFar = 0;
                // works as Map<newIndex, oldIndex>
                // Note that oldIndex is offset by +1
                // and oldIndex = 0 is a special value indicating the new node has
                // no corresponding old node.
                // used for determining longest stable subsequence
                const newIndexToOldIndexMap = new Array(toBePatched);
                for (i = 0; i < toBePatched; i++)
                    newIndexToOldIndexMap[i] = 0;
                for (i = s1; i <= e1; i++) {
                    const prevChild = c1[i];
                    if (patched >= toBePatched) {
                        // all new children have been patched so this can only be a removal
                        unmount(prevChild, parentComponent, parentSuspense, true);
                        continue;
                    }
                    let newIndex;
                    if (prevChild.key != null) {
                        newIndex = keyToNewIndexMap.get(prevChild.key);
                    }
                    else {
                        // key-less node, try to locate a key-less node of the same type
                        for (j = s2; j <= e2; j++) {
                            if (newIndexToOldIndexMap[j - s2] === 0 &&
                                isSameVNodeType(prevChild, c2[j])) {
                                newIndex = j;
                                break;
                            }
                        }
                    }
                    if (newIndex === undefined) {
                        unmount(prevChild, parentComponent, parentSuspense, true);
                    }
                    else {
                        newIndexToOldIndexMap[newIndex - s2] = i + 1;
                        if (newIndex >= maxNewIndexSoFar) {
                            maxNewIndexSoFar = newIndex;
                        }
                        else {
                            moved = true;
                        }
                        patch(prevChild, c2[newIndex], container, null, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                        patched++;
                    }
                }
                // 5.3 move and mount
                // generate longest stable subsequence only when nodes have moved
                const increasingNewIndexSequence = moved
                    ? getSequence(newIndexToOldIndexMap)
                    : EMPTY_ARR;
                j = increasingNewIndexSequence.length - 1;
                // looping backwards so that we can use last patched node as anchor
                for (i = toBePatched - 1; i >= 0; i--) {
                    const nextIndex = s2 + i;
                    const nextChild = c2[nextIndex];
                    const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;
                    if (newIndexToOldIndexMap[i] === 0) {
                        // mount new
                        patch(null, nextChild, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    }
                    else if (moved) {
                        // move if:
                        // There is no stable subsequence (e.g. a reverse)
                        // OR current node is not among the stable sequence
                        if (j < 0 || i !== increasingNewIndexSequence[j]) {
                            move(nextChild, container, anchor, 2 /* REORDER */);
                        }
                        else {
                            j--;
                        }
                    }
                }
            }
        };
        const move = (vnode, container, anchor, moveType, parentSuspense = null) => {
            const { el, type, transition, children, shapeFlag } = vnode;
            if (shapeFlag & 6 /* COMPONENT */) {
                move(vnode.component.subTree, container, anchor, moveType);
                return;
            }
            if (shapeFlag & 128 /* SUSPENSE */) {
                vnode.suspense.move(container, anchor, moveType);
                return;
            }
            if (shapeFlag & 64 /* TELEPORT */) {
                type.move(vnode, container, anchor, internals);
                return;
            }
            if (type === Fragment) {
                hostInsert(el, container, anchor);
                for (let i = 0; i < children.length; i++) {
                    move(children[i], container, anchor, moveType);
                }
                hostInsert(vnode.anchor, container, anchor);
                return;
            }
            if (type === Static) {
                moveStaticNode(vnode, container, anchor);
                return;
            }
            // single nodes
            const needTransition = moveType !== 2 /* REORDER */ &&
                shapeFlag & 1 /* ELEMENT */ &&
                transition;
            if (needTransition) {
                if (moveType === 0 /* ENTER */) {
                    transition.beforeEnter(el);
                    hostInsert(el, container, anchor);
                    queuePostRenderEffect(() => transition.enter(el), parentSuspense);
                }
                else {
                    const { leave, delayLeave, afterLeave } = transition;
                    const remove = () => hostInsert(el, container, anchor);
                    const performLeave = () => {
                        leave(el, () => {
                            remove();
                            afterLeave && afterLeave();
                        });
                    };
                    if (delayLeave) {
                        delayLeave(el, remove, performLeave);
                    }
                    else {
                        performLeave();
                    }
                }
            }
            else {
                hostInsert(el, container, anchor);
            }
        };
        const unmount = (vnode, parentComponent, parentSuspense, doRemove = false, optimized = false) => {
            const { type, props, ref, children, dynamicChildren, shapeFlag, patchFlag, dirs } = vnode;
            // unset ref
            if (ref != null) {
                setRef(ref, null, parentSuspense, vnode, true);
            }
            if (shapeFlag & 256 /* COMPONENT_SHOULD_KEEP_ALIVE */) {
                parentComponent.ctx.deactivate(vnode);
                return;
            }
            const shouldInvokeDirs = shapeFlag & 1 /* ELEMENT */ && dirs;
            const shouldInvokeVnodeHook = !isAsyncWrapper(vnode);
            let vnodeHook;
            if (shouldInvokeVnodeHook &&
                (vnodeHook = props && props.onVnodeBeforeUnmount)) {
                invokeVNodeHook(vnodeHook, parentComponent, vnode);
            }
            if (shapeFlag & 6 /* COMPONENT */) {
                unmountComponent(vnode.component, parentSuspense, doRemove);
            }
            else {
                if (shapeFlag & 128 /* SUSPENSE */) {
                    vnode.suspense.unmount(parentSuspense, doRemove);
                    return;
                }
                if (shouldInvokeDirs) {
                    invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount');
                }
                if (shapeFlag & 64 /* TELEPORT */) {
                    vnode.type.remove(vnode, parentComponent, parentSuspense, optimized, internals, doRemove);
                }
                else if (dynamicChildren &&
                    // #1153: fast path should not be taken for non-stable (v-for) fragments
                    (type !== Fragment ||
                        (patchFlag > 0 && patchFlag & 64 /* STABLE_FRAGMENT */))) {
                    // fast path for block nodes: only need to unmount dynamic children.
                    unmountChildren(dynamicChildren, parentComponent, parentSuspense, false, true);
                }
                else if ((type === Fragment &&
                    patchFlag &
                        (128 /* KEYED_FRAGMENT */ | 256 /* UNKEYED_FRAGMENT */)) ||
                    (!optimized && shapeFlag & 16 /* ARRAY_CHILDREN */)) {
                    unmountChildren(children, parentComponent, parentSuspense);
                }
                if (doRemove) {
                    remove(vnode);
                }
            }
            if ((shouldInvokeVnodeHook &&
                (vnodeHook = props && props.onVnodeUnmounted)) ||
                shouldInvokeDirs) {
                queuePostRenderEffect(() => {
                    vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode);
                    shouldInvokeDirs &&
                        invokeDirectiveHook(vnode, null, parentComponent, 'unmounted');
                }, parentSuspense);
            }
        };
        const remove = vnode => {
            const { type, el, anchor, transition } = vnode;
            if (type === Fragment) {
                if (vnode.patchFlag > 0 &&
                    vnode.patchFlag & 2048 /* DEV_ROOT_FRAGMENT */ &&
                    transition &&
                    !transition.persisted) {
                    vnode.children.forEach(child => {
                        if (child.type === Comment) {
                            hostRemove(child.el);
                        }
                        else {
                            remove(child);
                        }
                    });
                }
                else {
                    removeFragment(el, anchor);
                }
                return;
            }
            if (type === Static) {
                removeStaticNode(vnode);
                return;
            }
            const performRemove = () => {
                hostRemove(el);
                if (transition && !transition.persisted && transition.afterLeave) {
                    transition.afterLeave();
                }
            };
            if (vnode.shapeFlag & 1 /* ELEMENT */ &&
                transition &&
                !transition.persisted) {
                const { leave, delayLeave } = transition;
                const performLeave = () => leave(el, performRemove);
                if (delayLeave) {
                    delayLeave(vnode.el, performRemove, performLeave);
                }
                else {
                    performLeave();
                }
            }
            else {
                performRemove();
            }
        };
        const removeFragment = (cur, end) => {
            // For fragments, directly remove all contained DOM nodes.
            // (fragment child nodes cannot have transition)
            let next;
            while (cur !== end) {
                next = hostNextSibling(cur);
                hostRemove(cur);
                cur = next;
            }
            hostRemove(end);
        };
        const unmountComponent = (instance, parentSuspense, doRemove) => {
            if (instance.type.__hmrId) {
                unregisterHMR(instance);
            }
            const { bum, scope, update, subTree, um } = instance;
            // beforeUnmount hook
            if (bum) {
                invokeArrayFns(bum);
            }
            // stop effects in component scope
            scope.stop();
            // update may be null if a component is unmounted before its async
            // setup has resolved.
            if (update) {
                // so that scheduler will no longer invoke it
                update.active = false;
                unmount(subTree, instance, parentSuspense, doRemove);
            }
            // unmounted hook
            if (um) {
                queuePostRenderEffect(um, parentSuspense);
            }
            queuePostRenderEffect(() => {
                instance.isUnmounted = true;
            }, parentSuspense);
            // A component with async dep inside a pending suspense is unmounted before
            // its async dep resolves. This should remove the dep from the suspense, and
            // cause the suspense to resolve immediately if that was the last dep.
            if (parentSuspense &&
                parentSuspense.pendingBranch &&
                !parentSuspense.isUnmounted &&
                instance.asyncDep &&
                !instance.asyncResolved &&
                instance.suspenseId === parentSuspense.pendingId) {
                parentSuspense.deps--;
                if (parentSuspense.deps === 0) {
                    parentSuspense.resolve();
                }
            }
            {
                devtoolsComponentRemoved(instance);
            }
        };
        const unmountChildren = (children, parentComponent, parentSuspense, doRemove = false, optimized = false, start = 0) => {
            for (let i = start; i < children.length; i++) {
                unmount(children[i], parentComponent, parentSuspense, doRemove, optimized);
            }
        };
        const getNextHostNode = vnode => {
            if (vnode.shapeFlag & 6 /* COMPONENT */) {
                return getNextHostNode(vnode.component.subTree);
            }
            if (vnode.shapeFlag & 128 /* SUSPENSE */) {
                return vnode.suspense.next();
            }
            return hostNextSibling((vnode.anchor || vnode.el));
        };
        const render = (vnode, container, isSVG) => {
            if (vnode == null) {
                if (container._vnode) {
                    unmount(container._vnode, null, null, true);
                }
            }
            else {
                patch(container._vnode || null, vnode, container, null, null, null, isSVG);
            }
            flushPostFlushCbs();
            container._vnode = vnode;
        };
        const internals = {
            p: patch,
            um: unmount,
            m: move,
            r: remove,
            mt: mountComponent,
            mc: mountChildren,
            pc: patchChildren,
            pbc: patchBlockChildren,
            n: getNextHostNode,
            o: options
        };
        let hydrate;
        let hydrateNode;
        if (createHydrationFns) {
            [hydrate, hydrateNode] = createHydrationFns(internals);
        }
        return {
            render,
            hydrate,
            createApp: createAppAPI(render, hydrate)
        };
    }
    function toggleRecurse({ effect, update }, allowed) {
        effect.allowRecurse = update.allowRecurse = allowed;
    }
    /**
     * #1156
     * When a component is HMR-enabled, we need to make sure that all static nodes
     * inside a block also inherit the DOM element from the previous tree so that
     * HMR updates (which are full updates) can retrieve the element for patching.
     *
     * #2080
     * Inside keyed `template` fragment static children, if a fragment is moved,
     * the children will always be moved. Therefore, in order to ensure correct move
     * position, el should be inherited from previous nodes.
     */
    function traverseStaticChildren(n1, n2, shallow = false) {
        const ch1 = n1.children;
        const ch2 = n2.children;
        if (isArray(ch1) && isArray(ch2)) {
            for (let i = 0; i < ch1.length; i++) {
                // this is only called in the optimized path so array children are
                // guaranteed to be vnodes
                const c1 = ch1[i];
                let c2 = ch2[i];
                if (c2.shapeFlag & 1 /* ELEMENT */ && !c2.dynamicChildren) {
                    if (c2.patchFlag <= 0 || c2.patchFlag === 32 /* HYDRATE_EVENTS */) {
                        c2 = ch2[i] = cloneIfMounted(ch2[i]);
                        c2.el = c1.el;
                    }
                    if (!shallow)
                        traverseStaticChildren(c1, c2);
                }
                // also inherit for comment nodes, but not placeholders (e.g. v-if which
                // would have received .el during block patch)
                if (c2.type === Comment && !c2.el) {
                    c2.el = c1.el;
                }
            }
        }
    }
    // https://en.wikipedia.org/wiki/Longest_increasing_subsequence
    function getSequence(arr) {
        const p = arr.slice();
        const result = [0];
        let i, j, u, v, c;
        const len = arr.length;
        for (i = 0; i < len; i++) {
            const arrI = arr[i];
            if (arrI !== 0) {
                j = result[result.length - 1];
                if (arr[j] < arrI) {
                    p[i] = j;
                    result.push(i);
                    continue;
                }
                u = 0;
                v = result.length - 1;
                while (u < v) {
                    c = (u + v) >> 1;
                    if (arr[result[c]] < arrI) {
                        u = c + 1;
                    }
                    else {
                        v = c;
                    }
                }
                if (arrI < arr[result[u]]) {
                    if (u > 0) {
                        p[i] = result[u - 1];
                    }
                    result[u] = i;
                }
            }
        }
        u = result.length;
        v = result[u - 1];
        while (u-- > 0) {
            result[u] = v;
            v = p[v];
        }
        return result;
    }

    const isTeleport = (type) => type.__isTeleport;
    const isTeleportDisabled = (props) => props && (props.disabled || props.disabled === '');
    const isTargetSVG = (target) => typeof SVGElement !== 'undefined' && target instanceof SVGElement;
    const resolveTarget = (props, select) => {
        const targetSelector = props && props.to;
        if (isString(targetSelector)) {
            if (!select) {
                warn$1(`Current renderer does not support string target for Teleports. ` +
                        `(missing querySelector renderer option)`);
                return null;
            }
            else {
                const target = select(targetSelector);
                if (!target) {
                    warn$1(`Failed to locate Teleport target with selector "${targetSelector}". ` +
                            `Note the target element must exist before the component is mounted - ` +
                            `i.e. the target cannot be rendered by the component itself, and ` +
                            `ideally should be outside of the entire Vue component tree.`);
                }
                return target;
            }
        }
        else {
            if (!targetSelector && !isTeleportDisabled(props)) {
                warn$1(`Invalid Teleport target: ${targetSelector}`);
            }
            return targetSelector;
        }
    };
    const TeleportImpl = {
        __isTeleport: true,
        process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, internals) {
            const { mc: mountChildren, pc: patchChildren, pbc: patchBlockChildren, o: { insert, querySelector, createText, createComment } } = internals;
            const disabled = isTeleportDisabled(n2.props);
            let { shapeFlag, children, dynamicChildren } = n2;
            // #3302
            // HMR updated, force full diff
            if (isHmrUpdating) {
                optimized = false;
                dynamicChildren = null;
            }
            if (n1 == null) {
                // insert anchors in the main view
                const placeholder = (n2.el = createComment('teleport start')
                    );
                const mainAnchor = (n2.anchor = createComment('teleport end')
                    );
                insert(placeholder, container, anchor);
                insert(mainAnchor, container, anchor);
                const target = (n2.target = resolveTarget(n2.props, querySelector));
                const targetAnchor = (n2.targetAnchor = createText(''));
                if (target) {
                    insert(targetAnchor, target);
                    // #2652 we could be teleporting from a non-SVG tree into an SVG tree
                    isSVG = isSVG || isTargetSVG(target);
                }
                else if (!disabled) {
                    warn$1('Invalid Teleport target on mount:', target, `(${typeof target})`);
                }
                const mount = (container, anchor) => {
                    // Teleport *always* has Array children. This is enforced in both the
                    // compiler and vnode children normalization.
                    if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
                        mountChildren(children, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
                    }
                };
                if (disabled) {
                    mount(container, mainAnchor);
                }
                else if (target) {
                    mount(target, targetAnchor);
                }
            }
            else {
                // update content
                n2.el = n1.el;
                const mainAnchor = (n2.anchor = n1.anchor);
                const target = (n2.target = n1.target);
                const targetAnchor = (n2.targetAnchor = n1.targetAnchor);
                const wasDisabled = isTeleportDisabled(n1.props);
                const currentContainer = wasDisabled ? container : target;
                const currentAnchor = wasDisabled ? mainAnchor : targetAnchor;
                isSVG = isSVG || isTargetSVG(target);
                if (dynamicChildren) {
                    // fast path when the teleport happens to be a block root
                    patchBlockChildren(n1.dynamicChildren, dynamicChildren, currentContainer, parentComponent, parentSuspense, isSVG, slotScopeIds);
                    // even in block tree mode we need to make sure all root-level nodes
                    // in the teleport inherit previous DOM references so that they can
                    // be moved in future patches.
                    traverseStaticChildren(n1, n2, true);
                }
                else if (!optimized) {
                    patchChildren(n1, n2, currentContainer, currentAnchor, parentComponent, parentSuspense, isSVG, slotScopeIds, false);
                }
                if (disabled) {
                    if (!wasDisabled) {
                        // enabled -> disabled
                        // move into main container
                        moveTeleport(n2, container, mainAnchor, internals, 1 /* TOGGLE */);
                    }
                }
                else {
                    // target changed
                    if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) {
                        const nextTarget = (n2.target = resolveTarget(n2.props, querySelector));
                        if (nextTarget) {
                            moveTeleport(n2, nextTarget, null, internals, 0 /* TARGET_CHANGE */);
                        }
                        else {
                            warn$1('Invalid Teleport target on update:', target, `(${typeof target})`);
                        }
                    }
                    else if (wasDisabled) {
                        // disabled -> enabled
                        // move into teleport target
                        moveTeleport(n2, target, targetAnchor, internals, 1 /* TOGGLE */);
                    }
                }
            }
        },
        remove(vnode, parentComponent, parentSuspense, optimized, { um: unmount, o: { remove: hostRemove } }, doRemove) {
            const { shapeFlag, children, anchor, targetAnchor, target, props } = vnode;
            if (target) {
                hostRemove(targetAnchor);
            }
            // an unmounted teleport should always remove its children if not disabled
            if (doRemove || !isTeleportDisabled(props)) {
                hostRemove(anchor);
                if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
                    for (let i = 0; i < children.length; i++) {
                        const child = children[i];
                        unmount(child, parentComponent, parentSuspense, true, !!child.dynamicChildren);
                    }
                }
            }
        },
        move: moveTeleport,
        hydrate: hydrateTeleport
    };
    function moveTeleport(vnode, container, parentAnchor, { o: { insert }, m: move }, moveType = 2 /* REORDER */) {
        // move target anchor if this is a target change.
        if (moveType === 0 /* TARGET_CHANGE */) {
            insert(vnode.targetAnchor, container, parentAnchor);
        }
        const { el, anchor, shapeFlag, children, props } = vnode;
        const isReorder = moveType === 2 /* REORDER */;
        // move main view anchor if this is a re-order.
        if (isReorder) {
            insert(el, container, parentAnchor);
        }
        // if this is a re-order and teleport is enabled (content is in target)
        // do not move children. So the opposite is: only move children if this
        // is not a reorder, or the teleport is disabled
        if (!isReorder || isTeleportDisabled(props)) {
            // Teleport has either Array children or no children.
            if (shapeFlag & 16 /* ARRAY_CHILDREN */) {
                for (let i = 0; i < children.length; i++) {
                    move(children[i], container, parentAnchor, 2 /* REORDER */);
                }
            }
        }
        // move main view anchor if this is a re-order.
        if (isReorder) {
            insert(anchor, container, parentAnchor);
        }
    }
    function hydrateTeleport(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized, { o: { nextSibling, parentNode, querySelector } }, hydrateChildren) {
        const target = (vnode.target = resolveTarget(vnode.props, querySelector));
        if (target) {
            // if multiple teleports rendered to the same target element, we need to
            // pick up from where the last teleport finished instead of the first node
            const targetNode = target._lpa || target.firstChild;
            if (vnode.shapeFlag & 16 /* ARRAY_CHILDREN */) {
                if (isTeleportDisabled(vnode.props)) {
                    vnode.anchor = hydrateChildren(nextSibling(node), vnode, parentNode(node), parentComponent, parentSuspense, slotScopeIds, optimized);
                    vnode.targetAnchor = targetNode;
                }
                else {
                    vnode.anchor = nextSibling(node);
                    // lookahead until we find the target anchor
                    // we cannot rely on return value of hydrateChildren() because there
                    // could be nested teleports
                    let targetAnchor = targetNode;
                    while (targetAnchor) {
                        targetAnchor = nextSibling(targetAnchor);
                        if (targetAnchor &&
                            targetAnchor.nodeType === 8 &&
                            targetAnchor.data === 'teleport anchor') {
                            vnode.targetAnchor = targetAnchor;
                            target._lpa =
                                vnode.targetAnchor && nextSibling(vnode.targetAnchor);
                            break;
                        }
                    }
                    hydrateChildren(targetNode, vnode, target, parentComponent, parentSuspense, slotScopeIds, optimized);
                }
            }
        }
        return vnode.anchor && nextSibling(vnode.anchor);
    }
    // Force-casted public typing for h and TSX props inference
    const Teleport = TeleportImpl;

    const Fragment = Symbol('Fragment' );
    const Text = Symbol('Text' );
    const Comment = Symbol('Comment' );
    const Static = Symbol('Static' );
    // Since v-if and v-for are the two possible ways node structure can dynamically
    // change, once we consider v-if branches and each v-for fragment a block, we
    // can divide a template into nested blocks, and within each block the node
    // structure would be stable. This allows us to skip most children diffing
    // and only worry about the dynamic nodes (indicated by patch flags).
    const blockStack = [];
    let currentBlock = null;
    /**
     * Open a block.
     * This must be called before `createBlock`. It cannot be part of `createBlock`
     * because the children of the block are evaluated before `createBlock` itself
     * is called. The generated code typically looks like this:
     *
     * ```js
     * function render() {
     *   return (openBlock(),createBlock('div', null, [...]))
     * }
     * ```
     * disableTracking is true when creating a v-for fragment block, since a v-for
     * fragment always diffs its children.
     *
     * @private
     */
    function openBlock(disableTracking = false) {
        blockStack.push((currentBlock = disableTracking ? null : []));
    }
    function closeBlock() {
        blockStack.pop();
        currentBlock = blockStack[blockStack.length - 1] || null;
    }
    // Whether we should be tracking dynamic child nodes inside a block.
    // Only tracks when this value is > 0
    // We are not using a simple boolean because this value may need to be
    // incremented/decremented by nested usage of v-once (see below)
    let isBlockTreeEnabled = 1;
    /**
     * Block tracking sometimes needs to be disabled, for example during the
     * creation of a tree that needs to be cached by v-once. The compiler generates
     * code like this:
     *
     * ``` js
     * _cache[1] || (
     *   setBlockTracking(-1),
     *   _cache[1] = createVNode(...),
     *   setBlockTracking(1),
     *   _cache[1]
     * )
     * ```
     *
     * @private
     */
    function setBlockTracking(value) {
        isBlockTreeEnabled += value;
    }
    function setupBlock(vnode) {
        // save current block children on the block vnode
        vnode.dynamicChildren =
            isBlockTreeEnabled > 0 ? currentBlock || EMPTY_ARR : null;
        // close block
        closeBlock();
        // a block is always going to be patched, so track it as a child of its
        // parent block
        if (isBlockTreeEnabled > 0 && currentBlock) {
            currentBlock.push(vnode);
        }
        return vnode;
    }
    /**
     * @private
     */
    function createElementBlock(type, props, children, patchFlag, dynamicProps, shapeFlag) {
        return setupBlock(createBaseVNode(type, props, children, patchFlag, dynamicProps, shapeFlag, true /* isBlock */));
    }
    /**
     * Create a block root vnode. Takes the same exact arguments as `createVNode`.
     * A block root keeps track of dynamic nodes within the block in the
     * `dynamicChildren` array.
     *
     * @private
     */
    function createBlock(type, props, children, patchFlag, dynamicProps) {
        return setupBlock(createVNode(type, props, children, patchFlag, dynamicProps, true /* isBlock: prevent a block from tracking itself */));
    }
    function isVNode(value) {
        return value ? value.__v_isVNode === true : false;
    }
    function isSameVNodeType(n1, n2) {
        if (n2.shapeFlag & 6 /* COMPONENT */ &&
            hmrDirtyComponents.has(n2.type)) {
            // HMR only: if the component has been hot-updated, force a reload.
            return false;
        }
        return n1.type === n2.type && n1.key === n2.key;
    }
    let vnodeArgsTransformer;
    /**
     * Internal API for registering an arguments transform for createVNode
     * used for creating stubs in the test-utils
     * It is *internal* but needs to be exposed for test-utils to pick up proper
     * typings
     */
    function transformVNodeArgs(transformer) {
        vnodeArgsTransformer = transformer;
    }
    const createVNodeWithArgsTransform = (...args) => {
        return _createVNode(...(vnodeArgsTransformer
            ? vnodeArgsTransformer(args, currentRenderingInstance)
            : args));
    };
    const InternalObjectKey = `__vInternal`;
    const normalizeKey = ({ key }) => key != null ? key : null;
    const normalizeRef = ({ ref, ref_key, ref_for }) => {
        return (ref != null
            ? isString(ref) || isRef(ref) || isFunction(ref)
                ? { i: currentRenderingInstance, r: ref, k: ref_key, f: !!ref_for }
                : ref
            : null);
    };
    function createBaseVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, shapeFlag = type === Fragment ? 0 : 1 /* ELEMENT */, isBlockNode = false, needFullChildrenNormalization = false) {
        const vnode = {
            __v_isVNode: true,
            __v_skip: true,
            type,
            props,
            key: props && normalizeKey(props),
            ref: props && normalizeRef(props),
            scopeId: currentScopeId,
            slotScopeIds: null,
            children,
            component: null,
            suspense: null,
            ssContent: null,
            ssFallback: null,
            dirs: null,
            transition: null,
            el: null,
            anchor: null,
            target: null,
            targetAnchor: null,
            staticCount: 0,
            shapeFlag,
            patchFlag,
            dynamicProps,
            dynamicChildren: null,
            appContext: null
        };
        if (needFullChildrenNormalization) {
            normalizeChildren(vnode, children);
            // normalize suspense children
            if (shapeFlag & 128 /* SUSPENSE */) {
                type.normalize(vnode);
            }
        }
        else if (children) {
            // compiled element vnode - if children is passed, only possible types are
            // string or Array.
            vnode.shapeFlag |= isString(children)
                ? 8 /* TEXT_CHILDREN */
                : 16 /* ARRAY_CHILDREN */;
        }
        // validate key
        if (vnode.key !== vnode.key) {
            warn$1(`VNode created with invalid key (NaN). VNode type:`, vnode.type);
        }
        // track vnode for block tree
        if (isBlockTreeEnabled > 0 &&
            // avoid a block node from tracking itself
            !isBlockNode &&
            // has current parent block
            currentBlock &&
            // presence of a patch flag indicates this node needs patching on updates.
            // component nodes also should always be patched, because even if the
            // component doesn't need to update, it needs to persist the instance on to
            // the next vnode so that it can be properly unmounted later.
            (vnode.patchFlag > 0 || shapeFlag & 6 /* COMPONENT */) &&
            // the EVENTS flag is only for hydration and if it is the only flag, the
            // vnode should not be considered dynamic due to handler caching.
            vnode.patchFlag !== 32 /* HYDRATE_EVENTS */) {
            currentBlock.push(vnode);
        }
        return vnode;
    }
    const createVNode = (createVNodeWithArgsTransform );
    function _createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, isBlockNode = false) {
        if (!type || type === NULL_DYNAMIC_COMPONENT) {
            if (!type) {
                warn$1(`Invalid vnode type when creating vnode: ${type}.`);
            }
            type = Comment;
        }
        if (isVNode(type)) {
            // createVNode receiving an existing vnode. This happens in cases like
            // <component :is="vnode"/>
            // #2078 make sure to merge refs during the clone instead of overwriting it
            const cloned = cloneVNode(type, props, true /* mergeRef: true */);
            if (children) {
                normalizeChildren(cloned, children);
            }
            if (isBlockTreeEnabled > 0 && !isBlockNode && currentBlock) {
                if (cloned.shapeFlag & 6 /* COMPONENT */) {
                    currentBlock[currentBlock.indexOf(type)] = cloned;
                }
                else {
                    currentBlock.push(cloned);
                }
            }
            cloned.patchFlag |= -2 /* BAIL */;
            return cloned;
        }
        // class component normalization.
        if (isClassComponent(type)) {
            type = type.__vccOpts;
        }
        // class & style normalization.
        if (props) {
            // for reactive or proxy objects, we need to clone it to enable mutation.
            props = guardReactiveProps(props);
            let { class: klass, style } = props;
            if (klass && !isString(klass)) {
                props.class = normalizeClass(klass);
            }
            if (isObject(style)) {
                // reactive state objects need to be cloned since they are likely to be
                // mutated
                if (isProxy(style) && !isArray(style)) {
                    style = extend({}, style);
                }
                props.style = normalizeStyle(style);
            }
        }
        // encode the vnode type information into a bitmap
        const shapeFlag = isString(type)
            ? 1 /* ELEMENT */
            : isSuspense(type)
                ? 128 /* SUSPENSE */
                : isTeleport(type)
                    ? 64 /* TELEPORT */
                    : isObject(type)
                        ? 4 /* STATEFUL_COMPONENT */
                        : isFunction(type)
                            ? 2 /* FUNCTIONAL_COMPONENT */
                            : 0;
        if (shapeFlag & 4 /* STATEFUL_COMPONENT */ && isProxy(type)) {
            type = toRaw(type);
            warn$1(`Vue received a Component which was made a reactive object. This can ` +
                `lead to unnecessary performance overhead, and should be avoided by ` +
                `marking the component with \`markRaw\` or using \`shallowRef\` ` +
                `instead of \`ref\`.`, `\nComponent that was made reactive: `, type);
        }
        return createBaseVNode(type, props, children, patchFlag, dynamicProps, shapeFlag, isBlockNode, true);
    }
    function guardReactiveProps(props) {
        if (!props)
            return null;
        return isProxy(props) || InternalObjectKey in props
            ? extend({}, props)
            : props;
    }
    function cloneVNode(vnode, extraProps, mergeRef = false) {
        // This is intentionally NOT using spread or extend to avoid the runtime
        // key enumeration cost.
        const { props, ref, patchFlag, children } = vnode;
        const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props;
        const cloned = {
            __v_isVNode: true,
            __v_skip: true,
            type: vnode.type,
            props: mergedProps,
            key: mergedProps && normalizeKey(mergedProps),
            ref: extraProps && extraProps.ref
                ? // #2078 in the case of <component :is="vnode" ref="extra"/>
                    // if the vnode itself already has a ref, cloneVNode will need to merge
                    // the refs so the single vnode can be set on multiple refs
                    mergeRef && ref
                        ? isArray(ref)
                            ? ref.concat(normalizeRef(extraProps))
                            : [ref, normalizeRef(extraProps)]
                        : normalizeRef(extraProps)
                : ref,
            scopeId: vnode.scopeId,
            slotScopeIds: vnode.slotScopeIds,
            children: patchFlag === -1 /* HOISTED */ && isArray(children)
                ? children.map(deepCloneVNode)
                : children,
            target: vnode.target,
            targetAnchor: vnode.targetAnchor,
            staticCount: vnode.staticCount,
            shapeFlag: vnode.shapeFlag,
            // if the vnode is cloned with extra props, we can no longer assume its
            // existing patch flag to be reliable and need to add the FULL_PROPS flag.
            // note: preserve flag for fragments since they use the flag for children
            // fast paths only.
            patchFlag: extraProps && vnode.type !== Fragment
                ? patchFlag === -1 // hoisted node
                    ? 16 /* FULL_PROPS */
                    : patchFlag | 16 /* FULL_PROPS */
                : patchFlag,
            dynamicProps: vnode.dynamicProps,
            dynamicChildren: vnode.dynamicChildren,
            appContext: vnode.appContext,
            dirs: vnode.dirs,
            transition: vnode.transition,
            // These should technically only be non-null on mounted VNodes. However,
            // they *should* be copied for kept-alive vnodes. So we just always copy
            // them since them being non-null during a mount doesn't affect the logic as
            // they will simply be overwritten.
            component: vnode.component,
            suspense: vnode.suspense,
            ssContent: vnode.ssContent && cloneVNode(vnode.ssContent),
            ssFallback: vnode.ssFallback && cloneVNode(vnode.ssFallback),
            el: vnode.el,
            anchor: vnode.anchor
        };
        return cloned;
    }
    /**
     * Dev only, for HMR of hoisted vnodes reused in v-for
     * https://github.com/vitejs/vite/issues/2022
     */
    function deepCloneVNode(vnode) {
        const cloned = cloneVNode(vnode);
        if (isArray(vnode.children)) {
            cloned.children = vnode.children.map(deepCloneVNode);
        }
        return cloned;
    }
    /**
     * @private
     */
    function createTextVNode(text = ' ', flag = 0) {
        return createVNode(Text, null, text, flag);
    }
    /**
     * @private
     */
    function createStaticVNode(content, numberOfNodes) {
        // A static vnode can contain multiple stringified elements, and the number
        // of elements is necessary for hydration.
        const vnode = createVNode(Static, null, content);
        vnode.staticCount = numberOfNodes;
        return vnode;
    }
    /**
     * @private
     */
    function createCommentVNode(text = '', 
    // when used as the v-else branch, the comment node must be created as a
    // block to ensure correct updates.
    asBlock = false) {
        return asBlock
            ? (openBlock(), createBlock(Comment, null, text))
            : createVNode(Comment, null, text);
    }
    function normalizeVNode(child) {
        if (child == null || typeof child === 'boolean') {
            // empty placeholder
            return createVNode(Comment);
        }
        else if (isArray(child)) {
            // fragment
            return createVNode(Fragment, null, 
            // #3666, avoid reference pollution when reusing vnode
            child.slice());
        }
        else if (typeof child === 'object') {
            // already vnode, this should be the most common since compiled templates
            // always produce all-vnode children arrays
            return cloneIfMounted(child);
        }
        else {
            // strings and numbers
            return createVNode(Text, null, String(child));
        }
    }
    // optimized normalization for template-compiled render fns
    function cloneIfMounted(child) {
        return child.el === null || child.memo ? child : cloneVNode(child);
    }
    function normalizeChildren(vnode, children) {
        let type = 0;
        const { shapeFlag } = vnode;
        if (children == null) {
            children = null;
        }
        else if (isArray(children)) {
            type = 16 /* ARRAY_CHILDREN */;
        }
        else if (typeof children === 'object') {
            if (shapeFlag & (1 /* ELEMENT */ | 64 /* TELEPORT */)) {
                // Normalize slot to plain children for plain element and Teleport
                const slot = children.default;
                if (slot) {
                    // _c marker is added by withCtx() indicating this is a compiled slot
                    slot._c && (slot._d = false);
                    normalizeChildren(vnode, slot());
                    slot._c && (slot._d = true);
                }
                return;
            }
            else {
                type = 32 /* SLOTS_CHILDREN */;
                const slotFlag = children._;
                if (!slotFlag && !(InternalObjectKey in children)) {
                    children._ctx = currentRenderingInstance;
                }
                else if (slotFlag === 3 /* FORWARDED */ && currentRenderingInstance) {
                    // a child component receives forwarded slots from the parent.
                    // its slot type is determined by its parent's slot type.
                    if (currentRenderingInstance.slots._ === 1 /* STABLE */) {
                        children._ = 1 /* STABLE */;
                    }
                    else {
                        children._ = 2 /* DYNAMIC */;
                        vnode.patchFlag |= 1024 /* DYNAMIC_SLOTS */;
                    }
                }
            }
        }
        else if (isFunction(children)) {
            children = { default: children, _ctx: currentRenderingInstance };
            type = 32 /* SLOTS_CHILDREN */;
        }
        else {
            children = String(children);
            // force teleport children to array so it can be moved around
            if (shapeFlag & 64 /* TELEPORT */) {
                type = 16 /* ARRAY_CHILDREN */;
                children = [createTextVNode(children)];
            }
            else {
                type = 8 /* TEXT_CHILDREN */;
            }
        }
        vnode.children = children;
        vnode.shapeFlag |= type;
    }
    function mergeProps(...args) {
        const ret = {};
        for (let i = 0; i < args.length; i++) {
            const toMerge = args[i];
            for (const key in toMerge) {
                if (key === 'class') {
                    if (ret.class !== toMerge.class) {
                        ret.class = normalizeClass([ret.class, toMerge.class]);
                    }
                }
                else if (key === 'style') {
                    ret.style = normalizeStyle([ret.style, toMerge.style]);
                }
                else if (isOn(key)) {
                    const existing = ret[key];
                    const incoming = toMerge[key];
                    if (incoming &&
                        existing !== incoming &&
                        !(isArray(existing) && existing.includes(incoming))) {
                        ret[key] = existing
                            ? [].concat(existing, incoming)
                            : incoming;
                    }
                }
                else if (key !== '') {
                    ret[key] = toMerge[key];
                }
            }
        }
        return ret;
    }
    function invokeVNodeHook(hook, instance, vnode, prevVNode = null) {
        callWithAsyncErrorHandling(hook, instance, 7 /* VNODE_HOOK */, [
            vnode,
            prevVNode
        ]);
    }

    const emptyAppContext = createAppContext();
    let uid$1 = 0;
    function createComponentInstance(vnode, parent, suspense) {
        const type = vnode.type;
        // inherit parent app context - or - if root, adopt from root vnode
        const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;
        const instance = {
            uid: uid$1++,
            vnode,
            type,
            parent,
            appContext,
            root: null,
            next: null,
            subTree: null,
            effect: null,
            update: null,
            scope: new EffectScope(true /* detached */),
            render: null,
            proxy: null,
            exposed: null,
            exposeProxy: null,
            withProxy: null,
            provides: parent ? parent.provides : Object.create(appContext.provides),
            accessCache: null,
            renderCache: [],
            // local resolved assets
            components: null,
            directives: null,
            // resolved props and emits options
            propsOptions: normalizePropsOptions(type, appContext),
            emitsOptions: normalizeEmitsOptions(type, appContext),
            // emit
            emit: null,
            emitted: null,
            // props default value
            propsDefaults: EMPTY_OBJ,
            // inheritAttrs
            inheritAttrs: type.inheritAttrs,
            // state
            ctx: EMPTY_OBJ,
            data: EMPTY_OBJ,
            props: EMPTY_OBJ,
            attrs: EMPTY_OBJ,
            slots: EMPTY_OBJ,
            refs: EMPTY_OBJ,
            setupState: EMPTY_OBJ,
            setupContext: null,
            // suspense related
            suspense,
            suspenseId: suspense ? suspense.pendingId : 0,
            asyncDep: null,
            asyncResolved: false,
            // lifecycle hooks
            // not using enums here because it results in computed properties
            isMounted: false,
            isUnmounted: false,
            isDeactivated: false,
            bc: null,
            c: null,
            bm: null,
            m: null,
            bu: null,
            u: null,
            um: null,
            bum: null,
            da: null,
            a: null,
            rtg: null,
            rtc: null,
            ec: null,
            sp: null
        };
        {
            instance.ctx = createDevRenderContext(instance);
        }
        instance.root = parent ? parent.root : instance;
        instance.emit = emit$1.bind(null, instance);
        // apply custom element special handling
        if (vnode.ce) {
            vnode.ce(instance);
        }
        return instance;
    }
    let currentInstance = null;
    const getCurrentInstance = () => currentInstance || currentRenderingInstance;
    const setCurrentInstance = (instance) => {
        currentInstance = instance;
        instance.scope.on();
    };
    const unsetCurrentInstance = () => {
        currentInstance && currentInstance.scope.off();
        currentInstance = null;
    };
    const isBuiltInTag = /*#__PURE__*/ makeMap('slot,component');
    function validateComponentName(name, config) {
        const appIsNativeTag = config.isNativeTag || NO;
        if (isBuiltInTag(name) || appIsNativeTag(name)) {
            warn$1('Do not use built-in or reserved HTML elements as component id: ' + name);
        }
    }
    function isStatefulComponent(instance) {
        return instance.vnode.shapeFlag & 4 /* STATEFUL_COMPONENT */;
    }
    let isInSSRComponentSetup = false;
    function setupComponent(instance, isSSR = false) {
        isInSSRComponentSetup = isSSR;
        const { props, children } = instance.vnode;
        const isStateful = isStatefulComponent(instance);
        initProps(instance, props, isStateful, isSSR);
        initSlots(instance, children);
        const setupResult = isStateful
            ? setupStatefulComponent(instance, isSSR)
            : undefined;
        isInSSRComponentSetup = false;
        return setupResult;
    }
    function setupStatefulComponent(instance, isSSR) {
        var _a;
        const Component = instance.type;
        {
            if (Component.name) {
                validateComponentName(Component.name, instance.appContext.config);
            }
            if (Component.components) {
                const names = Object.keys(Component.components);
                for (let i = 0; i < names.length; i++) {
                    validateComponentName(names[i], instance.appContext.config);
                }
            }
            if (Component.directives) {
                const names = Object.keys(Component.directives);
                for (let i = 0; i < names.length; i++) {
                    validateDirectiveName(names[i]);
                }
            }
            if (Component.compilerOptions && isRuntimeOnly()) {
                warn$1(`"compilerOptions" is only supported when using a build of Vue that ` +
                    `includes the runtime compiler. Since you are using a runtime-only ` +
                    `build, the options should be passed via your build tool config instead.`);
            }
        }
        // 0. create render proxy property access cache
        instance.accessCache = Object.create(null);
        // 1. create public instance / render proxy
        // also mark it raw so it's never observed
        instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers));
        {
            exposePropsOnRenderContext(instance);
        }
        // 2. call setup()
        const { setup } = Component;
        if (setup) {
            const setupContext = (instance.setupContext =
                setup.length > 1 ? createSetupContext(instance) : null);
            setCurrentInstance(instance);
            pauseTracking();
            const setupResult = callWithErrorHandling(setup, instance, 0 /* SETUP_FUNCTION */, [shallowReadonly(instance.props) , setupContext]);
            resetTracking();
            unsetCurrentInstance();
            if (isPromise(setupResult)) {
                setupResult.then(unsetCurrentInstance, unsetCurrentInstance);
                if (isSSR) {
                    // return the promise so server-renderer can wait on it
                    return setupResult
                        .then((resolvedResult) => {
                        handleSetupResult(instance, resolvedResult, isSSR);
                    })
                        .catch(e => {
                        handleError(e, instance, 0 /* SETUP_FUNCTION */);
                    });
                }
                else {
                    // async setup returned Promise.
                    // bail here and wait for re-entry.
                    instance.asyncDep = setupResult;
                    if (!instance.suspense) {
                        const name = (_a = Component.name) !== null && _a !== void 0 ? _a : 'Anonymous';
                        warn$1(`Component <${name}>: setup function returned a promise, but no ` +
                            `<Suspense> boundary was found in the parent component tree. ` +
                            `A component with async setup() must be nested in a <Suspense> ` +
                            `in order to be rendered.`);
                    }
                }
            }
            else {
                handleSetupResult(instance, setupResult, isSSR);
            }
        }
        else {
            finishComponentSetup(instance, isSSR);
        }
    }
    function handleSetupResult(instance, setupResult, isSSR) {
        if (isFunction(setupResult)) {
            // setup returned an inline render function
            {
                instance.render = setupResult;
            }
        }
        else if (isObject(setupResult)) {
            if (isVNode(setupResult)) {
                warn$1(`setup() should not return VNodes directly - ` +
                    `return a render function instead.`);
            }
            // setup returned bindings.
            // assuming a render function compiled from template is present.
            {
                instance.devtoolsRawSetupState = setupResult;
            }
            instance.setupState = proxyRefs(setupResult);
            {
                exposeSetupStateOnRenderContext(instance);
            }
        }
        else if (setupResult !== undefined) {
            warn$1(`setup() should return an object. Received: ${setupResult === null ? 'null' : typeof setupResult}`);
        }
        finishComponentSetup(instance, isSSR);
    }
    let compile;
    let installWithProxy;
    /**
     * For runtime-dom to register the compiler.
     * Note the exported method uses any to avoid d.ts relying on the compiler types.
     */
    function registerRuntimeCompiler(_compile) {
        compile = _compile;
        installWithProxy = i => {
            if (i.render._rc) {
                i.withProxy = new Proxy(i.ctx, RuntimeCompiledPublicInstanceProxyHandlers);
            }
        };
    }
    // dev only
    const isRuntimeOnly = () => !compile;
    function finishComponentSetup(instance, isSSR, skipOptions) {
        const Component = instance.type;
        // template / render function normalization
        // could be already set when returned from setup()
        if (!instance.render) {
            // only do on-the-fly compile if not in SSR - SSR on-the-fly compilation
            // is done by server-renderer
            if (!isSSR && compile && !Component.render) {
                const template = Component.template;
                if (template) {
                    {
                        startMeasure(instance, `compile`);
                    }
                    const { isCustomElement, compilerOptions } = instance.appContext.config;
                    const { delimiters, compilerOptions: componentCompilerOptions } = Component;
                    const finalCompilerOptions = extend(extend({
                        isCustomElement,
                        delimiters
                    }, compilerOptions), componentCompilerOptions);
                    Component.render = compile(template, finalCompilerOptions);
                    {
                        endMeasure(instance, `compile`);
                    }
                }
            }
            instance.render = (Component.render || NOOP);
            // for runtime-compiled render functions using `with` blocks, the render
            // proxy used needs a different `has` handler which is more performant and
            // also only allows a whitelist of globals to fallthrough.
            if (installWithProxy) {
                installWithProxy(instance);
            }
        }
        // support for 2.x options
        {
            setCurrentInstance(instance);
            pauseTracking();
            applyOptions(instance);
            resetTracking();
            unsetCurrentInstance();
        }
        // warn missing template/render
        // the runtime compilation of template in SSR is done by server-render
        if (!Component.render && instance.render === NOOP && !isSSR) {
            /* istanbul ignore if */
            if (!compile && Component.template) {
                warn$1(`Component provided template option but ` +
                    `runtime compilation is not supported in this build of Vue.` +
                    (` Use "vue.esm-browser.js" instead.`
                            ) /* should not happen */);
            }
            else {
                warn$1(`Component is missing template or render function.`);
            }
        }
    }
    function createAttrsProxy(instance) {
        return new Proxy(instance.attrs, {
                get(target, key) {
                    markAttrsAccessed();
                    track(instance, "get" /* GET */, '$attrs');
                    return target[key];
                },
                set() {
                    warn$1(`setupContext.attrs is readonly.`);
                    return false;
                },
                deleteProperty() {
                    warn$1(`setupContext.attrs is readonly.`);
                    return false;
                }
            }
            );
    }
    function createSetupContext(instance) {
        const expose = exposed => {
            if (instance.exposed) {
                warn$1(`expose() should be called only once per setup().`);
            }
            instance.exposed = exposed || {};
        };
        let attrs;
        {
            // We use getters in dev in case libs like test-utils overwrite instance
            // properties (overwrites should not be done in prod)
            return Object.freeze({
                get attrs() {
                    return attrs || (attrs = createAttrsProxy(instance));
                },
                get slots() {
                    return shallowReadonly(instance.slots);
                },
                get emit() {
                    return (event, ...args) => instance.emit(event, ...args);
                },
                expose
            });
        }
    }
    function getExposeProxy(instance) {
        if (instance.exposed) {
            return (instance.exposeProxy ||
                (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {
                    get(target, key) {
                        if (key in target) {
                            return target[key];
                        }
                        else if (key in publicPropertiesMap) {
                            return publicPropertiesMap[key](instance);
                        }
                    }
                })));
        }
    }
    const classifyRE = /(?:^|[-_])(\w)/g;
    const classify = (str) => str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '');
    function getComponentName(Component, includeInferred = true) {
        return isFunction(Component)
            ? Component.displayName || Component.name
            : Component.name || (includeInferred && Component.__name);
    }
    /* istanbul ignore next */
    function formatComponentName(instance, Component, isRoot = false) {
        let name = getComponentName(Component);
        if (!name && Component.__file) {
            const match = Component.__file.match(/([^/\\]+)\.\w+$/);
            if (match) {
                name = match[1];
            }
        }
        if (!name && instance && instance.parent) {
            // try to infer the name based on reverse resolution
            const inferFromRegistry = (registry) => {
                for (const key in registry) {
                    if (registry[key] === Component) {
                        return key;
                    }
                }
            };
            name =
                inferFromRegistry(instance.components ||
                    instance.parent.type.components) || inferFromRegistry(instance.appContext.components);
        }
        return name ? classify(name) : isRoot ? `App` : `Anonymous`;
    }
    function isClassComponent(value) {
        return isFunction(value) && '__vccOpts' in value;
    }

    const computed$1 = ((getterOrOptions, debugOptions) => {
        // @ts-ignore
        return computed(getterOrOptions, debugOptions, isInSSRComponentSetup);
    });

    // dev only
    const warnRuntimeUsage = (method) => warn$1(`${method}() is a compiler-hint helper that is only usable inside ` +
        `<script setup> of a single file component. Its arguments should be ` +
        `compiled away and passing it at runtime has no effect.`);
    // implementation
    function defineProps() {
        {
            warnRuntimeUsage(`defineProps`);
        }
        return null;
    }
    // implementation
    function defineEmits() {
        {
            warnRuntimeUsage(`defineEmits`);
        }
        return null;
    }
    /**
     * Vue `<script setup>` compiler macro for declaring a component's exposed
     * instance properties when it is accessed by a parent component via template
     * refs.
     *
     * `<script setup>` components are closed by default - i.e. variables inside
     * the `<script setup>` scope is not exposed to parent unless explicitly exposed
     * via `defineExpose`.
     *
     * This is only usable inside `<script setup>`, is compiled away in the
     * output and should **not** be actually called at runtime.
     */
    function defineExpose(exposed) {
        {
            warnRuntimeUsage(`defineExpose`);
        }
    }
    /**
     * Vue `<script setup>` compiler macro for providing props default values when
     * using type-based `defineProps` declaration.
     *
     * Example usage:
     * ```ts
     * withDefaults(defineProps<{
     *   size?: number
     *   labels?: string[]
     * }>(), {
     *   size: 3,
     *   labels: () => ['default label']
     * })
     * ```
     *
     * This is only usable inside `<script setup>`, is compiled away in the output
     * and should **not** be actually called at runtime.
     */
    function withDefaults(props, defaults) {
        {
            warnRuntimeUsage(`withDefaults`);
        }
        return null;
    }
    function useSlots() {
        return getContext$1().slots;
    }
    function useAttrs() {
        return getContext$1().attrs;
    }
    function getContext$1() {
        const i = getCurrentInstance();
        if (!i) {
            warn$1(`useContext() called without active instance.`);
        }
        return i.setupContext || (i.setupContext = createSetupContext(i));
    }
    /**
     * Runtime helper for merging default declarations. Imported by compiled code
     * only.
     * @internal
     */
    function mergeDefaults(raw, defaults) {
        const props = isArray(raw)
            ? raw.reduce((normalized, p) => ((normalized[p] = {}), normalized), {})
            : raw;
        for (const key in defaults) {
            const opt = props[key];
            if (opt) {
                if (isArray(opt) || isFunction(opt)) {
                    props[key] = { type: opt, default: defaults[key] };
                }
                else {
                    opt.default = defaults[key];
                }
            }
            else if (opt === null) {
                props[key] = { default: defaults[key] };
            }
            else {
                warn$1(`props default key "${key}" has no corresponding declaration.`);
            }
        }
        return props;
    }
    /**
     * Used to create a proxy for the rest element when destructuring props with
     * defineProps().
     * @internal
     */
    function createPropsRestProxy(props, excludedKeys) {
        const ret = {};
        for (const key in props) {
            if (!excludedKeys.includes(key)) {
                Object.defineProperty(ret, key, {
                    enumerable: true,
                    get: () => props[key]
                });
            }
        }
        return ret;
    }
    /**
     * `<script setup>` helper for persisting the current instance context over
     * async/await flows.
     *
     * `@vue/compiler-sfc` converts the following:
     *
     * ```ts
     * const x = await foo()
     * ```
     *
     * into:
     *
     * ```ts
     * let __temp, __restore
     * const x = (([__temp, __restore] = withAsyncContext(() => foo())),__temp=await __temp,__restore(),__temp)
     * ```
     * @internal
     */
    function withAsyncContext(getAwaitable) {
        const ctx = getCurrentInstance();
        if (!ctx) {
            warn$1(`withAsyncContext called without active current instance. ` +
                `This is likely a bug.`);
        }
        let awaitable = getAwaitable();
        unsetCurrentInstance();
        if (isPromise(awaitable)) {
            awaitable = awaitable.catch(e => {
                setCurrentInstance(ctx);
                throw e;
            });
        }
        return [awaitable, () => setCurrentInstance(ctx)];
    }

    // Actual implementation
    function h$2(type, propsOrChildren, children) {
        const l = arguments.length;
        if (l === 2) {
            if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
                // single vnode without props
                if (isVNode(propsOrChildren)) {
                    return createVNode(type, null, [propsOrChildren]);
                }
                // props without children
                return createVNode(type, propsOrChildren);
            }
            else {
                // omit props
                return createVNode(type, null, propsOrChildren);
            }
        }
        else {
            if (l > 3) {
                children = Array.prototype.slice.call(arguments, 2);
            }
            else if (l === 3 && isVNode(children)) {
                children = [children];
            }
            return createVNode(type, propsOrChildren, children);
        }
    }

    const ssrContextKey = Symbol(`ssrContext` );
    const useSSRContext = () => {
        {
            const ctx = inject(ssrContextKey);
            if (!ctx) {
                warn$1(`Server rendering context not provided. Make sure to only call ` +
                    `useSSRContext() conditionally in the server build.`);
            }
            return ctx;
        }
    };

    function initCustomFormatter() {
        /* eslint-disable no-restricted-globals */
        if (typeof window === 'undefined') {
            return;
        }
        const vueStyle = { style: 'color:#3ba776' };
        const numberStyle = { style: 'color:#0b1bc9' };
        const stringStyle = { style: 'color:#b62e24' };
        const keywordStyle = { style: 'color:#9d288c' };
        // custom formatter for Chrome
        // https://www.mattzeunert.com/2016/02/19/custom-chrome-devtools-object-formatters.html
        const formatter = {
            header(obj) {
                // TODO also format ComponentPublicInstance & ctx.slots/attrs in setup
                if (!isObject(obj)) {
                    return null;
                }
                if (obj.__isVue) {
                    return ['div', vueStyle, `VueInstance`];
                }
                else if (isRef(obj)) {
                    return [
                        'div',
                        {},
                        ['span', vueStyle, genRefFlag(obj)],
                        '<',
                        formatValue(obj.value),
                        `>`
                    ];
                }
                else if (isReactive(obj)) {
                    return [
                        'div',
                        {},
                        ['span', vueStyle, isShallow(obj) ? 'ShallowReactive' : 'Reactive'],
                        '<',
                        formatValue(obj),
                        `>${isReadonly(obj) ? ` (readonly)` : ``}`
                    ];
                }
                else if (isReadonly(obj)) {
                    return [
                        'div',
                        {},
                        ['span', vueStyle, isShallow(obj) ? 'ShallowReadonly' : 'Readonly'],
                        '<',
                        formatValue(obj),
                        '>'
                    ];
                }
                return null;
            },
            hasBody(obj) {
                return obj && obj.__isVue;
            },
            body(obj) {
                if (obj && obj.__isVue) {
                    return [
                        'div',
                        {},
                        ...formatInstance(obj.$)
                    ];
                }
            }
        };
        function formatInstance(instance) {
            const blocks = [];
            if (instance.type.props && instance.props) {
                blocks.push(createInstanceBlock('props', toRaw(instance.props)));
            }
            if (instance.setupState !== EMPTY_OBJ) {
                blocks.push(createInstanceBlock('setup', instance.setupState));
            }
            if (instance.data !== EMPTY_OBJ) {
                blocks.push(createInstanceBlock('data', toRaw(instance.data)));
            }
            const computed = extractKeys(instance, 'computed');
            if (computed) {
                blocks.push(createInstanceBlock('computed', computed));
            }
            const injected = extractKeys(instance, 'inject');
            if (injected) {
                blocks.push(createInstanceBlock('injected', injected));
            }
            blocks.push([
                'div',
                {},
                [
                    'span',
                    {
                        style: keywordStyle.style + ';opacity:0.66'
                    },
                    '$ (internal): '
                ],
                ['object', { object: instance }]
            ]);
            return blocks;
        }
        function createInstanceBlock(type, target) {
            target = extend({}, target);
            if (!Object.keys(target).length) {
                return ['span', {}];
            }
            return [
                'div',
                { style: 'line-height:1.25em;margin-bottom:0.6em' },
                [
                    'div',
                    {
                        style: 'color:#476582'
                    },
                    type
                ],
                [
                    'div',
                    {
                        style: 'padding-left:1.25em'
                    },
                    ...Object.keys(target).map(key => {
                        return [
                            'div',
                            {},
                            ['span', keywordStyle, key + ': '],
                            formatValue(target[key], false)
                        ];
                    })
                ]
            ];
        }
        function formatValue(v, asRaw = true) {
            if (typeof v === 'number') {
                return ['span', numberStyle, v];
            }
            else if (typeof v === 'string') {
                return ['span', stringStyle, JSON.stringify(v)];
            }
            else if (typeof v === 'boolean') {
                return ['span', keywordStyle, v];
            }
            else if (isObject(v)) {
                return ['object', { object: asRaw ? toRaw(v) : v }];
            }
            else {
                return ['span', stringStyle, String(v)];
            }
        }
        function extractKeys(instance, type) {
            const Comp = instance.type;
            if (isFunction(Comp)) {
                return;
            }
            const extracted = {};
            for (const key in instance.ctx) {
                if (isKeyOfType(Comp, key, type)) {
                    extracted[key] = instance.ctx[key];
                }
            }
            return extracted;
        }
        function isKeyOfType(Comp, key, type) {
            const opts = Comp[type];
            if ((isArray(opts) && opts.includes(key)) ||
                (isObject(opts) && key in opts)) {
                return true;
            }
            if (Comp.extends && isKeyOfType(Comp.extends, key, type)) {
                return true;
            }
            if (Comp.mixins && Comp.mixins.some(m => isKeyOfType(m, key, type))) {
                return true;
            }
        }
        function genRefFlag(v) {
            if (isShallow(v)) {
                return `ShallowRef`;
            }
            if (v.effect) {
                return `ComputedRef`;
            }
            return `Ref`;
        }
        if (window.devtoolsFormatters) {
            window.devtoolsFormatters.push(formatter);
        }
        else {
            window.devtoolsFormatters = [formatter];
        }
    }

    function withMemo(memo, render, cache, index) {
        const cached = cache[index];
        if (cached && isMemoSame(cached, memo)) {
            return cached;
        }
        const ret = render();
        // shallow clone
        ret.memo = memo.slice();
        return (cache[index] = ret);
    }
    function isMemoSame(cached, memo) {
        const prev = cached.memo;
        if (prev.length != memo.length) {
            return false;
        }
        for (let i = 0; i < prev.length; i++) {
            if (hasChanged(prev[i], memo[i])) {
                return false;
            }
        }
        // make sure to let parent block track it when returning cached
        if (isBlockTreeEnabled > 0 && currentBlock) {
            currentBlock.push(cached);
        }
        return true;
    }

    // Core API ------------------------------------------------------------------
    const version = "3.2.37";
    /**
     * SSR utils for \@vue/server-renderer. Only exposed in ssr-possible builds.
     * @internal
     */
    const ssrUtils = (null);
    /**
     * @internal only exposed in compat builds
     */
    const resolveFilter = null;
    /**
     * @internal only exposed in compat builds.
     */
    const compatUtils = (null);

    const svgNS = 'http://www.w3.org/2000/svg';
    const doc = (typeof document !== 'undefined' ? document : null);
    const templateContainer = doc && /*#__PURE__*/ doc.createElement('template');
    const nodeOps = {
        insert: (child, parent, anchor) => {
            parent.insertBefore(child, anchor || null);
        },
        remove: child => {
            const parent = child.parentNode;
            if (parent) {
                parent.removeChild(child);
            }
        },
        createElement: (tag, isSVG, is, props) => {
            const el = isSVG
                ? doc.createElementNS(svgNS, tag)
                : doc.createElement(tag, is ? { is } : undefined);
            if (tag === 'select' && props && props.multiple != null) {
                el.setAttribute('multiple', props.multiple);
            }
            return el;
        },
        createText: text => doc.createTextNode(text),
        createComment: text => doc.createComment(text),
        setText: (node, text) => {
            node.nodeValue = text;
        },
        setElementText: (el, text) => {
            el.textContent = text;
        },
        parentNode: node => node.parentNode,
        nextSibling: node => node.nextSibling,
        querySelector: selector => doc.querySelector(selector),
        setScopeId(el, id) {
            el.setAttribute(id, '');
        },
        cloneNode(el) {
            const cloned = el.cloneNode(true);
            // #3072
            // - in `patchDOMProp`, we store the actual value in the `el._value` property.
            // - normally, elements using `:value` bindings will not be hoisted, but if
            //   the bound value is a constant, e.g. `:value="true"` - they do get
            //   hoisted.
            // - in production, hoisted nodes are cloned when subsequent inserts, but
            //   cloneNode() does not copy the custom property we attached.
            // - This may need to account for other custom DOM properties we attach to
            //   elements in addition to `_value` in the future.
            if (`_value` in el) {
                cloned._value = el._value;
            }
            return cloned;
        },
        // __UNSAFE__
        // Reason: innerHTML.
        // Static content here can only come from compiled templates.
        // As long as the user only uses trusted templates, this is safe.
        insertStaticContent(content, parent, anchor, isSVG, start, end) {
            // <parent> before | first ... last | anchor </parent>
            const before = anchor ? anchor.previousSibling : parent.lastChild;
            // #5308 can only take cached path if:
            // - has a single root node
            // - nextSibling info is still available
            if (start && (start === end || start.nextSibling)) {
                // cached
                while (true) {
                    parent.insertBefore(start.cloneNode(true), anchor);
                    if (start === end || !(start = start.nextSibling))
                        break;
                }
            }
            else {
                // fresh insert
                templateContainer.innerHTML = isSVG ? `<svg>${content}</svg>` : content;
                const template = templateContainer.content;
                if (isSVG) {
                    // remove outer svg wrapper
                    const wrapper = template.firstChild;
                    while (wrapper.firstChild) {
                        template.appendChild(wrapper.firstChild);
                    }
                    template.removeChild(wrapper);
                }
                parent.insertBefore(template, anchor);
            }
            return [
                // first
                before ? before.nextSibling : parent.firstChild,
                // last
                anchor ? anchor.previousSibling : parent.lastChild
            ];
        }
    };

    // compiler should normalize class + :class bindings on the same element
    // into a single binding ['staticClass', dynamic]
    function patchClass(el, value, isSVG) {
        // directly setting className should be faster than setAttribute in theory
        // if this is an element during a transition, take the temporary transition
        // classes into account.
        const transitionClasses = el._vtc;
        if (transitionClasses) {
            value = (value ? [value, ...transitionClasses] : [...transitionClasses]).join(' ');
        }
        if (value == null) {
            el.removeAttribute('class');
        }
        else if (isSVG) {
            el.setAttribute('class', value);
        }
        else {
            el.className = value;
        }
    }

    function patchStyle(el, prev, next) {
        const style = el.style;
        const isCssString = isString(next);
        if (next && !isCssString) {
            for (const key in next) {
                setStyle(style, key, next[key]);
            }
            if (prev && !isString(prev)) {
                for (const key in prev) {
                    if (next[key] == null) {
                        setStyle(style, key, '');
                    }
                }
            }
        }
        else {
            const currentDisplay = style.display;
            if (isCssString) {
                if (prev !== next) {
                    style.cssText = next;
                }
            }
            else if (prev) {
                el.removeAttribute('style');
            }
            // indicates that the `display` of the element is controlled by `v-show`,
            // so we always keep the current `display` value regardless of the `style`
            // value, thus handing over control to `v-show`.
            if ('_vod' in el) {
                style.display = currentDisplay;
            }
        }
    }
    const importantRE = /\s*!important$/;
    function setStyle(style, name, val) {
        if (isArray(val)) {
            val.forEach(v => setStyle(style, name, v));
        }
        else {
            if (val == null)
                val = '';
            if (name.startsWith('--')) {
                // custom property definition
                style.setProperty(name, val);
            }
            else {
                const prefixed = autoPrefix(style, name);
                if (importantRE.test(val)) {
                    // !important
                    style.setProperty(hyphenate(prefixed), val.replace(importantRE, ''), 'important');
                }
                else {
                    style[prefixed] = val;
                }
            }
        }
    }
    const prefixes = ['Webkit', 'Moz', 'ms'];
    const prefixCache = {};
    function autoPrefix(style, rawName) {
        const cached = prefixCache[rawName];
        if (cached) {
            return cached;
        }
        let name = camelize(rawName);
        if (name !== 'filter' && name in style) {
            return (prefixCache[rawName] = name);
        }
        name = capitalize(name);
        for (let i = 0; i < prefixes.length; i++) {
            const prefixed = prefixes[i] + name;
            if (prefixed in style) {
                return (prefixCache[rawName] = prefixed);
            }
        }
        return rawName;
    }

    const xlinkNS = 'http://www.w3.org/1999/xlink';
    function patchAttr(el, key, value, isSVG, instance) {
        if (isSVG && key.startsWith('xlink:')) {
            if (value == null) {
                el.removeAttributeNS(xlinkNS, key.slice(6, key.length));
            }
            else {
                el.setAttributeNS(xlinkNS, key, value);
            }
        }
        else {
            // note we are only checking boolean attributes that don't have a
            // corresponding dom prop of the same name here.
            const isBoolean = isSpecialBooleanAttr(key);
            if (value == null || (isBoolean && !includeBooleanAttr(value))) {
                el.removeAttribute(key);
            }
            else {
                el.setAttribute(key, isBoolean ? '' : value);
            }
        }
    }

    // __UNSAFE__
    // functions. The user is responsible for using them with only trusted content.
    function patchDOMProp(el, key, value, 
    // the following args are passed only due to potential innerHTML/textContent
    // overriding existing VNodes, in which case the old tree must be properly
    // unmounted.
    prevChildren, parentComponent, parentSuspense, unmountChildren) {
        if (key === 'innerHTML' || key === 'textContent') {
            if (prevChildren) {
                unmountChildren(prevChildren, parentComponent, parentSuspense);
            }
            el[key] = value == null ? '' : value;
            return;
        }
        if (key === 'value' &&
            el.tagName !== 'PROGRESS' &&
            // custom elements may use _value internally
            !el.tagName.includes('-')) {
            // store value as _value as well since
            // non-string values will be stringified.
            el._value = value;
            const newValue = value == null ? '' : value;
            if (el.value !== newValue ||
                // #4956: always set for OPTION elements because its value falls back to
                // textContent if no value attribute is present. And setting .value for
                // OPTION has no side effect
                el.tagName === 'OPTION') {
                el.value = newValue;
            }
            if (value == null) {
                el.removeAttribute(key);
            }
            return;
        }
        let needRemove = false;
        if (value === '' || value == null) {
            const type = typeof el[key];
            if (type === 'boolean') {
                // e.g. <select multiple> compiles to { multiple: '' }
                value = includeBooleanAttr(value);
            }
            else if (value == null && type === 'string') {
                // e.g. <div :id="null">
                value = '';
                needRemove = true;
            }
            else if (type === 'number') {
                // e.g. <img :width="null">
                // the value of some IDL attr must be greater than 0, e.g. input.size = 0 -> error
                value = 0;
                needRemove = true;
            }
        }
        // some properties perform value validation and throw,
        // some properties has getter, no setter, will error in 'use strict'
        // eg. <select :type="null"></select> <select :willValidate="null"></select>
        try {
            el[key] = value;
        }
        catch (e) {
            {
                warn$1(`Failed setting prop "${key}" on <${el.tagName.toLowerCase()}>: ` +
                    `value ${value} is invalid.`, e);
            }
        }
        needRemove && el.removeAttribute(key);
    }

    // Async edge case fix requires storing an event listener's attach timestamp.
    const [_getNow, skipTimestampCheck] = /*#__PURE__*/ (() => {
        let _getNow = Date.now;
        let skipTimestampCheck = false;
        if (typeof window !== 'undefined') {
            // Determine what event timestamp the browser is using. Annoyingly, the
            // timestamp can either be hi-res (relative to page load) or low-res
            // (relative to UNIX epoch), so in order to compare time we have to use the
            // same timestamp type when saving the flush timestamp.
            if (Date.now() > document.createEvent('Event').timeStamp) {
                // if the low-res timestamp which is bigger than the event timestamp
                // (which is evaluated AFTER) it means the event is using a hi-res timestamp,
                // and we need to use the hi-res version for event listeners as well.
                _getNow = performance.now.bind(performance);
            }
            // #3485: Firefox <= 53 has incorrect Event.timeStamp implementation
            // and does not fire microtasks in between event propagation, so safe to exclude.
            const ffMatch = navigator.userAgent.match(/firefox\/(\d+)/i);
            skipTimestampCheck = !!(ffMatch && Number(ffMatch[1]) <= 53);
        }
        return [_getNow, skipTimestampCheck];
    })();
    // To avoid the overhead of repeatedly calling performance.now(), we cache
    // and use the same timestamp for all event listeners attached in the same tick.
    let cachedNow = 0;
    const p$2 = /*#__PURE__*/ Promise.resolve();
    const reset = () => {
        cachedNow = 0;
    };
    const getNow = () => cachedNow || (p$2.then(reset), (cachedNow = _getNow()));
    function addEventListener(el, event, handler, options) {
        el.addEventListener(event, handler, options);
    }
    function removeEventListener(el, event, handler, options) {
        el.removeEventListener(event, handler, options);
    }
    function patchEvent(el, rawName, prevValue, nextValue, instance = null) {
        // vei = vue event invokers
        const invokers = el._vei || (el._vei = {});
        const existingInvoker = invokers[rawName];
        if (nextValue && existingInvoker) {
            // patch
            existingInvoker.value = nextValue;
        }
        else {
            const [name, options] = parseName(rawName);
            if (nextValue) {
                // add
                const invoker = (invokers[rawName] = createInvoker(nextValue, instance));
                addEventListener(el, name, invoker, options);
            }
            else if (existingInvoker) {
                // remove
                removeEventListener(el, name, existingInvoker, options);
                invokers[rawName] = undefined;
            }
        }
    }
    const optionsModifierRE = /(?:Once|Passive|Capture)$/;
    function parseName(name) {
        let options;
        if (optionsModifierRE.test(name)) {
            options = {};
            let m;
            while ((m = name.match(optionsModifierRE))) {
                name = name.slice(0, name.length - m[0].length);
                options[m[0].toLowerCase()] = true;
            }
        }
        return [hyphenate(name.slice(2)), options];
    }
    function createInvoker(initialValue, instance) {
        const invoker = (e) => {
            // async edge case #6566: inner click event triggers patch, event handler
            // attached to outer element during patch, and triggered again. This
            // happens because browsers fire microtask ticks between event propagation.
            // the solution is simple: we save the timestamp when a handler is attached,
            // and the handler would only fire if the event passed to it was fired
            // AFTER it was attached.
            const timeStamp = e.timeStamp || _getNow();
            if (skipTimestampCheck || timeStamp >= invoker.attached - 1) {
                callWithAsyncErrorHandling(patchStopImmediatePropagation(e, invoker.value), instance, 5 /* NATIVE_EVENT_HANDLER */, [e]);
            }
        };
        invoker.value = initialValue;
        invoker.attached = getNow();
        return invoker;
    }
    function patchStopImmediatePropagation(e, value) {
        if (isArray(value)) {
            const originalStop = e.stopImmediatePropagation;
            e.stopImmediatePropagation = () => {
                originalStop.call(e);
                e._stopped = true;
            };
            return value.map(fn => (e) => !e._stopped && fn && fn(e));
        }
        else {
            return value;
        }
    }

    const nativeOnRE = /^on[a-z]/;
    const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => {
        if (key === 'class') {
            patchClass(el, nextValue, isSVG);
        }
        else if (key === 'style') {
            patchStyle(el, prevValue, nextValue);
        }
        else if (isOn(key)) {
            // ignore v-model listeners
            if (!isModelListener(key)) {
                patchEvent(el, key, prevValue, nextValue, parentComponent);
            }
        }
        else if (key[0] === '.'
            ? ((key = key.slice(1)), true)
            : key[0] === '^'
                ? ((key = key.slice(1)), false)
                : shouldSetAsProp(el, key, nextValue, isSVG)) {
            patchDOMProp(el, key, nextValue, prevChildren, parentComponent, parentSuspense, unmountChildren);
        }
        else {
            // special case for <input v-model type="checkbox"> with
            // :true-value & :false-value
            // store value as dom properties since non-string values will be
            // stringified.
            if (key === 'true-value') {
                el._trueValue = nextValue;
            }
            else if (key === 'false-value') {
                el._falseValue = nextValue;
            }
            patchAttr(el, key, nextValue, isSVG);
        }
    };
    function shouldSetAsProp(el, key, value, isSVG) {
        if (isSVG) {
            // most keys must be set as attribute on svg elements to work
            // ...except innerHTML & textContent
            if (key === 'innerHTML' || key === 'textContent') {
                return true;
            }
            // or native onclick with function values
            if (key in el && nativeOnRE.test(key) && isFunction(value)) {
                return true;
            }
            return false;
        }
        // these are enumerated attrs, however their corresponding DOM properties
        // are actually booleans - this leads to setting it with a string "false"
        // value leading it to be coerced to `true`, so we need to always treat
        // them as attributes.
        // Note that `contentEditable` doesn't have this problem: its DOM
        // property is also enumerated string values.
        if (key === 'spellcheck' || key === 'draggable' || key === 'translate') {
            return false;
        }
        // #1787, #2840 form property on form elements is readonly and must be set as
        // attribute.
        if (key === 'form') {
            return false;
        }
        // #1526 <input list> must be set as attribute
        if (key === 'list' && el.tagName === 'INPUT') {
            return false;
        }
        // #2766 <textarea type> must be set as attribute
        if (key === 'type' && el.tagName === 'TEXTAREA') {
            return false;
        }
        // native onclick with string value, must be set as attribute
        if (nativeOnRE.test(key) && isString(value)) {
            return false;
        }
        return key in el;
    }

    function defineCustomElement(options, hydrate) {
        const Comp = defineComponent(options);
        class VueCustomElement extends VueElement {
            constructor(initialProps) {
                super(Comp, initialProps, hydrate);
            }
        }
        VueCustomElement.def = Comp;
        return VueCustomElement;
    }
    const defineSSRCustomElement = ((options) => {
        // @ts-ignore
        return defineCustomElement(options, hydrate);
    });
    const BaseClass = (typeof HTMLElement !== 'undefined' ? HTMLElement : class {
    });
    class VueElement extends BaseClass {
        constructor(_def, _props = {}, hydrate) {
            super();
            this._def = _def;
            this._props = _props;
            /**
             * @internal
             */
            this._instance = null;
            this._connected = false;
            this._resolved = false;
            this._numberProps = null;
            if (this.shadowRoot && hydrate) {
                hydrate(this._createVNode(), this.shadowRoot);
            }
            else {
                if (this.shadowRoot) {
                    warn$1(`Custom element has pre-rendered declarative shadow root but is not ` +
                        `defined as hydratable. Use \`defineSSRCustomElement\`.`);
                }
                this.attachShadow({ mode: 'open' });
            }
        }
        connectedCallback() {
            this._connected = true;
            if (!this._instance) {
                this._resolveDef();
            }
        }
        disconnectedCallback() {
            this._connected = false;
            nextTick(() => {
                if (!this._connected) {
                    render$l(null, this.shadowRoot);
                    this._instance = null;
                }
            });
        }
        /**
         * resolve inner component definition (handle possible async component)
         */
        _resolveDef() {
            if (this._resolved) {
                return;
            }
            this._resolved = true;
            // set initial attrs
            for (let i = 0; i < this.attributes.length; i++) {
                this._setAttr(this.attributes[i].name);
            }
            // watch future attr changes
            new MutationObserver(mutations => {
                for (const m of mutations) {
                    this._setAttr(m.attributeName);
                }
            }).observe(this, { attributes: true });
            const resolve = (def) => {
                const { props, styles } = def;
                const hasOptions = !isArray(props);
                const rawKeys = props ? (hasOptions ? Object.keys(props) : props) : [];
                // cast Number-type props set before resolve
                let numberProps;
                if (hasOptions) {
                    for (const key in this._props) {
                        const opt = props[key];
                        if (opt === Number || (opt && opt.type === Number)) {
                            this._props[key] = toNumber(this._props[key]);
                            (numberProps || (numberProps = Object.create(null)))[key] = true;
                        }
                    }
                }
                this._numberProps = numberProps;
                // check if there are props set pre-upgrade or connect
                for (const key of Object.keys(this)) {
                    if (key[0] !== '_') {
                        this._setProp(key, this[key], true, false);
                    }
                }
                // defining getter/setters on prototype
                for (const key of rawKeys.map(camelize)) {
                    Object.defineProperty(this, key, {
                        get() {
                            return this._getProp(key);
                        },
                        set(val) {
                            this._setProp(key, val);
                        }
                    });
                }
                // apply CSS
                this._applyStyles(styles);
                // initial render
                this._update();
            };
            const asyncDef = this._def.__asyncLoader;
            if (asyncDef) {
                asyncDef().then(resolve);
            }
            else {
                resolve(this._def);
            }
        }
        _setAttr(key) {
            let value = this.getAttribute(key);
            if (this._numberProps && this._numberProps[key]) {
                value = toNumber(value);
            }
            this._setProp(camelize(key), value, false);
        }
        /**
         * @internal
         */
        _getProp(key) {
            return this._props[key];
        }
        /**
         * @internal
         */
        _setProp(key, val, shouldReflect = true, shouldUpdate = true) {
            if (val !== this._props[key]) {
                this._props[key] = val;
                if (shouldUpdate && this._instance) {
                    this._update();
                }
                // reflect
                if (shouldReflect) {
                    if (val === true) {
                        this.setAttribute(hyphenate(key), '');
                    }
                    else if (typeof val === 'string' || typeof val === 'number') {
                        this.setAttribute(hyphenate(key), val + '');
                    }
                    else if (!val) {
                        this.removeAttribute(hyphenate(key));
                    }
                }
            }
        }
        _update() {
            render$l(this._createVNode(), this.shadowRoot);
        }
        _createVNode() {
            const vnode = createVNode(this._def, extend({}, this._props));
            if (!this._instance) {
                vnode.ce = instance => {
                    this._instance = instance;
                    instance.isCE = true;
                    // HMR
                    {
                        instance.ceReload = newStyles => {
                            // always reset styles
                            if (this._styles) {
                                this._styles.forEach(s => this.shadowRoot.removeChild(s));
                                this._styles.length = 0;
                            }
                            this._applyStyles(newStyles);
                            // if this is an async component, ceReload is called from the inner
                            // component so no need to reload the async wrapper
                            if (!this._def.__asyncLoader) {
                                // reload
                                this._instance = null;
                                this._update();
                            }
                        };
                    }
                    // intercept emit
                    instance.emit = (event, ...args) => {
                        this.dispatchEvent(new CustomEvent(event, {
                            detail: args
                        }));
                    };
                    // locate nearest Vue custom element parent for provide/inject
                    let parent = this;
                    while ((parent =
                        parent && (parent.parentNode || parent.host))) {
                        if (parent instanceof VueElement) {
                            instance.parent = parent._instance;
                            break;
                        }
                    }
                };
            }
            return vnode;
        }
        _applyStyles(styles) {
            if (styles) {
                styles.forEach(css => {
                    const s = document.createElement('style');
                    s.textContent = css;
                    this.shadowRoot.appendChild(s);
                    // record for HMR
                    {
                        (this._styles || (this._styles = [])).push(s);
                    }
                });
            }
        }
    }

    function useCssModule(name = '$style') {
        /* istanbul ignore else */
        {
            const instance = getCurrentInstance();
            if (!instance) {
                warn$1(`useCssModule must be called inside setup()`);
                return EMPTY_OBJ;
            }
            const modules = instance.type.__cssModules;
            if (!modules) {
                warn$1(`Current instance does not have CSS modules injected.`);
                return EMPTY_OBJ;
            }
            const mod = modules[name];
            if (!mod) {
                warn$1(`Current instance does not have CSS module named "${name}".`);
                return EMPTY_OBJ;
            }
            return mod;
        }
    }

    /**
     * Runtime helper for SFC's CSS variable injection feature.
     * @private
     */
    function useCssVars(getter) {
        const instance = getCurrentInstance();
        /* istanbul ignore next */
        if (!instance) {
            warn$1(`useCssVars is called without current active component instance.`);
            return;
        }
        const setVars = () => setVarsOnVNode(instance.subTree, getter(instance.proxy));
        watchPostEffect(setVars);
        onMounted(() => {
            const ob = new MutationObserver(setVars);
            ob.observe(instance.subTree.el.parentNode, { childList: true });
            onUnmounted(() => ob.disconnect());
        });
    }
    function setVarsOnVNode(vnode, vars) {
        if (vnode.shapeFlag & 128 /* SUSPENSE */) {
            const suspense = vnode.suspense;
            vnode = suspense.activeBranch;
            if (suspense.pendingBranch && !suspense.isHydrating) {
                suspense.effects.push(() => {
                    setVarsOnVNode(suspense.activeBranch, vars);
                });
            }
        }
        // drill down HOCs until it's a non-component vnode
        while (vnode.component) {
            vnode = vnode.component.subTree;
        }
        if (vnode.shapeFlag & 1 /* ELEMENT */ && vnode.el) {
            setVarsOnNode(vnode.el, vars);
        }
        else if (vnode.type === Fragment) {
            vnode.children.forEach(c => setVarsOnVNode(c, vars));
        }
        else if (vnode.type === Static) {
            let { el, anchor } = vnode;
            while (el) {
                setVarsOnNode(el, vars);
                if (el === anchor)
                    break;
                el = el.nextSibling;
            }
        }
    }
    function setVarsOnNode(el, vars) {
        if (el.nodeType === 1) {
            const style = el.style;
            for (const key in vars) {
                style.setProperty(`--${key}`, vars[key]);
            }
        }
    }

    const TRANSITION = 'transition';
    const ANIMATION = 'animation';
    // DOM Transition is a higher-order-component based on the platform-agnostic
    // base Transition component, with DOM-specific logic.
    const Transition = (props, { slots }) => h$2(BaseTransition, resolveTransitionProps(props), slots);
    Transition.displayName = 'Transition';
    const DOMTransitionPropsValidators = {
        name: String,
        type: String,
        css: {
            type: Boolean,
            default: true
        },
        duration: [String, Number, Object],
        enterFromClass: String,
        enterActiveClass: String,
        enterToClass: String,
        appearFromClass: String,
        appearActiveClass: String,
        appearToClass: String,
        leaveFromClass: String,
        leaveActiveClass: String,
        leaveToClass: String
    };
    const TransitionPropsValidators = (Transition.props =
        /*#__PURE__*/ extend({}, BaseTransition.props, DOMTransitionPropsValidators));
    /**
     * #3227 Incoming hooks may be merged into arrays when wrapping Transition
     * with custom HOCs.
     */
    const callHook$1 = (hook, args = []) => {
        if (isArray(hook)) {
            hook.forEach(h => h(...args));
        }
        else if (hook) {
            hook(...args);
        }
    };
    /**
     * Check if a hook expects a callback (2nd arg), which means the user
     * intends to explicitly control the end of the transition.
     */
    const hasExplicitCallback = (hook) => {
        return hook
            ? isArray(hook)
                ? hook.some(h => h.length > 1)
                : hook.length > 1
            : false;
    };
    function resolveTransitionProps(rawProps) {
        const baseProps = {};
        for (const key in rawProps) {
            if (!(key in DOMTransitionPropsValidators)) {
                baseProps[key] = rawProps[key];
            }
        }
        if (rawProps.css === false) {
            return baseProps;
        }
        const { name = 'v', type, duration, enterFromClass = `${name}-enter-from`, enterActiveClass = `${name}-enter-active`, enterToClass = `${name}-enter-to`, appearFromClass = enterFromClass, appearActiveClass = enterActiveClass, appearToClass = enterToClass, leaveFromClass = `${name}-leave-from`, leaveActiveClass = `${name}-leave-active`, leaveToClass = `${name}-leave-to` } = rawProps;
        const durations = normalizeDuration(duration);
        const enterDuration = durations && durations[0];
        const leaveDuration = durations && durations[1];
        const { onBeforeEnter, onEnter, onEnterCancelled, onLeave, onLeaveCancelled, onBeforeAppear = onBeforeEnter, onAppear = onEnter, onAppearCancelled = onEnterCancelled } = baseProps;
        const finishEnter = (el, isAppear, done) => {
            removeTransitionClass(el, isAppear ? appearToClass : enterToClass);
            removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass);
            done && done();
        };
        const finishLeave = (el, done) => {
            el._isLeaving = false;
            removeTransitionClass(el, leaveFromClass);
            removeTransitionClass(el, leaveToClass);
            removeTransitionClass(el, leaveActiveClass);
            done && done();
        };
        const makeEnterHook = (isAppear) => {
            return (el, done) => {
                const hook = isAppear ? onAppear : onEnter;
                const resolve = () => finishEnter(el, isAppear, done);
                callHook$1(hook, [el, resolve]);
                nextFrame(() => {
                    removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass);
                    addTransitionClass(el, isAppear ? appearToClass : enterToClass);
                    if (!hasExplicitCallback(hook)) {
                        whenTransitionEnds(el, type, enterDuration, resolve);
                    }
                });
            };
        };
        return extend(baseProps, {
            onBeforeEnter(el) {
                callHook$1(onBeforeEnter, [el]);
                addTransitionClass(el, enterFromClass);
                addTransitionClass(el, enterActiveClass);
            },
            onBeforeAppear(el) {
                callHook$1(onBeforeAppear, [el]);
                addTransitionClass(el, appearFromClass);
                addTransitionClass(el, appearActiveClass);
            },
            onEnter: makeEnterHook(false),
            onAppear: makeEnterHook(true),
            onLeave(el, done) {
                el._isLeaving = true;
                const resolve = () => finishLeave(el, done);
                addTransitionClass(el, leaveFromClass);
                // force reflow so *-leave-from classes immediately take effect (#2593)
                forceReflow();
                addTransitionClass(el, leaveActiveClass);
                nextFrame(() => {
                    if (!el._isLeaving) {
                        // cancelled
                        return;
                    }
                    removeTransitionClass(el, leaveFromClass);
                    addTransitionClass(el, leaveToClass);
                    if (!hasExplicitCallback(onLeave)) {
                        whenTransitionEnds(el, type, leaveDuration, resolve);
                    }
                });
                callHook$1(onLeave, [el, resolve]);
            },
            onEnterCancelled(el) {
                finishEnter(el, false);
                callHook$1(onEnterCancelled, [el]);
            },
            onAppearCancelled(el) {
                finishEnter(el, true);
                callHook$1(onAppearCancelled, [el]);
            },
            onLeaveCancelled(el) {
                finishLeave(el);
                callHook$1(onLeaveCancelled, [el]);
            }
        });
    }
    function normalizeDuration(duration) {
        if (duration == null) {
            return null;
        }
        else if (isObject(duration)) {
            return [NumberOf(duration.enter), NumberOf(duration.leave)];
        }
        else {
            const n = NumberOf(duration);
            return [n, n];
        }
    }
    function NumberOf(val) {
        const res = toNumber(val);
        validateDuration(res);
        return res;
    }
    function validateDuration(val) {
        if (typeof val !== 'number') {
            warn$1(`<transition> explicit duration is not a valid number - ` +
                `got ${JSON.stringify(val)}.`);
        }
        else if (isNaN(val)) {
            warn$1(`<transition> explicit duration is NaN - ` +
                'the duration expression might be incorrect.');
        }
    }
    function addTransitionClass(el, cls) {
        cls.split(/\s+/).forEach(c => c && el.classList.add(c));
        (el._vtc ||
            (el._vtc = new Set())).add(cls);
    }
    function removeTransitionClass(el, cls) {
        cls.split(/\s+/).forEach(c => c && el.classList.remove(c));
        const { _vtc } = el;
        if (_vtc) {
            _vtc.delete(cls);
            if (!_vtc.size) {
                el._vtc = undefined;
            }
        }
    }
    function nextFrame(cb) {
        requestAnimationFrame(() => {
            requestAnimationFrame(cb);
        });
    }
    let endId = 0;
    function whenTransitionEnds(el, expectedType, explicitTimeout, resolve) {
        const id = (el._endId = ++endId);
        const resolveIfNotStale = () => {
            if (id === el._endId) {
                resolve();
            }
        };
        if (explicitTimeout) {
            return setTimeout(resolveIfNotStale, explicitTimeout);
        }
        const { type, timeout, propCount } = getTransitionInfo(el, expectedType);
        if (!type) {
            return resolve();
        }
        const endEvent = type + 'end';
        let ended = 0;
        const end = () => {
            el.removeEventListener(endEvent, onEnd);
            resolveIfNotStale();
        };
        const onEnd = (e) => {
            if (e.target === el && ++ended >= propCount) {
                end();
            }
        };
        setTimeout(() => {
            if (ended < propCount) {
                end();
            }
        }, timeout + 1);
        el.addEventListener(endEvent, onEnd);
    }
    function getTransitionInfo(el, expectedType) {
        const styles = window.getComputedStyle(el);
        // JSDOM may return undefined for transition properties
        const getStyleProperties = (key) => (styles[key] || '').split(', ');
        const transitionDelays = getStyleProperties(TRANSITION + 'Delay');
        const transitionDurations = getStyleProperties(TRANSITION + 'Duration');
        const transitionTimeout = getTimeout(transitionDelays, transitionDurations);
        const animationDelays = getStyleProperties(ANIMATION + 'Delay');
        const animationDurations = getStyleProperties(ANIMATION + 'Duration');
        const animationTimeout = getTimeout(animationDelays, animationDurations);
        let type = null;
        let timeout = 0;
        let propCount = 0;
        /* istanbul ignore if */
        if (expectedType === TRANSITION) {
            if (transitionTimeout > 0) {
                type = TRANSITION;
                timeout = transitionTimeout;
                propCount = transitionDurations.length;
            }
        }
        else if (expectedType === ANIMATION) {
            if (animationTimeout > 0) {
                type = ANIMATION;
                timeout = animationTimeout;
                propCount = animationDurations.length;
            }
        }
        else {
            timeout = Math.max(transitionTimeout, animationTimeout);
            type =
                timeout > 0
                    ? transitionTimeout > animationTimeout
                        ? TRANSITION
                        : ANIMATION
                    : null;
            propCount = type
                ? type === TRANSITION
                    ? transitionDurations.length
                    : animationDurations.length
                : 0;
        }
        const hasTransform = type === TRANSITION &&
            /\b(transform|all)(,|$)/.test(styles[TRANSITION + 'Property']);
        return {
            type,
            timeout,
            propCount,
            hasTransform
        };
    }
    function getTimeout(delays, durations) {
        while (delays.length < durations.length) {
            delays = delays.concat(delays);
        }
        return Math.max(...durations.map((d, i) => toMs(d) + toMs(delays[i])));
    }
    // Old versions of Chromium (below 61.0.3163.100) formats floating pointer
    // numbers in a locale-dependent way, using a comma instead of a dot.
    // If comma is not replaced with a dot, the input will be rounded down
    // (i.e. acting as a floor function) causing unexpected behaviors
    function toMs(s) {
        return Number(s.slice(0, -1).replace(',', '.')) * 1000;
    }
    // synchronously force layout to put elements into a certain state
    function forceReflow() {
        return document.body.offsetHeight;
    }

    const positionMap = new WeakMap();
    const newPositionMap = new WeakMap();
    const TransitionGroupImpl = {
        name: 'TransitionGroup',
        props: /*#__PURE__*/ extend({}, TransitionPropsValidators, {
            tag: String,
            moveClass: String
        }),
        setup(props, { slots }) {
            const instance = getCurrentInstance();
            const state = useTransitionState();
            let prevChildren;
            let children;
            onUpdated(() => {
                // children is guaranteed to exist after initial render
                if (!prevChildren.length) {
                    return;
                }
                const moveClass = props.moveClass || `${props.name || 'v'}-move`;
                if (!hasCSSTransform(prevChildren[0].el, instance.vnode.el, moveClass)) {
                    return;
                }
                // we divide the work into three loops to avoid mixing DOM reads and writes
                // in each iteration - which helps prevent layout thrashing.
                prevChildren.forEach(callPendingCbs);
                prevChildren.forEach(recordPosition);
                const movedChildren = prevChildren.filter(applyTranslation);
                // force reflow to put everything in position
                forceReflow();
                movedChildren.forEach(c => {
                    const el = c.el;
                    const style = el.style;
                    addTransitionClass(el, moveClass);
                    style.transform = style.webkitTransform = style.transitionDuration = '';
                    const cb = (el._moveCb = (e) => {
                        if (e && e.target !== el) {
                            return;
                        }
                        if (!e || /transform$/.test(e.propertyName)) {
                            el.removeEventListener('transitionend', cb);
                            el._moveCb = null;
                            removeTransitionClass(el, moveClass);
                        }
                    });
                    el.addEventListener('transitionend', cb);
                });
            });
            return () => {
                const rawProps = toRaw(props);
                const cssTransitionProps = resolveTransitionProps(rawProps);
                let tag = rawProps.tag || Fragment;
                prevChildren = children;
                children = slots.default ? getTransitionRawChildren(slots.default()) : [];
                for (let i = 0; i < children.length; i++) {
                    const child = children[i];
                    if (child.key != null) {
                        setTransitionHooks(child, resolveTransitionHooks(child, cssTransitionProps, state, instance));
                    }
                    else {
                        warn$1(`<TransitionGroup> children must be keyed.`);
                    }
                }
                if (prevChildren) {
                    for (let i = 0; i < prevChildren.length; i++) {
                        const child = prevChildren[i];
                        setTransitionHooks(child, resolveTransitionHooks(child, cssTransitionProps, state, instance));
                        positionMap.set(child, child.el.getBoundingClientRect());
                    }
                }
                return createVNode(tag, null, children);
            };
        }
    };
    const TransitionGroup = TransitionGroupImpl;
    function callPendingCbs(c) {
        const el = c.el;
        if (el._moveCb) {
            el._moveCb();
        }
        if (el._enterCb) {
            el._enterCb();
        }
    }
    function recordPosition(c) {
        newPositionMap.set(c, c.el.getBoundingClientRect());
    }
    function applyTranslation(c) {
        const oldPos = positionMap.get(c);
        const newPos = newPositionMap.get(c);
        const dx = oldPos.left - newPos.left;
        const dy = oldPos.top - newPos.top;
        if (dx || dy) {
            const s = c.el.style;
            s.transform = s.webkitTransform = `translate(${dx}px,${dy}px)`;
            s.transitionDuration = '0s';
            return c;
        }
    }
    function hasCSSTransform(el, root, moveClass) {
        // Detect whether an element with the move class applied has
        // CSS transitions. Since the element may be inside an entering
        // transition at this very moment, we make a clone of it and remove
        // all other transition classes applied to ensure only the move class
        // is applied.
        const clone = el.cloneNode();
        if (el._vtc) {
            el._vtc.forEach(cls => {
                cls.split(/\s+/).forEach(c => c && clone.classList.remove(c));
            });
        }
        moveClass.split(/\s+/).forEach(c => c && clone.classList.add(c));
        clone.style.display = 'none';
        const container = (root.nodeType === 1 ? root : root.parentNode);
        container.appendChild(clone);
        const { hasTransform } = getTransitionInfo(clone);
        container.removeChild(clone);
        return hasTransform;
    }

    const getModelAssigner = (vnode) => {
        const fn = vnode.props['onUpdate:modelValue'] ||
            (false );
        return isArray(fn) ? value => invokeArrayFns(fn, value) : fn;
    };
    function onCompositionStart(e) {
        e.target.composing = true;
    }
    function onCompositionEnd(e) {
        const target = e.target;
        if (target.composing) {
            target.composing = false;
            target.dispatchEvent(new Event('input'));
        }
    }
    // We are exporting the v-model runtime directly as vnode hooks so that it can
    // be tree-shaken in case v-model is never used.
    const vModelText = {
        created(el, { modifiers: { lazy, trim, number } }, vnode) {
            el._assign = getModelAssigner(vnode);
            const castToNumber = number || (vnode.props && vnode.props.type === 'number');
            addEventListener(el, lazy ? 'change' : 'input', e => {
                if (e.target.composing)
                    return;
                let domValue = el.value;
                if (trim) {
                    domValue = domValue.trim();
                }
                if (castToNumber) {
                    domValue = toNumber(domValue);
                }
                el._assign(domValue);
            });
            if (trim) {
                addEventListener(el, 'change', () => {
                    el.value = el.value.trim();
                });
            }
            if (!lazy) {
                addEventListener(el, 'compositionstart', onCompositionStart);
                addEventListener(el, 'compositionend', onCompositionEnd);
                // Safari < 10.2 & UIWebView doesn't fire compositionend when
                // switching focus before confirming composition choice
                // this also fixes the issue where some browsers e.g. iOS Chrome
                // fires "change" instead of "input" on autocomplete.
                addEventListener(el, 'change', onCompositionEnd);
            }
        },
        // set value on mounted so it's after min/max for type="range"
        mounted(el, { value }) {
            el.value = value == null ? '' : value;
        },
        beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) {
            el._assign = getModelAssigner(vnode);
            // avoid clearing unresolved text. #2302
            if (el.composing)
                return;
            if (document.activeElement === el && el.type !== 'range') {
                if (lazy) {
                    return;
                }
                if (trim && el.value.trim() === value) {
                    return;
                }
                if ((number || el.type === 'number') && toNumber(el.value) === value) {
                    return;
                }
            }
            const newValue = value == null ? '' : value;
            if (el.value !== newValue) {
                el.value = newValue;
            }
        }
    };
    const vModelCheckbox = {
        // #4096 array checkboxes need to be deep traversed
        deep: true,
        created(el, _, vnode) {
            el._assign = getModelAssigner(vnode);
            addEventListener(el, 'change', () => {
                const modelValue = el._modelValue;
                const elementValue = getValue(el);
                const checked = el.checked;
                const assign = el._assign;
                if (isArray(modelValue)) {
                    const index = looseIndexOf(modelValue, elementValue);
                    const found = index !== -1;
                    if (checked && !found) {
                        assign(modelValue.concat(elementValue));
                    }
                    else if (!checked && found) {
                        const filtered = [...modelValue];
                        filtered.splice(index, 1);
                        assign(filtered);
                    }
                }
                else if (isSet(modelValue)) {
                    const cloned = new Set(modelValue);
                    if (checked) {
                        cloned.add(elementValue);
                    }
                    else {
                        cloned.delete(elementValue);
                    }
                    assign(cloned);
                }
                else {
                    assign(getCheckboxValue(el, checked));
                }
            });
        },
        // set initial checked on mount to wait for true-value/false-value
        mounted: setChecked,
        beforeUpdate(el, binding, vnode) {
            el._assign = getModelAssigner(vnode);
            setChecked(el, binding, vnode);
        }
    };
    function setChecked(el, { value, oldValue }, vnode) {
        el._modelValue = value;
        if (isArray(value)) {
            el.checked = looseIndexOf(value, vnode.props.value) > -1;
        }
        else if (isSet(value)) {
            el.checked = value.has(vnode.props.value);
        }
        else if (value !== oldValue) {
            el.checked = looseEqual(value, getCheckboxValue(el, true));
        }
    }
    const vModelRadio = {
        created(el, { value }, vnode) {
            el.checked = looseEqual(value, vnode.props.value);
            el._assign = getModelAssigner(vnode);
            addEventListener(el, 'change', () => {
                el._assign(getValue(el));
            });
        },
        beforeUpdate(el, { value, oldValue }, vnode) {
            el._assign = getModelAssigner(vnode);
            if (value !== oldValue) {
                el.checked = looseEqual(value, vnode.props.value);
            }
        }
    };
    const vModelSelect = {
        // <select multiple> value need to be deep traversed
        deep: true,
        created(el, { value, modifiers: { number } }, vnode) {
            const isSetModel = isSet(value);
            addEventListener(el, 'change', () => {
                const selectedVal = Array.prototype.filter
                    .call(el.options, (o) => o.selected)
                    .map((o) => number ? toNumber(getValue(o)) : getValue(o));
                el._assign(el.multiple
                    ? isSetModel
                        ? new Set(selectedVal)
                        : selectedVal
                    : selectedVal[0]);
            });
            el._assign = getModelAssigner(vnode);
        },
        // set value in mounted & updated because <select> relies on its children
        // <option>s.
        mounted(el, { value }) {
            setSelected(el, value);
        },
        beforeUpdate(el, _binding, vnode) {
            el._assign = getModelAssigner(vnode);
        },
        updated(el, { value }) {
            setSelected(el, value);
        }
    };
    function setSelected(el, value) {
        const isMultiple = el.multiple;
        if (isMultiple && !isArray(value) && !isSet(value)) {
            warn$1(`<select multiple v-model> expects an Array or Set value for its binding, ` +
                    `but got ${Object.prototype.toString.call(value).slice(8, -1)}.`);
            return;
        }
        for (let i = 0, l = el.options.length; i < l; i++) {
            const option = el.options[i];
            const optionValue = getValue(option);
            if (isMultiple) {
                if (isArray(value)) {
                    option.selected = looseIndexOf(value, optionValue) > -1;
                }
                else {
                    option.selected = value.has(optionValue);
                }
            }
            else {
                if (looseEqual(getValue(option), value)) {
                    if (el.selectedIndex !== i)
                        el.selectedIndex = i;
                    return;
                }
            }
        }
        if (!isMultiple && el.selectedIndex !== -1) {
            el.selectedIndex = -1;
        }
    }
    // retrieve raw value set via :value bindings
    function getValue(el) {
        return '_value' in el ? el._value : el.value;
    }
    // retrieve raw value for true-value and false-value set via :true-value or :false-value bindings
    function getCheckboxValue(el, checked) {
        const key = checked ? '_trueValue' : '_falseValue';
        return key in el ? el[key] : checked;
    }
    const vModelDynamic = {
        created(el, binding, vnode) {
            callModelHook(el, binding, vnode, null, 'created');
        },
        mounted(el, binding, vnode) {
            callModelHook(el, binding, vnode, null, 'mounted');
        },
        beforeUpdate(el, binding, vnode, prevVNode) {
            callModelHook(el, binding, vnode, prevVNode, 'beforeUpdate');
        },
        updated(el, binding, vnode, prevVNode) {
            callModelHook(el, binding, vnode, prevVNode, 'updated');
        }
    };
    function resolveDynamicModel(tagName, type) {
        switch (tagName) {
            case 'SELECT':
                return vModelSelect;
            case 'TEXTAREA':
                return vModelText;
            default:
                switch (type) {
                    case 'checkbox':
                        return vModelCheckbox;
                    case 'radio':
                        return vModelRadio;
                    default:
                        return vModelText;
                }
        }
    }
    function callModelHook(el, binding, vnode, prevVNode, hook) {
        const modelToUse = resolveDynamicModel(el.tagName, vnode.props && vnode.props.type);
        const fn = modelToUse[hook];
        fn && fn(el, binding, vnode, prevVNode);
    }

    const systemModifiers = ['ctrl', 'shift', 'alt', 'meta'];
    const modifierGuards = {
        stop: e => e.stopPropagation(),
        prevent: e => e.preventDefault(),
        self: e => e.target !== e.currentTarget,
        ctrl: e => !e.ctrlKey,
        shift: e => !e.shiftKey,
        alt: e => !e.altKey,
        meta: e => !e.metaKey,
        left: e => 'button' in e && e.button !== 0,
        middle: e => 'button' in e && e.button !== 1,
        right: e => 'button' in e && e.button !== 2,
        exact: (e, modifiers) => systemModifiers.some(m => e[`${m}Key`] && !modifiers.includes(m))
    };
    /**
     * @private
     */
    const withModifiers = (fn, modifiers) => {
        return (event, ...args) => {
            for (let i = 0; i < modifiers.length; i++) {
                const guard = modifierGuards[modifiers[i]];
                if (guard && guard(event, modifiers))
                    return;
            }
            return fn(event, ...args);
        };
    };
    // Kept for 2.x compat.
    // Note: IE11 compat for `spacebar` and `del` is removed for now.
    const keyNames = {
        esc: 'escape',
        space: ' ',
        up: 'arrow-up',
        left: 'arrow-left',
        right: 'arrow-right',
        down: 'arrow-down',
        delete: 'backspace'
    };
    /**
     * @private
     */
    const withKeys = (fn, modifiers) => {
        return (event) => {
            if (!('key' in event)) {
                return;
            }
            const eventKey = hyphenate(event.key);
            if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) {
                return fn(event);
            }
        };
    };

    const vShow = {
        beforeMount(el, { value }, { transition }) {
            el._vod = el.style.display === 'none' ? '' : el.style.display;
            if (transition && value) {
                transition.beforeEnter(el);
            }
            else {
                setDisplay(el, value);
            }
        },
        mounted(el, { value }, { transition }) {
            if (transition && value) {
                transition.enter(el);
            }
        },
        updated(el, { value, oldValue }, { transition }) {
            if (!value === !oldValue)
                return;
            if (transition) {
                if (value) {
                    transition.beforeEnter(el);
                    setDisplay(el, true);
                    transition.enter(el);
                }
                else {
                    transition.leave(el, () => {
                        setDisplay(el, false);
                    });
                }
            }
            else {
                setDisplay(el, value);
            }
        },
        beforeUnmount(el, { value }) {
            setDisplay(el, value);
        }
    };
    function setDisplay(el, value) {
        el.style.display = value ? el._vod : 'none';
    }

    const rendererOptions = /*#__PURE__*/ extend({ patchProp }, nodeOps);
    // lazy create the renderer - this makes core renderer logic tree-shakable
    // in case the user only imports reactivity utilities from Vue.
    let renderer;
    let enabledHydration = false;
    function ensureRenderer() {
        return (renderer ||
            (renderer = createRenderer(rendererOptions)));
    }
    function ensureHydrationRenderer() {
        renderer = enabledHydration
            ? renderer
            : createHydrationRenderer(rendererOptions);
        enabledHydration = true;
        return renderer;
    }
    // use explicit type casts here to avoid import() calls in rolled-up d.ts
    const render$l = ((...args) => {
        ensureRenderer().render(...args);
    });
    const hydrate = ((...args) => {
        ensureHydrationRenderer().hydrate(...args);
    });
    const createApp = ((...args) => {
        const app = ensureRenderer().createApp(...args);
        {
            injectNativeTagCheck(app);
            injectCompilerOptionsCheck(app);
        }
        const { mount } = app;
        app.mount = (containerOrSelector) => {
            const container = normalizeContainer(containerOrSelector);
            if (!container)
                return;
            const component = app._component;
            if (!isFunction(component) && !component.render && !component.template) {
                // __UNSAFE__
                // Reason: potential execution of JS expressions in in-DOM template.
                // The user must make sure the in-DOM template is trusted. If it's
                // rendered by the server, the template should not contain any user data.
                component.template = container.innerHTML;
            }
            // clear content before mounting
            container.innerHTML = '';
            const proxy = mount(container, false, container instanceof SVGElement);
            if (container instanceof Element) {
                container.removeAttribute('v-cloak');
                container.setAttribute('data-v-app', '');
            }
            return proxy;
        };
        return app;
    });
    const createSSRApp = ((...args) => {
        const app = ensureHydrationRenderer().createApp(...args);
        {
            injectNativeTagCheck(app);
            injectCompilerOptionsCheck(app);
        }
        const { mount } = app;
        app.mount = (containerOrSelector) => {
            const container = normalizeContainer(containerOrSelector);
            if (container) {
                return mount(container, true, container instanceof SVGElement);
            }
        };
        return app;
    });
    function injectNativeTagCheck(app) {
        // Inject `isNativeTag`
        // this is used for component name validation (dev only)
        Object.defineProperty(app.config, 'isNativeTag', {
            value: (tag) => isHTMLTag(tag) || isSVGTag(tag),
            writable: false
        });
    }
    // dev only
    function injectCompilerOptionsCheck(app) {
        if (isRuntimeOnly()) {
            const isCustomElement = app.config.isCustomElement;
            Object.defineProperty(app.config, 'isCustomElement', {
                get() {
                    return isCustomElement;
                },
                set() {
                    warn$1(`The \`isCustomElement\` config option is deprecated. Use ` +
                        `\`compilerOptions.isCustomElement\` instead.`);
                }
            });
            const compilerOptions = app.config.compilerOptions;
            const msg = `The \`compilerOptions\` config option is only respected when using ` +
                `a build of Vue.js that includes the runtime compiler (aka "full build"). ` +
                `Since you are using the runtime-only build, \`compilerOptions\` ` +
                `must be passed to \`@vue/compiler-dom\` in the build setup instead.\n` +
                `- For vue-loader: pass it via vue-loader's \`compilerOptions\` loader option.\n` +
                `- For vue-cli: see https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-loader\n` +
                `- For vite: pass it via @vitejs/plugin-vue options. See https://github.com/vitejs/vite/tree/main/packages/plugin-vue#example-for-passing-options-to-vuecompiler-dom`;
            Object.defineProperty(app.config, 'compilerOptions', {
                get() {
                    warn$1(msg);
                    return compilerOptions;
                },
                set() {
                    warn$1(msg);
                }
            });
        }
    }
    function normalizeContainer(container) {
        if (isString(container)) {
            const res = document.querySelector(container);
            if (!res) {
                warn$1(`Failed to mount app: mount target selector "${container}" returned null.`);
            }
            return res;
        }
        if (window.ShadowRoot &&
            container instanceof window.ShadowRoot &&
            container.mode === 'closed') {
            warn$1(`mounting on a ShadowRoot with \`{mode: "closed"}\` may lead to unpredictable bugs`);
        }
        return container;
    }
    /**
     * @internal
     */
    const initDirectivesForSSR = NOOP;

    var runtimeDom = /*#__PURE__*/Object.freeze({
      __proto__: null,
      render: render$l,
      hydrate: hydrate,
      createApp: createApp,
      createSSRApp: createSSRApp,
      initDirectivesForSSR: initDirectivesForSSR,
      defineCustomElement: defineCustomElement,
      defineSSRCustomElement: defineSSRCustomElement,
      VueElement: VueElement,
      useCssModule: useCssModule,
      useCssVars: useCssVars,
      Transition: Transition,
      TransitionGroup: TransitionGroup,
      vModelText: vModelText,
      vModelCheckbox: vModelCheckbox,
      vModelRadio: vModelRadio,
      vModelSelect: vModelSelect,
      vModelDynamic: vModelDynamic,
      withModifiers: withModifiers,
      withKeys: withKeys,
      vShow: vShow,
      reactive: reactive,
      ref: ref,
      readonly: readonly,
      unref: unref,
      proxyRefs: proxyRefs,
      isRef: isRef,
      toRef: toRef,
      toRefs: toRefs,
      isProxy: isProxy,
      isReactive: isReactive,
      isReadonly: isReadonly,
      isShallow: isShallow,
      customRef: customRef,
      triggerRef: triggerRef,
      shallowRef: shallowRef,
      shallowReactive: shallowReactive,
      shallowReadonly: shallowReadonly,
      markRaw: markRaw,
      toRaw: toRaw,
      effect: effect,
      stop: stop,
      ReactiveEffect: ReactiveEffect,
      effectScope: effectScope,
      EffectScope: EffectScope,
      getCurrentScope: getCurrentScope,
      onScopeDispose: onScopeDispose,
      computed: computed$1,
      watch: watch,
      watchEffect: watchEffect,
      watchPostEffect: watchPostEffect,
      watchSyncEffect: watchSyncEffect,
      onBeforeMount: onBeforeMount,
      onMounted: onMounted,
      onBeforeUpdate: onBeforeUpdate,
      onUpdated: onUpdated,
      onBeforeUnmount: onBeforeUnmount,
      onUnmounted: onUnmounted,
      onActivated: onActivated,
      onDeactivated: onDeactivated,
      onRenderTracked: onRenderTracked,
      onRenderTriggered: onRenderTriggered,
      onErrorCaptured: onErrorCaptured,
      onServerPrefetch: onServerPrefetch,
      provide: provide,
      inject: inject,
      nextTick: nextTick,
      defineComponent: defineComponent,
      defineAsyncComponent: defineAsyncComponent,
      useAttrs: useAttrs,
      useSlots: useSlots,
      defineProps: defineProps,
      defineEmits: defineEmits,
      defineExpose: defineExpose,
      withDefaults: withDefaults,
      mergeDefaults: mergeDefaults,
      createPropsRestProxy: createPropsRestProxy,
      withAsyncContext: withAsyncContext,
      getCurrentInstance: getCurrentInstance,
      h: h$2,
      createVNode: createVNode,
      cloneVNode: cloneVNode,
      mergeProps: mergeProps,
      isVNode: isVNode,
      Fragment: Fragment,
      Text: Text,
      Comment: Comment,
      Static: Static,
      Teleport: Teleport,
      Suspense: Suspense,
      KeepAlive: KeepAlive,
      BaseTransition: BaseTransition,
      withDirectives: withDirectives,
      useSSRContext: useSSRContext,
      ssrContextKey: ssrContextKey,
      createRenderer: createRenderer,
      createHydrationRenderer: createHydrationRenderer,
      queuePostFlushCb: queuePostFlushCb,
      warn: warn$1,
      handleError: handleError,
      callWithErrorHandling: callWithErrorHandling,
      callWithAsyncErrorHandling: callWithAsyncErrorHandling,
      resolveComponent: resolveComponent,
      resolveDirective: resolveDirective,
      resolveDynamicComponent: resolveDynamicComponent,
      registerRuntimeCompiler: registerRuntimeCompiler,
      isRuntimeOnly: isRuntimeOnly,
      useTransitionState: useTransitionState,
      resolveTransitionHooks: resolveTransitionHooks,
      setTransitionHooks: setTransitionHooks,
      getTransitionRawChildren: getTransitionRawChildren,
      initCustomFormatter: initCustomFormatter,
      get devtools () { return devtools; },
      setDevtoolsHook: setDevtoolsHook,
      withCtx: withCtx,
      pushScopeId: pushScopeId,
      popScopeId: popScopeId,
      withScopeId: withScopeId,
      renderList: renderList,
      toHandlers: toHandlers,
      renderSlot: renderSlot,
      createSlots: createSlots,
      withMemo: withMemo,
      isMemoSame: isMemoSame,
      openBlock: openBlock,
      createBlock: createBlock,
      setBlockTracking: setBlockTracking,
      createTextVNode: createTextVNode,
      createCommentVNode: createCommentVNode,
      createStaticVNode: createStaticVNode,
      createElementVNode: createBaseVNode,
      createElementBlock: createElementBlock,
      guardReactiveProps: guardReactiveProps,
      toDisplayString: toDisplayString,
      camelize: camelize,
      capitalize: capitalize,
      toHandlerKey: toHandlerKey,
      normalizeProps: normalizeProps,
      normalizeClass: normalizeClass,
      normalizeStyle: normalizeStyle,
      transformVNodeArgs: transformVNodeArgs,
      version: version,
      ssrUtils: ssrUtils,
      resolveFilter: resolveFilter,
      compatUtils: compatUtils
    });

    function initDev() {
        {
            {
                console.info(`You are running a development build of Vue.\n` +
                    `Make sure to use the production build (*.prod.js) when deploying for production.`);
            }
            initCustomFormatter();
        }
    }

    function defaultOnError(error) {
        throw error;
    }
    function defaultOnWarn(msg) {
        console.warn(`[Vue warn] ${msg.message}`);
    }
    function createCompilerError(code, loc, messages, additionalMessage) {
        const msg = (messages || errorMessages)[code] + (additionalMessage || ``)
            ;
        const error = new SyntaxError(String(msg));
        error.code = code;
        error.loc = loc;
        return error;
    }
    const errorMessages = {
        // parse errors
        [0 /* ABRUPT_CLOSING_OF_EMPTY_COMMENT */]: 'Illegal comment.',
        [1 /* CDATA_IN_HTML_CONTENT */]: 'CDATA section is allowed only in XML context.',
        [2 /* DUPLICATE_ATTRIBUTE */]: 'Duplicate attribute.',
        [3 /* END_TAG_WITH_ATTRIBUTES */]: 'End tag cannot have attributes.',
        [4 /* END_TAG_WITH_TRAILING_SOLIDUS */]: "Illegal '/' in tags.",
        [5 /* EOF_BEFORE_TAG_NAME */]: 'Unexpected EOF in tag.',
        [6 /* EOF_IN_CDATA */]: 'Unexpected EOF in CDATA section.',
        [7 /* EOF_IN_COMMENT */]: 'Unexpected EOF in comment.',
        [8 /* EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT */]: 'Unexpected EOF in script.',
        [9 /* EOF_IN_TAG */]: 'Unexpected EOF in tag.',
        [10 /* INCORRECTLY_CLOSED_COMMENT */]: 'Incorrectly closed comment.',
        [11 /* INCORRECTLY_OPENED_COMMENT */]: 'Incorrectly opened comment.',
        [12 /* INVALID_FIRST_CHARACTER_OF_TAG_NAME */]: "Illegal tag name. Use '&lt;' to print '<'.",
        [13 /* MISSING_ATTRIBUTE_VALUE */]: 'Attribute value was expected.',
        [14 /* MISSING_END_TAG_NAME */]: 'End tag name was expected.',
        [15 /* MISSING_WHITESPACE_BETWEEN_ATTRIBUTES */]: 'Whitespace was expected.',
        [16 /* NESTED_COMMENT */]: "Unexpected '<!--' in comment.",
        [17 /* UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME */]: 'Attribute name cannot contain U+0022 ("), U+0027 (\'), and U+003C (<).',
        [18 /* UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE */]: 'Unquoted attribute value cannot contain U+0022 ("), U+0027 (\'), U+003C (<), U+003D (=), and U+0060 (`).',
        [19 /* UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME */]: "Attribute name cannot start with '='.",
        [21 /* UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME */]: "'<?' is allowed only in XML context.",
        [20 /* UNEXPECTED_NULL_CHARACTER */]: `Unexpected null character.`,
        [22 /* UNEXPECTED_SOLIDUS_IN_TAG */]: "Illegal '/' in tags.",
        // Vue-specific parse errors
        [23 /* X_INVALID_END_TAG */]: 'Invalid end tag.',
        [24 /* X_MISSING_END_TAG */]: 'Element is missing end tag.',
        [25 /* X_MISSING_INTERPOLATION_END */]: 'Interpolation end sign was not found.',
        [27 /* X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END */]: 'End bracket for dynamic directive argument was not found. ' +
            'Note that dynamic directive argument cannot contain spaces.',
        [26 /* X_MISSING_DIRECTIVE_NAME */]: 'Legal directive name was expected.',
        // transform errors
        [28 /* X_V_IF_NO_EXPRESSION */]: `v-if/v-else-if is missing expression.`,
        [29 /* X_V_IF_SAME_KEY */]: `v-if/else branches must use unique keys.`,
        [30 /* X_V_ELSE_NO_ADJACENT_IF */]: `v-else/v-else-if has no adjacent v-if or v-else-if.`,
        [31 /* X_V_FOR_NO_EXPRESSION */]: `v-for is missing expression.`,
        [32 /* X_V_FOR_MALFORMED_EXPRESSION */]: `v-for has invalid expression.`,
        [33 /* X_V_FOR_TEMPLATE_KEY_PLACEMENT */]: `<template v-for> key should be placed on the <template> tag.`,
        [34 /* X_V_BIND_NO_EXPRESSION */]: `v-bind is missing expression.`,
        [35 /* X_V_ON_NO_EXPRESSION */]: `v-on is missing expression.`,
        [36 /* X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET */]: `Unexpected custom directive on <slot> outlet.`,
        [37 /* X_V_SLOT_MIXED_SLOT_USAGE */]: `Mixed v-slot usage on both the component and nested <template>.` +
            `When there are multiple named slots, all slots should use <template> ` +
            `syntax to avoid scope ambiguity.`,
        [38 /* X_V_SLOT_DUPLICATE_SLOT_NAMES */]: `Duplicate slot names found. `,
        [39 /* X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN */]: `Extraneous children found when component already has explicitly named ` +
            `default slot. These children will be ignored.`,
        [40 /* X_V_SLOT_MISPLACED */]: `v-slot can only be used on components or <template> tags.`,
        [41 /* X_V_MODEL_NO_EXPRESSION */]: `v-model is missing expression.`,
        [42 /* X_V_MODEL_MALFORMED_EXPRESSION */]: `v-model value must be a valid JavaScript member expression.`,
        [43 /* X_V_MODEL_ON_SCOPE_VARIABLE */]: `v-model cannot be used on v-for or v-slot scope variables because they are not writable.`,
        [44 /* X_INVALID_EXPRESSION */]: `Error parsing JavaScript expression: `,
        [45 /* X_KEEP_ALIVE_INVALID_CHILDREN */]: `<KeepAlive> expects exactly one child component.`,
        // generic errors
        [46 /* X_PREFIX_ID_NOT_SUPPORTED */]: `"prefixIdentifiers" option is not supported in this build of compiler.`,
        [47 /* X_MODULE_MODE_NOT_SUPPORTED */]: `ES module mode is not supported in this build of compiler.`,
        [48 /* X_CACHE_HANDLER_NOT_SUPPORTED */]: `"cacheHandlers" option is only supported when the "prefixIdentifiers" option is enabled.`,
        [49 /* X_SCOPE_ID_NOT_SUPPORTED */]: `"scopeId" option is only supported in module mode.`,
        // just to fulfill types
        [50 /* __EXTEND_POINT__ */]: ``
    };

    const FRAGMENT = Symbol(`Fragment` );
    const TELEPORT = Symbol(`Teleport` );
    const SUSPENSE = Symbol(`Suspense` );
    const KEEP_ALIVE = Symbol(`KeepAlive` );
    const BASE_TRANSITION = Symbol(`BaseTransition` );
    const OPEN_BLOCK = Symbol(`openBlock` );
    const CREATE_BLOCK = Symbol(`createBlock` );
    const CREATE_ELEMENT_BLOCK = Symbol(`createElementBlock` );
    const CREATE_VNODE = Symbol(`createVNode` );
    const CREATE_ELEMENT_VNODE = Symbol(`createElementVNode` );
    const CREATE_COMMENT = Symbol(`createCommentVNode` );
    const CREATE_TEXT = Symbol(`createTextVNode` );
    const CREATE_STATIC = Symbol(`createStaticVNode` );
    const RESOLVE_COMPONENT = Symbol(`resolveComponent` );
    const RESOLVE_DYNAMIC_COMPONENT = Symbol(`resolveDynamicComponent` );
    const RESOLVE_DIRECTIVE = Symbol(`resolveDirective` );
    const RESOLVE_FILTER = Symbol(`resolveFilter` );
    const WITH_DIRECTIVES = Symbol(`withDirectives` );
    const RENDER_LIST = Symbol(`renderList` );
    const RENDER_SLOT = Symbol(`renderSlot` );
    const CREATE_SLOTS = Symbol(`createSlots` );
    const TO_DISPLAY_STRING = Symbol(`toDisplayString` );
    const MERGE_PROPS = Symbol(`mergeProps` );
    const NORMALIZE_CLASS = Symbol(`normalizeClass` );
    const NORMALIZE_STYLE = Symbol(`normalizeStyle` );
    const NORMALIZE_PROPS = Symbol(`normalizeProps` );
    const GUARD_REACTIVE_PROPS = Symbol(`guardReactiveProps` );
    const TO_HANDLERS = Symbol(`toHandlers` );
    const CAMELIZE = Symbol(`camelize` );
    const CAPITALIZE = Symbol(`capitalize` );
    const TO_HANDLER_KEY = Symbol(`toHandlerKey` );
    const SET_BLOCK_TRACKING = Symbol(`setBlockTracking` );
    const PUSH_SCOPE_ID = Symbol(`pushScopeId` );
    const POP_SCOPE_ID = Symbol(`popScopeId` );
    const WITH_CTX = Symbol(`withCtx` );
    const UNREF = Symbol(`unref` );
    const IS_REF = Symbol(`isRef` );
    const WITH_MEMO = Symbol(`withMemo` );
    const IS_MEMO_SAME = Symbol(`isMemoSame` );
    // Name mapping for runtime helpers that need to be imported from 'vue' in
    // generated code. Make sure these are correctly exported in the runtime!
    // Using `any` here because TS doesn't allow symbols as index type.
    const helperNameMap = {
        [FRAGMENT]: `Fragment`,
        [TELEPORT]: `Teleport`,
        [SUSPENSE]: `Suspense`,
        [KEEP_ALIVE]: `KeepAlive`,
        [BASE_TRANSITION]: `BaseTransition`,
        [OPEN_BLOCK]: `openBlock`,
        [CREATE_BLOCK]: `createBlock`,
        [CREATE_ELEMENT_BLOCK]: `createElementBlock`,
        [CREATE_VNODE]: `createVNode`,
        [CREATE_ELEMENT_VNODE]: `createElementVNode`,
        [CREATE_COMMENT]: `createCommentVNode`,
        [CREATE_TEXT]: `createTextVNode`,
        [CREATE_STATIC]: `createStaticVNode`,
        [RESOLVE_COMPONENT]: `resolveComponent`,
        [RESOLVE_DYNAMIC_COMPONENT]: `resolveDynamicComponent`,
        [RESOLVE_DIRECTIVE]: `resolveDirective`,
        [RESOLVE_FILTER]: `resolveFilter`,
        [WITH_DIRECTIVES]: `withDirectives`,
        [RENDER_LIST]: `renderList`,
        [RENDER_SLOT]: `renderSlot`,
        [CREATE_SLOTS]: `createSlots`,
        [TO_DISPLAY_STRING]: `toDisplayString`,
        [MERGE_PROPS]: `mergeProps`,
        [NORMALIZE_CLASS]: `normalizeClass`,
        [NORMALIZE_STYLE]: `normalizeStyle`,
        [NORMALIZE_PROPS]: `normalizeProps`,
        [GUARD_REACTIVE_PROPS]: `guardReactiveProps`,
        [TO_HANDLERS]: `toHandlers`,
        [CAMELIZE]: `camelize`,
        [CAPITALIZE]: `capitalize`,
        [TO_HANDLER_KEY]: `toHandlerKey`,
        [SET_BLOCK_TRACKING]: `setBlockTracking`,
        [PUSH_SCOPE_ID]: `pushScopeId`,
        [POP_SCOPE_ID]: `popScopeId`,
        [WITH_CTX]: `withCtx`,
        [UNREF]: `unref`,
        [IS_REF]: `isRef`,
        [WITH_MEMO]: `withMemo`,
        [IS_MEMO_SAME]: `isMemoSame`
    };
    function registerRuntimeHelpers(helpers) {
        Object.getOwnPropertySymbols(helpers).forEach(s => {
            helperNameMap[s] = helpers[s];
        });
    }

    // AST Utilities ---------------------------------------------------------------
    // Some expressions, e.g. sequence and conditional expressions, are never
    // associated with template nodes, so their source locations are just a stub.
    // Container types like CompoundExpression also don't need a real location.
    const locStub = {
        source: '',
        start: { line: 1, column: 1, offset: 0 },
        end: { line: 1, column: 1, offset: 0 }
    };
    function createRoot(children, loc = locStub) {
        return {
            type: 0 /* ROOT */,
            children,
            helpers: [],
            components: [],
            directives: [],
            hoists: [],
            imports: [],
            cached: 0,
            temps: 0,
            codegenNode: undefined,
            loc
        };
    }
    function createVNodeCall(context, tag, props, children, patchFlag, dynamicProps, directives, isBlock = false, disableTracking = false, isComponent = false, loc = locStub) {
        if (context) {
            if (isBlock) {
                context.helper(OPEN_BLOCK);
                context.helper(getVNodeBlockHelper(context.inSSR, isComponent));
            }
            else {
                context.helper(getVNodeHelper(context.inSSR, isComponent));
            }
            if (directives) {
                context.helper(WITH_DIRECTIVES);
            }
        }
        return {
            type: 13 /* VNODE_CALL */,
            tag,
            props,
            children,
            patchFlag,
            dynamicProps,
            directives,
            isBlock,
            disableTracking,
            isComponent,
            loc
        };
    }
    function createArrayExpression(elements, loc = locStub) {
        return {
            type: 17 /* JS_ARRAY_EXPRESSION */,
            loc,
            elements
        };
    }
    function createObjectExpression(properties, loc = locStub) {
        return {
            type: 15 /* JS_OBJECT_EXPRESSION */,
            loc,
            properties
        };
    }
    function createObjectProperty(key, value) {
        return {
            type: 16 /* JS_PROPERTY */,
            loc: locStub,
            key: isString(key) ? createSimpleExpression(key, true) : key,
            value
        };
    }
    function createSimpleExpression(content, isStatic = false, loc = locStub, constType = 0 /* NOT_CONSTANT */) {
        return {
            type: 4 /* SIMPLE_EXPRESSION */,
            loc,
            content,
            isStatic,
            constType: isStatic ? 3 /* CAN_STRINGIFY */ : constType
        };
    }
    function createCompoundExpression(children, loc = locStub) {
        return {
            type: 8 /* COMPOUND_EXPRESSION */,
            loc,
            children
        };
    }
    function createCallExpression(callee, args = [], loc = locStub) {
        return {
            type: 14 /* JS_CALL_EXPRESSION */,
            loc,
            callee,
            arguments: args
        };
    }
    function createFunctionExpression(params, returns = undefined, newline = false, isSlot = false, loc = locStub) {
        return {
            type: 18 /* JS_FUNCTION_EXPRESSION */,
            params,
            returns,
            newline,
            isSlot,
            loc
        };
    }
    function createConditionalExpression(test, consequent, alternate, newline = true) {
        return {
            type: 19 /* JS_CONDITIONAL_EXPRESSION */,
            test,
            consequent,
            alternate,
            newline,
            loc: locStub
        };
    }
    function createCacheExpression(index, value, isVNode = false) {
        return {
            type: 20 /* JS_CACHE_EXPRESSION */,
            index,
            value,
            isVNode,
            loc: locStub
        };
    }
    function createBlockStatement(body) {
        return {
            type: 21 /* JS_BLOCK_STATEMENT */,
            body,
            loc: locStub
        };
    }

    const isStaticExp = (p) => p.type === 4 /* SIMPLE_EXPRESSION */ && p.isStatic;
    const isBuiltInType = (tag, expected) => tag === expected || tag === hyphenate(expected);
    function isCoreComponent(tag) {
        if (isBuiltInType(tag, 'Teleport')) {
            return TELEPORT;
        }
        else if (isBuiltInType(tag, 'Suspense')) {
            return SUSPENSE;
        }
        else if (isBuiltInType(tag, 'KeepAlive')) {
            return KEEP_ALIVE;
        }
        else if (isBuiltInType(tag, 'BaseTransition')) {
            return BASE_TRANSITION;
        }
    }
    const nonIdentifierRE = /^\d|[^\$\w]/;
    const isSimpleIdentifier = (name) => !nonIdentifierRE.test(name);
    const validFirstIdentCharRE = /[A-Za-z_$\xA0-\uFFFF]/;
    const validIdentCharRE = /[\.\?\w$\xA0-\uFFFF]/;
    const whitespaceRE = /\s+[.[]\s*|\s*[.[]\s+/g;
    /**
     * Simple lexer to check if an expression is a member expression. This is
     * lax and only checks validity at the root level (i.e. does not validate exps
     * inside square brackets), but it's ok since these are only used on template
     * expressions and false positives are invalid expressions in the first place.
     */
    const isMemberExpressionBrowser = (path) => {
        // remove whitespaces around . or [ first
        path = path.trim().replace(whitespaceRE, s => s.trim());
        let state = 0 /* inMemberExp */;
        let stateStack = [];
        let currentOpenBracketCount = 0;
        let currentOpenParensCount = 0;
        let currentStringType = null;
        for (let i = 0; i < path.length; i++) {
            const char = path.charAt(i);
            switch (state) {
                case 0 /* inMemberExp */:
                    if (char === '[') {
                        stateStack.push(state);
                        state = 1 /* inBrackets */;
                        currentOpenBracketCount++;
                    }
                    else if (char === '(') {
                        stateStack.push(state);
                        state = 2 /* inParens */;
                        currentOpenParensCount++;
                    }
                    else if (!(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char)) {
                        return false;
                    }
                    break;
                case 1 /* inBrackets */:
                    if (char === `'` || char === `"` || char === '`') {
                        stateStack.push(state);
                        state = 3 /* inString */;
                        currentStringType = char;
                    }
                    else if (char === `[`) {
                        currentOpenBracketCount++;
                    }
                    else if (char === `]`) {
                        if (!--currentOpenBracketCount) {
                            state = stateStack.pop();
                        }
                    }
                    break;
                case 2 /* inParens */:
                    if (char === `'` || char === `"` || char === '`') {
                        stateStack.push(state);
                        state = 3 /* inString */;
                        currentStringType = char;
                    }
                    else if (char === `(`) {
                        currentOpenParensCount++;
                    }
                    else if (char === `)`) {
                        // if the exp ends as a call then it should not be considered valid
                        if (i === path.length - 1) {
                            return false;
                        }
                        if (!--currentOpenParensCount) {
                            state = stateStack.pop();
                        }
                    }
                    break;
                case 3 /* inString */:
                    if (char === currentStringType) {
                        state = stateStack.pop();
                        currentStringType = null;
                    }
                    break;
            }
        }
        return !currentOpenBracketCount && !currentOpenParensCount;
    };
    const isMemberExpression = isMemberExpressionBrowser
        ;
    function getInnerRange(loc, offset, length) {
        const source = loc.source.slice(offset, offset + length);
        const newLoc = {
            source,
            start: advancePositionWithClone(loc.start, loc.source, offset),
            end: loc.end
        };
        if (length != null) {
            newLoc.end = advancePositionWithClone(loc.start, loc.source, offset + length);
        }
        return newLoc;
    }
    function advancePositionWithClone(pos, source, numberOfCharacters = source.length) {
        return advancePositionWithMutation(extend({}, pos), source, numberOfCharacters);
    }
    // advance by mutation without cloning (for performance reasons), since this
    // gets called a lot in the parser
    function advancePositionWithMutation(pos, source, numberOfCharacters = source.length) {
        let linesCount = 0;
        let lastNewLinePos = -1;
        for (let i = 0; i < numberOfCharacters; i++) {
            if (source.charCodeAt(i) === 10 /* newline char code */) {
                linesCount++;
                lastNewLinePos = i;
            }
        }
        pos.offset += numberOfCharacters;
        pos.line += linesCount;
        pos.column =
            lastNewLinePos === -1
                ? pos.column + numberOfCharacters
                : numberOfCharacters - lastNewLinePos;
        return pos;
    }
    function assert(condition, msg) {
        /* istanbul ignore if */
        if (!condition) {
            throw new Error(msg || `unexpected compiler condition`);
        }
    }
    function findDir(node, name, allowEmpty = false) {
        for (let i = 0; i < node.props.length; i++) {
            const p = node.props[i];
            if (p.type === 7 /* DIRECTIVE */ &&
                (allowEmpty || p.exp) &&
                (isString(name) ? p.name === name : name.test(p.name))) {
                return p;
            }
        }
    }
    function findProp(node, name, dynamicOnly = false, allowEmpty = false) {
        for (let i = 0; i < node.props.length; i++) {
            const p = node.props[i];
            if (p.type === 6 /* ATTRIBUTE */) {
                if (dynamicOnly)
                    continue;
                if (p.name === name && (p.value || allowEmpty)) {
                    return p;
                }
            }
            else if (p.name === 'bind' &&
                (p.exp || allowEmpty) &&
                isStaticArgOf(p.arg, name)) {
                return p;
            }
        }
    }
    function isStaticArgOf(arg, name) {
        return !!(arg && isStaticExp(arg) && arg.content === name);
    }
    function hasDynamicKeyVBind(node) {
        return node.props.some(p => p.type === 7 /* DIRECTIVE */ &&
            p.name === 'bind' &&
            (!p.arg || // v-bind="obj"
                p.arg.type !== 4 /* SIMPLE_EXPRESSION */ || // v-bind:[_ctx.foo]
                !p.arg.isStatic) // v-bind:[foo]
        );
    }
    function isText(node) {
        return node.type === 5 /* INTERPOLATION */ || node.type === 2 /* TEXT */;
    }
    function isVSlot(p) {
        return p.type === 7 /* DIRECTIVE */ && p.name === 'slot';
    }
    function isTemplateNode(node) {
        return (node.type === 1 /* ELEMENT */ && node.tagType === 3 /* TEMPLATE */);
    }
    function isSlotOutlet(node) {
        return node.type === 1 /* ELEMENT */ && node.tagType === 2 /* SLOT */;
    }
    function getVNodeHelper(ssr, isComponent) {
        return ssr || isComponent ? CREATE_VNODE : CREATE_ELEMENT_VNODE;
    }
    function getVNodeBlockHelper(ssr, isComponent) {
        return ssr || isComponent ? CREATE_BLOCK : CREATE_ELEMENT_BLOCK;
    }
    const propsHelperSet = new Set([NORMALIZE_PROPS, GUARD_REACTIVE_PROPS]);
    function getUnnormalizedProps(props, callPath = []) {
        if (props &&
            !isString(props) &&
            props.type === 14 /* JS_CALL_EXPRESSION */) {
            const callee = props.callee;
            if (!isString(callee) && propsHelperSet.has(callee)) {
                return getUnnormalizedProps(props.arguments[0], callPath.concat(props));
            }
        }
        return [props, callPath];
    }
    function injectProp(node, prop, context) {
        let propsWithInjection;
        /**
         * 1. mergeProps(...)
         * 2. toHandlers(...)
         * 3. normalizeProps(...)
         * 4. normalizeProps(guardReactiveProps(...))
         *
         * we need to get the real props before normalization
         */
        let props = node.type === 13 /* VNODE_CALL */ ? node.props : node.arguments[2];
        let callPath = [];
        let parentCall;
        if (props &&
            !isString(props) &&
            props.type === 14 /* JS_CALL_EXPRESSION */) {
            const ret = getUnnormalizedProps(props);
            props = ret[0];
            callPath = ret[1];
            parentCall = callPath[callPath.length - 1];
        }
        if (props == null || isString(props)) {
            propsWithInjection = createObjectExpression([prop]);
        }
        else if (props.type === 14 /* JS_CALL_EXPRESSION */) {
            // merged props... add ours
            // only inject key to object literal if it's the first argument so that
            // if doesn't override user provided keys
            const first = props.arguments[0];
            if (!isString(first) && first.type === 15 /* JS_OBJECT_EXPRESSION */) {
                first.properties.unshift(prop);
            }
            else {
                if (props.callee === TO_HANDLERS) {
                    // #2366
                    propsWithInjection = createCallExpression(context.helper(MERGE_PROPS), [
                        createObjectExpression([prop]),
                        props
                    ]);
                }
                else {
                    props.arguments.unshift(createObjectExpression([prop]));
                }
            }
            !propsWithInjection && (propsWithInjection = props);
        }
        else if (props.type === 15 /* JS_OBJECT_EXPRESSION */) {
            let alreadyExists = false;
            // check existing key to avoid overriding user provided keys
            if (prop.key.type === 4 /* SIMPLE_EXPRESSION */) {
                const propKeyName = prop.key.content;
                alreadyExists = props.properties.some(p => p.key.type === 4 /* SIMPLE_EXPRESSION */ &&
                    p.key.content === propKeyName);
            }
            if (!alreadyExists) {
                props.properties.unshift(prop);
            }
            propsWithInjection = props;
        }
        else {
            // single v-bind with expression, return a merged replacement
            propsWithInjection = createCallExpression(context.helper(MERGE_PROPS), [
                createObjectExpression([prop]),
                props
            ]);
            // in the case of nested helper call, e.g. `normalizeProps(guardReactiveProps(props))`,
            // it will be rewritten as `normalizeProps(mergeProps({ key: 0 }, props))`,
            // the `guardReactiveProps` will no longer be needed
            if (parentCall && parentCall.callee === GUARD_REACTIVE_PROPS) {
                parentCall = callPath[callPath.length - 2];
            }
        }
        if (node.type === 13 /* VNODE_CALL */) {
            if (parentCall) {
                parentCall.arguments[0] = propsWithInjection;
            }
            else {
                node.props = propsWithInjection;
            }
        }
        else {
            if (parentCall) {
                parentCall.arguments[0] = propsWithInjection;
            }
            else {
                node.arguments[2] = propsWithInjection;
            }
        }
    }
    function toValidAssetId(name, type) {
        // see issue#4422, we need adding identifier on validAssetId if variable `name` has specific character
        return `_${type}_${name.replace(/[^\w]/g, (searchValue, replaceValue) => {
        return searchValue === '-' ? '_' : name.charCodeAt(replaceValue).toString();
    })}`;
    }
    function getMemoedVNodeCall(node) {
        if (node.type === 14 /* JS_CALL_EXPRESSION */ && node.callee === WITH_MEMO) {
            return node.arguments[1].returns;
        }
        else {
            return node;
        }
    }
    function makeBlock(node, { helper, removeHelper, inSSR }) {
        if (!node.isBlock) {
            node.isBlock = true;
            removeHelper(getVNodeHelper(inSSR, node.isComponent));
            helper(OPEN_BLOCK);
            helper(getVNodeBlockHelper(inSSR, node.isComponent));
        }
    }

    const deprecationData = {
        ["COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */]: {
            message: `Platform-native elements with "is" prop will no longer be ` +
                `treated as components in Vue 3 unless the "is" value is explicitly ` +
                `prefixed with "vue:".`,
            link: `https://v3-migration.vuejs.org/breaking-changes/custom-elements-interop.html`
        },
        ["COMPILER_V_BIND_SYNC" /* COMPILER_V_BIND_SYNC */]: {
            message: key => `.sync modifier for v-bind has been removed. Use v-model with ` +
                `argument instead. \`v-bind:${key}.sync\` should be changed to ` +
                `\`v-model:${key}\`.`,
            link: `https://v3-migration.vuejs.org/breaking-changes/v-model.html`
        },
        ["COMPILER_V_BIND_PROP" /* COMPILER_V_BIND_PROP */]: {
            message: `.prop modifier for v-bind has been removed and no longer necessary. ` +
                `Vue 3 will automatically set a binding as DOM property when appropriate.`
        },
        ["COMPILER_V_BIND_OBJECT_ORDER" /* COMPILER_V_BIND_OBJECT_ORDER */]: {
            message: `v-bind="obj" usage is now order sensitive and behaves like JavaScript ` +
                `object spread: it will now overwrite an existing non-mergeable attribute ` +
                `that appears before v-bind in the case of conflict. ` +
                `To retain 2.x behavior, move v-bind to make it the first attribute. ` +
                `You can also suppress this warning if the usage is intended.`,
            link: `https://v3-migration.vuejs.org/breaking-changes/v-bind.html`
        },
        ["COMPILER_V_ON_NATIVE" /* COMPILER_V_ON_NATIVE */]: {
            message: `.native modifier for v-on has been removed as is no longer necessary.`,
            link: `https://v3-migration.vuejs.org/breaking-changes/v-on-native-modifier-removed.html`
        },
        ["COMPILER_V_IF_V_FOR_PRECEDENCE" /* COMPILER_V_IF_V_FOR_PRECEDENCE */]: {
            message: `v-if / v-for precedence when used on the same element has changed ` +
                `in Vue 3: v-if now takes higher precedence and will no longer have ` +
                `access to v-for scope variables. It is best to avoid the ambiguity ` +
                `with <template> tags or use a computed property that filters v-for ` +
                `data source.`,
            link: `https://v3-migration.vuejs.org/breaking-changes/v-if-v-for.html`
        },
        ["COMPILER_NATIVE_TEMPLATE" /* COMPILER_NATIVE_TEMPLATE */]: {
            message: `<template> with no special directives will render as a native template ` +
                `element instead of its inner content in Vue 3.`
        },
        ["COMPILER_INLINE_TEMPLATE" /* COMPILER_INLINE_TEMPLATE */]: {
            message: `"inline-template" has been removed in Vue 3.`,
            link: `https://v3-migration.vuejs.org/breaking-changes/inline-template-attribute.html`
        },
        ["COMPILER_FILTER" /* COMPILER_FILTERS */]: {
            message: `filters have been removed in Vue 3. ` +
                `The "|" symbol will be treated as native JavaScript bitwise OR operator. ` +
                `Use method calls or computed properties instead.`,
            link: `https://v3-migration.vuejs.org/breaking-changes/filters.html`
        }
    };
    function getCompatValue(key, context) {
        const config = context.options
            ? context.options.compatConfig
            : context.compatConfig;
        const value = config && config[key];
        if (key === 'MODE') {
            return value || 3; // compiler defaults to v3 behavior
        }
        else {
            return value;
        }
    }
    function isCompatEnabled(key, context) {
        const mode = getCompatValue('MODE', context);
        const value = getCompatValue(key, context);
        // in v3 mode, only enable if explicitly set to true
        // otherwise enable for any non-false value
        return mode === 3 ? value === true : value !== false;
    }
    function checkCompatEnabled(key, context, loc, ...args) {
        const enabled = isCompatEnabled(key, context);
        if (enabled) {
            warnDeprecation(key, context, loc, ...args);
        }
        return enabled;
    }
    function warnDeprecation(key, context, loc, ...args) {
        const val = getCompatValue(key, context);
        if (val === 'suppress-warning') {
            return;
        }
        const { message, link } = deprecationData[key];
        const msg = `(deprecation ${key}) ${typeof message === 'function' ? message(...args) : message}${link ? `\n  Details: ${link}` : ``}`;
        const err = new SyntaxError(msg);
        err.code = key;
        if (loc)
            err.loc = loc;
        context.onWarn(err);
    }

    // The default decoder only provides escapes for characters reserved as part of
    // the template syntax, and is only used if the custom renderer did not provide
    // a platform-specific decoder.
    const decodeRE = /&(gt|lt|amp|apos|quot);/g;
    const decodeMap = {
        gt: '>',
        lt: '<',
        amp: '&',
        apos: "'",
        quot: '"'
    };
    const defaultParserOptions = {
        delimiters: [`{{`, `}}`],
        getNamespace: () => 0 /* HTML */,
        getTextMode: () => 0 /* DATA */,
        isVoidTag: NO,
        isPreTag: NO,
        isCustomElement: NO,
        decodeEntities: (rawText) => rawText.replace(decodeRE, (_, p1) => decodeMap[p1]),
        onError: defaultOnError,
        onWarn: defaultOnWarn,
        comments: true
    };
    function baseParse(content, options = {}) {
        const context = createParserContext(content, options);
        const start = getCursor(context);
        return createRoot(parseChildren(context, 0 /* DATA */, []), getSelection(context, start));
    }
    function createParserContext(content, rawOptions) {
        const options = extend({}, defaultParserOptions);
        let key;
        for (key in rawOptions) {
            // @ts-ignore
            options[key] =
                rawOptions[key] === undefined
                    ? defaultParserOptions[key]
                    : rawOptions[key];
        }
        return {
            options,
            column: 1,
            line: 1,
            offset: 0,
            originalSource: content,
            source: content,
            inPre: false,
            inVPre: false,
            onWarn: options.onWarn
        };
    }
    function parseChildren(context, mode, ancestors) {
        const parent = last(ancestors);
        const ns = parent ? parent.ns : 0 /* HTML */;
        const nodes = [];
        while (!isEnd(context, mode, ancestors)) {
            const s = context.source;
            let node = undefined;
            if (mode === 0 /* DATA */ || mode === 1 /* RCDATA */) {
                if (!context.inVPre && startsWith(s, context.options.delimiters[0])) {
                    // '{{'
                    node = parseInterpolation(context, mode);
                }
                else if (mode === 0 /* DATA */ && s[0] === '<') {
                    // https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state
                    if (s.length === 1) {
                        emitError(context, 5 /* EOF_BEFORE_TAG_NAME */, 1);
                    }
                    else if (s[1] === '!') {
                        // https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state
                        if (startsWith(s, '<!--')) {
                            node = parseComment(context);
                        }
                        else if (startsWith(s, '<!DOCTYPE')) {
                            // Ignore DOCTYPE by a limitation.
                            node = parseBogusComment(context);
                        }
                        else if (startsWith(s, '<![CDATA[')) {
                            if (ns !== 0 /* HTML */) {
                                node = parseCDATA(context, ancestors);
                            }
                            else {
                                emitError(context, 1 /* CDATA_IN_HTML_CONTENT */);
                                node = parseBogusComment(context);
                            }
                        }
                        else {
                            emitError(context, 11 /* INCORRECTLY_OPENED_COMMENT */);
                            node = parseBogusComment(context);
                        }
                    }
                    else if (s[1] === '/') {
                        // https://html.spec.whatwg.org/multipage/parsing.html#end-tag-open-state
                        if (s.length === 2) {
                            emitError(context, 5 /* EOF_BEFORE_TAG_NAME */, 2);
                        }
                        else if (s[2] === '>') {
                            emitError(context, 14 /* MISSING_END_TAG_NAME */, 2);
                            advanceBy(context, 3);
                            continue;
                        }
                        else if (/[a-z]/i.test(s[2])) {
                            emitError(context, 23 /* X_INVALID_END_TAG */);
                            parseTag(context, 1 /* End */, parent);
                            continue;
                        }
                        else {
                            emitError(context, 12 /* INVALID_FIRST_CHARACTER_OF_TAG_NAME */, 2);
                            node = parseBogusComment(context);
                        }
                    }
                    else if (/[a-z]/i.test(s[1])) {
                        node = parseElement(context, ancestors);
                    }
                    else if (s[1] === '?') {
                        emitError(context, 21 /* UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME */, 1);
                        node = parseBogusComment(context);
                    }
                    else {
                        emitError(context, 12 /* INVALID_FIRST_CHARACTER_OF_TAG_NAME */, 1);
                    }
                }
            }
            if (!node) {
                node = parseText(context, mode);
            }
            if (isArray(node)) {
                for (let i = 0; i < node.length; i++) {
                    pushNode(nodes, node[i]);
                }
            }
            else {
                pushNode(nodes, node);
            }
        }
        // Whitespace handling strategy like v2
        let removedWhitespace = false;
        if (mode !== 2 /* RAWTEXT */ && mode !== 1 /* RCDATA */) {
            const shouldCondense = context.options.whitespace !== 'preserve';
            for (let i = 0; i < nodes.length; i++) {
                const node = nodes[i];
                if (!context.inPre && node.type === 2 /* TEXT */) {
                    if (!/[^\t\r\n\f ]/.test(node.content)) {
                        const prev = nodes[i - 1];
                        const next = nodes[i + 1];
                        // Remove if:
                        // - the whitespace is the first or last node, or:
                        // - (condense mode) the whitespace is adjacent to a comment, or:
                        // - (condense mode) the whitespace is between two elements AND contains newline
                        if (!prev ||
                            !next ||
                            (shouldCondense &&
                                (prev.type === 3 /* COMMENT */ ||
                                    next.type === 3 /* COMMENT */ ||
                                    (prev.type === 1 /* ELEMENT */ &&
                                        next.type === 1 /* ELEMENT */ &&
                                        /[\r\n]/.test(node.content))))) {
                            removedWhitespace = true;
                            nodes[i] = null;
                        }
                        else {
                            // Otherwise, the whitespace is condensed into a single space
                            node.content = ' ';
                        }
                    }
                    else if (shouldCondense) {
                        // in condense mode, consecutive whitespaces in text are condensed
                        // down to a single space.
                        node.content = node.content.replace(/[\t\r\n\f ]+/g, ' ');
                    }
                }
                // Remove comment nodes if desired by configuration.
                else if (node.type === 3 /* COMMENT */ && !context.options.comments) {
                    removedWhitespace = true;
                    nodes[i] = null;
                }
            }
            if (context.inPre && parent && context.options.isPreTag(parent.tag)) {
                // remove leading newline per html spec
                // https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element
                const first = nodes[0];
                if (first && first.type === 2 /* TEXT */) {
                    first.content = first.content.replace(/^\r?\n/, '');
                }
            }
        }
        return removedWhitespace ? nodes.filter(Boolean) : nodes;
    }
    function pushNode(nodes, node) {
        if (node.type === 2 /* TEXT */) {
            const prev = last(nodes);
            // Merge if both this and the previous node are text and those are
            // consecutive. This happens for cases like "a < b".
            if (prev &&
                prev.type === 2 /* TEXT */ &&
                prev.loc.end.offset === node.loc.start.offset) {
                prev.content += node.content;
                prev.loc.end = node.loc.end;
                prev.loc.source += node.loc.source;
                return;
            }
        }
        nodes.push(node);
    }
    function parseCDATA(context, ancestors) {
        advanceBy(context, 9);
        const nodes = parseChildren(context, 3 /* CDATA */, ancestors);
        if (context.source.length === 0) {
            emitError(context, 6 /* EOF_IN_CDATA */);
        }
        else {
            advanceBy(context, 3);
        }
        return nodes;
    }
    function parseComment(context) {
        const start = getCursor(context);
        let content;
        // Regular comment.
        const match = /--(\!)?>/.exec(context.source);
        if (!match) {
            content = context.source.slice(4);
            advanceBy(context, context.source.length);
            emitError(context, 7 /* EOF_IN_COMMENT */);
        }
        else {
            if (match.index <= 3) {
                emitError(context, 0 /* ABRUPT_CLOSING_OF_EMPTY_COMMENT */);
            }
            if (match[1]) {
                emitError(context, 10 /* INCORRECTLY_CLOSED_COMMENT */);
            }
            content = context.source.slice(4, match.index);
            // Advancing with reporting nested comments.
            const s = context.source.slice(0, match.index);
            let prevIndex = 1, nestedIndex = 0;
            while ((nestedIndex = s.indexOf('<!--', prevIndex)) !== -1) {
                advanceBy(context, nestedIndex - prevIndex + 1);
                if (nestedIndex + 4 < s.length) {
                    emitError(context, 16 /* NESTED_COMMENT */);
                }
                prevIndex = nestedIndex + 1;
            }
            advanceBy(context, match.index + match[0].length - prevIndex + 1);
        }
        return {
            type: 3 /* COMMENT */,
            content,
            loc: getSelection(context, start)
        };
    }
    function parseBogusComment(context) {
        const start = getCursor(context);
        const contentStart = context.source[1] === '?' ? 1 : 2;
        let content;
        const closeIndex = context.source.indexOf('>');
        if (closeIndex === -1) {
            content = context.source.slice(contentStart);
            advanceBy(context, context.source.length);
        }
        else {
            content = context.source.slice(contentStart, closeIndex);
            advanceBy(context, closeIndex + 1);
        }
        return {
            type: 3 /* COMMENT */,
            content,
            loc: getSelection(context, start)
        };
    }
    function parseElement(context, ancestors) {
        // Start tag.
        const wasInPre = context.inPre;
        const wasInVPre = context.inVPre;
        const parent = last(ancestors);
        const element = parseTag(context, 0 /* Start */, parent);
        const isPreBoundary = context.inPre && !wasInPre;
        const isVPreBoundary = context.inVPre && !wasInVPre;
        if (element.isSelfClosing || context.options.isVoidTag(element.tag)) {
            // #4030 self-closing <pre> tag
            if (isPreBoundary) {
                context.inPre = false;
            }
            if (isVPreBoundary) {
                context.inVPre = false;
            }
            return element;
        }
        // Children.
        ancestors.push(element);
        const mode = context.options.getTextMode(element, parent);
        const children = parseChildren(context, mode, ancestors);
        ancestors.pop();
        element.children = children;
        // End tag.
        if (startsWithEndTagOpen(context.source, element.tag)) {
            parseTag(context, 1 /* End */, parent);
        }
        else {
            emitError(context, 24 /* X_MISSING_END_TAG */, 0, element.loc.start);
            if (context.source.length === 0 && element.tag.toLowerCase() === 'script') {
                const first = children[0];
                if (first && startsWith(first.loc.source, '<!--')) {
                    emitError(context, 8 /* EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT */);
                }
            }
        }
        element.loc = getSelection(context, element.loc.start);
        if (isPreBoundary) {
            context.inPre = false;
        }
        if (isVPreBoundary) {
            context.inVPre = false;
        }
        return element;
    }
    const isSpecialTemplateDirective = /*#__PURE__*/ makeMap(`if,else,else-if,for,slot`);
    function parseTag(context, type, parent) {
        // Tag open.
        const start = getCursor(context);
        const match = /^<\/?([a-z][^\t\r\n\f />]*)/i.exec(context.source);
        const tag = match[1];
        const ns = context.options.getNamespace(tag, parent);
        advanceBy(context, match[0].length);
        advanceSpaces(context);
        // save current state in case we need to re-parse attributes with v-pre
        const cursor = getCursor(context);
        const currentSource = context.source;
        // check <pre> tag
        if (context.options.isPreTag(tag)) {
            context.inPre = true;
        }
        // Attributes.
        let props = parseAttributes(context, type);
        // check v-pre
        if (type === 0 /* Start */ &&
            !context.inVPre &&
            props.some(p => p.type === 7 /* DIRECTIVE */ && p.name === 'pre')) {
            context.inVPre = true;
            // reset context
            extend(context, cursor);
            context.source = currentSource;
            // re-parse attrs and filter out v-pre itself
            props = parseAttributes(context, type).filter(p => p.name !== 'v-pre');
        }
        // Tag close.
        let isSelfClosing = false;
        if (context.source.length === 0) {
            emitError(context, 9 /* EOF_IN_TAG */);
        }
        else {
            isSelfClosing = startsWith(context.source, '/>');
            if (type === 1 /* End */ && isSelfClosing) {
                emitError(context, 4 /* END_TAG_WITH_TRAILING_SOLIDUS */);
            }
            advanceBy(context, isSelfClosing ? 2 : 1);
        }
        if (type === 1 /* End */) {
            return;
        }
        let tagType = 0 /* ELEMENT */;
        if (!context.inVPre) {
            if (tag === 'slot') {
                tagType = 2 /* SLOT */;
            }
            else if (tag === 'template') {
                if (props.some(p => p.type === 7 /* DIRECTIVE */ && isSpecialTemplateDirective(p.name))) {
                    tagType = 3 /* TEMPLATE */;
                }
            }
            else if (isComponent(tag, props, context)) {
                tagType = 1 /* COMPONENT */;
            }
        }
        return {
            type: 1 /* ELEMENT */,
            ns,
            tag,
            tagType,
            props,
            isSelfClosing,
            children: [],
            loc: getSelection(context, start),
            codegenNode: undefined // to be created during transform phase
        };
    }
    function isComponent(tag, props, context) {
        const options = context.options;
        if (options.isCustomElement(tag)) {
            return false;
        }
        if (tag === 'component' ||
            /^[A-Z]/.test(tag) ||
            isCoreComponent(tag) ||
            (options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
            (options.isNativeTag && !options.isNativeTag(tag))) {
            return true;
        }
        // at this point the tag should be a native tag, but check for potential "is"
        // casting
        for (let i = 0; i < props.length; i++) {
            const p = props[i];
            if (p.type === 6 /* ATTRIBUTE */) {
                if (p.name === 'is' && p.value) {
                    if (p.value.content.startsWith('vue:')) {
                        return true;
                    }
                }
            }
            else {
                // directive
                // v-is (TODO Deprecate)
                if (p.name === 'is') {
                    return true;
                }
                else if (
                // :is on plain element - only treat as component in compat mode
                p.name === 'bind' &&
                    isStaticArgOf(p.arg, 'is') &&
                    false &&
                    checkCompatEnabled("COMPILER_IS_ON_ELEMENT" /* COMPILER_IS_ON_ELEMENT */, context, p.loc)) {
                    return true;
                }
            }
        }
    }
    function parseAttributes(context, type) {
        const props = [];
        const attributeNames = new Set();
        while (context.source.length > 0 &&
            !startsWith(context.source, '>') &&
            !startsWith(context.source, '/>')) {
            if (startsWith(context.source, '/')) {
                emitError(context, 22 /* UNEXPECTED_SOLIDUS_IN_TAG */);
                advanceBy(context, 1);
                advanceSpaces(context);
                continue;
            }
            if (type === 1 /* End */) {
                emitError(context, 3 /* END_TAG_WITH_ATTRIBUTES */);
            }
            const attr = parseAttribute(context, attributeNames);
            // Trim whitespace between class
            // https://github.com/vuejs/core/issues/4251
            if (attr.type === 6 /* ATTRIBUTE */ &&
                attr.value &&
                attr.name === 'class') {
                attr.value.content = attr.value.content.replace(/\s+/g, ' ').trim();
            }
            if (type === 0 /* Start */) {
                props.push(attr);
            }
            if (/^[^\t\r\n\f />]/.test(context.source)) {
                emitError(context, 15 /* MISSING_WHITESPACE_BETWEEN_ATTRIBUTES */);
            }
            advanceSpaces(context);
        }
        return props;
    }
    function parseAttribute(context, nameSet) {
        // Name.
        const start = getCursor(context);
        const match = /^[^\t\r\n\f />][^\t\r\n\f />=]*/.exec(context.source);
        const name = match[0];
        if (nameSet.has(name)) {
            emitError(context, 2 /* DUPLICATE_ATTRIBUTE */);
        }
        nameSet.add(name);
        if (name[0] === '=') {
            emitError(context, 19 /* UNEXPECTED_EQUALS_SIGN_BEFORE_ATTRIBUTE_NAME */);
        }
        {
            const pattern = /["'<]/g;
            let m;
            while ((m = pattern.exec(name))) {
                emitError(context, 17 /* UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME */, m.index);
            }
        }
        advanceBy(context, name.length);
        // Value
        let value = undefined;
        if (/^[\t\r\n\f ]*=/.test(context.source)) {
            advanceSpaces(context);
            advanceBy(context, 1);
            advanceSpaces(context);
            value = parseAttributeValue(context);
            if (!value) {
                emitError(context, 13 /* MISSING_ATTRIBUTE_VALUE */);
            }
        }
        const loc = getSelection(context, start);
        if (!context.inVPre && /^(v-[A-Za-z0-9-]|:|\.|@|#)/.test(name)) {
            const match = /(?:^v-([a-z0-9-]+))?(?:(?::|^\.|^@|^#)(\[[^\]]+\]|[^\.]+))?(.+)?$/i.exec(name);
            let isPropShorthand = startsWith(name, '.');
            let dirName = match[1] ||
                (isPropShorthand || startsWith(name, ':')
                    ? 'bind'
                    : startsWith(name, '@')
                        ? 'on'
                        : 'slot');
            let arg;
            if (match[2]) {
                const isSlot = dirName === 'slot';
                const startOffset = name.lastIndexOf(match[2]);
                const loc = getSelection(context, getNewPosition(context, start, startOffset), getNewPosition(context, start, startOffset + match[2].length + ((isSlot && match[3]) || '').length));
                let content = match[2];
                let isStatic = true;
                if (content.startsWith('[')) {
                    isStatic = false;
                    if (!content.endsWith(']')) {
                        emitError(context, 27 /* X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END */);
                        content = content.slice(1);
                    }
                    else {
                        content = content.slice(1, content.length - 1);
                    }
                }
                else if (isSlot) {
                    // #1241 special case for v-slot: vuetify relies extensively on slot
                    // names containing dots. v-slot doesn't have any modifiers and Vue 2.x
                    // supports such usage so we are keeping it consistent with 2.x.
                    content += match[3] || '';
                }
                arg = {
                    type: 4 /* SIMPLE_EXPRESSION */,
                    content,
                    isStatic,
                    constType: isStatic
                        ? 3 /* CAN_STRINGIFY */
                        : 0 /* NOT_CONSTANT */,
                    loc
                };
            }
            if (value && value.isQuoted) {
                const valueLoc = value.loc;
                valueLoc.start.offset++;
                valueLoc.start.column++;
                valueLoc.end = advancePositionWithClone(valueLoc.start, value.content);
                valueLoc.source = valueLoc.source.slice(1, -1);
            }
            const modifiers = match[3] ? match[3].slice(1).split('.') : [];
            if (isPropShorthand)
                modifiers.push('prop');
            return {
                type: 7 /* DIRECTIVE */,
                name: dirName,
                exp: value && {
                    type: 4 /* SIMPLE_EXPRESSION */,
                    content: value.content,
                    isStatic: false,
                    // Treat as non-constant by default. This can be potentially set to
                    // other values by `transformExpression` to make it eligible for hoisting.
                    constType: 0 /* NOT_CONSTANT */,
                    loc: value.loc
                },
                arg,
                modifiers,
                loc
            };
        }
        // missing directive name or illegal directive name
        if (!context.inVPre && startsWith(name, 'v-')) {
            emitError(context, 26 /* X_MISSING_DIRECTIVE_NAME */);
        }
        return {
            type: 6 /* ATTRIBUTE */,
            name,
            value: value && {
                type: 2 /* TEXT */,
                content: value.content,
                loc: value.loc
            },
            loc
        };
    }
    function parseAttributeValue(context) {
        const start = getCursor(context);
        let content;
        const quote = context.source[0];
        const isQuoted = quote === `"` || quote === `'`;
        if (isQuoted) {
            // Quoted value.
            advanceBy(context, 1);
            const endIndex = context.source.indexOf(quote);
            if (endIndex === -1) {
                content = parseTextData(context, context.source.length, 4 /* ATTRIBUTE_VALUE */);
            }
            else {
                content = parseTextData(context, endIndex, 4 /* ATTRIBUTE_VALUE */);
                advanceBy(context, 1);
            }
        }
        else {
            // Unquoted
            const match = /^[^\t\r\n\f >]+/.exec(context.source);
            if (!match) {
                return undefined;
            }
            const unexpectedChars = /["'<=`]/g;
            let m;
            while ((m = unexpectedChars.exec(match[0]))) {
                emitError(context, 18 /* UNEXPECTED_CHARACTER_IN_UNQUOTED_ATTRIBUTE_VALUE */, m.index);
            }
            content = parseTextData(context, match[0].length, 4 /* ATTRIBUTE_VALUE */);
        }
        return { content, isQuoted, loc: getSelection(context, start) };
    }
    function parseInterpolation(context, mode) {
        const [open, close] = context.options.delimiters;
        const closeIndex = context.source.indexOf(close, open.length);
        if (closeIndex === -1) {
            emitError(context, 25 /* X_MISSING_INTERPOLATION_END */);
            return undefined;
        }
        const start = getCursor(context);
        advanceBy(context, open.length);
        const innerStart = getCursor(context);
        const innerEnd = getCursor(context);
        const rawContentLength = closeIndex - open.length;
        const rawContent = context.source.slice(0, rawContentLength);
        const preTrimContent = parseTextData(context, rawContentLength, mode);
        const content = preTrimContent.trim();
        const startOffset = preTrimContent.indexOf(content);
        if (startOffset > 0) {
            advancePositionWithMutation(innerStart, rawContent, startOffset);
        }
        const endOffset = rawContentLength - (preTrimContent.length - content.length - startOffset);
        advancePositionWithMutation(innerEnd, rawContent, endOffset);
        advanceBy(context, close.length);
        return {
            type: 5 /* INTERPOLATION */,
            content: {
                type: 4 /* SIMPLE_EXPRESSION */,
                isStatic: false,
                // Set `isConstant` to false by default and will decide in transformExpression
                constType: 0 /* NOT_CONSTANT */,
                content,
                loc: getSelection(context, innerStart, innerEnd)
            },
            loc: getSelection(context, start)
        };
    }
    function parseText(context, mode) {
        const endTokens = mode === 3 /* CDATA */ ? [']]>'] : ['<', context.options.delimiters[0]];
        let endIndex = context.source.length;
        for (let i = 0; i < endTokens.length; i++) {
            const index = context.source.indexOf(endTokens[i], 1);
            if (index !== -1 && endIndex > index) {
                endIndex = index;
            }
        }
        const start = getCursor(context);
        const content = parseTextData(context, endIndex, mode);
        return {
            type: 2 /* TEXT */,
            content,
            loc: getSelection(context, start)
        };
    }
    /**
     * Get text data with a given length from the current location.
     * This translates HTML entities in the text data.
     */
    function parseTextData(context, length, mode) {
        const rawText = context.source.slice(0, length);
        advanceBy(context, length);
        if (mode === 2 /* RAWTEXT */ ||
            mode === 3 /* CDATA */ ||
            !rawText.includes('&')) {
            return rawText;
        }
        else {
            // DATA or RCDATA containing "&"". Entity decoding required.
            return context.options.decodeEntities(rawText, mode === 4 /* ATTRIBUTE_VALUE */);
        }
    }
    function getCursor(context) {
        const { column, line, offset } = context;
        return { column, line, offset };
    }
    function getSelection(context, start, end) {
        end = end || getCursor(context);
        return {
            start,
            end,
            source: context.originalSource.slice(start.offset, end.offset)
        };
    }
    function last(xs) {
        return xs[xs.length - 1];
    }
    function startsWith(source, searchString) {
        return source.startsWith(searchString);
    }
    function advanceBy(context, numberOfCharacters) {
        const { source } = context;
        advancePositionWithMutation(context, source, numberOfCharacters);
        context.source = source.slice(numberOfCharacters);
    }
    function advanceSpaces(context) {
        const match = /^[\t\r\n\f ]+/.exec(context.source);
        if (match) {
            advanceBy(context, match[0].length);
        }
    }
    function getNewPosition(context, start, numberOfCharacters) {
        return advancePositionWithClone(start, context.originalSource.slice(start.offset, numberOfCharacters), numberOfCharacters);
    }
    function emitError(context, code, offset, loc = getCursor(context)) {
        if (offset) {
            loc.offset += offset;
            loc.column += offset;
        }
        context.options.onError(createCompilerError(code, {
            start: loc,
            end: loc,
            source: ''
        }));
    }
    function isEnd(context, mode, ancestors) {
        const s = context.source;
        switch (mode) {
            case 0 /* DATA */:
                if (startsWith(s, '</')) {
                    // TODO: probably bad performance
                    for (let i = ancestors.length - 1; i >= 0; --i) {
                        if (startsWithEndTagOpen(s, ancestors[i].tag)) {
                            return true;
                        }
                    }
                }
                break;
            case 1 /* RCDATA */:
            case 2 /* RAWTEXT */: {
                const parent = last(ancestors);
                if (parent && startsWithEndTagOpen(s, parent.tag)) {
                    return true;
                }
                break;
            }
            case 3 /* CDATA */:
                if (startsWith(s, ']]>')) {
                    return true;
                }
                break;
        }
        return !s;
    }
    function startsWithEndTagOpen(source, tag) {
        return (startsWith(source, '</') &&
            source.slice(2, 2 + tag.length).toLowerCase() === tag.toLowerCase() &&
            /[\t\r\n\f />]/.test(source[2 + tag.length] || '>'));
    }

    function hoistStatic(root, context) {
        walk(root, context, 
        // Root node is unfortunately non-hoistable due to potential parent
        // fallthrough attributes.
        isSingleElementRoot(root, root.children[0]));
    }
    function isSingleElementRoot(root, child) {
        const { children } = root;
        return (children.length === 1 &&
            child.type === 1 /* ELEMENT */ &&
            !isSlotOutlet(child));
    }
    function walk(node, context, doNotHoistNode = false) {
        const { children } = node;
        const originalCount = children.length;
        let hoistedCount = 0;
        for (let i = 0; i < children.length; i++) {
            const child = children[i];
            // only plain elements & text calls are eligible for hoisting.
            if (child.type === 1 /* ELEMENT */ &&
                child.tagType === 0 /* ELEMENT */) {
                const constantType = doNotHoistNode
                    ? 0 /* NOT_CONSTANT */
                    : getConstantType(child, context);
                if (constantType > 0 /* NOT_CONSTANT */) {
                    if (constantType >= 2 /* CAN_HOIST */) {
                        child.codegenNode.patchFlag =
                            -1 /* HOISTED */ + (` /* HOISTED */` );
                        child.codegenNode = context.hoist(child.codegenNode);
                        hoistedCount++;
                        continue;
                    }
                }
                else {
                    // node may contain dynamic children, but its props may be eligible for
                    // hoisting.
                    const codegenNode = child.codegenNode;
                    if (codegenNode.type === 13 /* VNODE_CALL */) {
                        const flag = getPatchFlag(codegenNode);
                        if ((!flag ||
                            flag === 512 /* NEED_PATCH */ ||
                            flag === 1 /* TEXT */) &&
                            getGeneratedPropsConstantType(child, context) >=
                                2 /* CAN_HOIST */) {
                            const props = getNodeProps(child);
                            if (props) {
                                codegenNode.props = context.hoist(props);
                            }
                        }
                        if (codegenNode.dynamicProps) {
                            codegenNode.dynamicProps = context.hoist(codegenNode.dynamicProps);
                        }
                    }
                }
            }
            else if (child.type === 12 /* TEXT_CALL */ &&
                getConstantType(child.content, context) >= 2 /* CAN_HOIST */) {
                child.codegenNode = context.hoist(child.codegenNode);
                hoistedCount++;
            }
            // walk further
            if (child.type === 1 /* ELEMENT */) {
                const isComponent = child.tagType === 1 /* COMPONENT */;
                if (isComponent) {
                    context.scopes.vSlot++;
                }
                walk(child, context);
                if (isComponent) {
                    context.scopes.vSlot--;
                }
            }
            else if (child.type === 11 /* FOR */) {
                // Do not hoist v-for single child because it has to be a block
                walk(child, context, child.children.length === 1);
            }
            else if (child.type === 9 /* IF */) {
                for (let i = 0; i < child.branches.length; i++) {
                    // Do not hoist v-if single child because it has to be a block
                    walk(child.branches[i], context, child.branches[i].children.length === 1);
                }
            }
        }
        if (hoistedCount && context.transformHoist) {
            context.transformHoist(children, context, node);
        }
        // all children were hoisted - the entire children array is hoistable.
        if (hoistedCount &&
            hoistedCount === originalCount &&
            node.type === 1 /* ELEMENT */ &&
            node.tagType === 0 /* ELEMENT */ &&
            node.codegenNode &&
            node.codegenNode.type === 13 /* VNODE_CALL */ &&
            isArray(node.codegenNode.children)) {
            node.codegenNode.children = context.hoist(createArrayExpression(node.codegenNode.children));
        }
    }
    function getConstantType(node, context) {
        const { constantCache } = context;
        switch (node.type) {
            case 1 /* ELEMENT */:
                if (node.tagType !== 0 /* ELEMENT */) {
                    return 0 /* NOT_CONSTANT */;
                }
                const cached = constantCache.get(node);
                if (cached !== undefined) {
                    return cached;
                }
                const codegenNode = node.codegenNode;
                if (codegenNode.type !== 13 /* VNODE_CALL */) {
                    return 0 /* NOT_CONSTANT */;
                }
                if (codegenNode.isBlock &&
                    node.tag !== 'svg' &&
                    node.tag !== 'foreignObject') {
                    return 0 /* NOT_CONSTANT */;
                }
                const flag = getPatchFlag(codegenNode);
                if (!flag) {
                    let returnType = 3 /* CAN_STRINGIFY */;
                    // Element itself has no patch flag. However we still need to check:
                    // 1. Even for a node with no patch flag, it is possible for it to contain
                    // non-hoistable expressions that refers to scope variables, e.g. compiler
                    // injected keys or cached event handlers. Therefore we need to always
                    // check the codegenNode's props to be sure.
                    const generatedPropsType = getGeneratedPropsConstantType(node, context);
                    if (generatedPropsType === 0 /* NOT_CONSTANT */) {
                        constantCache.set(node, 0 /* NOT_CONSTANT */);
                        return 0 /* NOT_CONSTANT */;
                    }
                    if (generatedPropsType < returnType) {
                        returnType = generatedPropsType;
                    }
                    // 2. its children.
                    for (let i = 0; i < node.children.length; i++) {
                        const childType = getConstantType(node.children[i], context);
                        if (childType === 0 /* NOT_CONSTANT */) {
                            constantCache.set(node, 0 /* NOT_CONSTANT */);
                            return 0 /* NOT_CONSTANT */;
                        }
                        if (childType < returnType) {
                            returnType = childType;
                        }
                    }
                    // 3. if the type is not already CAN_SKIP_PATCH which is the lowest non-0
                    // type, check if any of the props can cause the type to be lowered
                    // we can skip can_patch because it's guaranteed by the absence of a
                    // patchFlag.
                    if (returnType > 1 /* CAN_SKIP_PATCH */) {
                        for (let i = 0; i < node.props.length; i++) {
                            const p = node.props[i];
                            if (p.type === 7 /* DIRECTIVE */ && p.name === 'bind' && p.exp) {
                                const expType = getConstantType(p.exp, context);
                                if (expType === 0 /* NOT_CONSTANT */) {
                                    constantCache.set(node, 0 /* NOT_CONSTANT */);
                                    return 0 /* NOT_CONSTANT */;
                                }
                                if (expType < returnType) {
                                    returnType = expType;
                                }
                            }
                        }
                    }
                    // only svg/foreignObject could be block here, however if they are
                    // static then they don't need to be blocks since there will be no
                    // nested updates.
                    if (codegenNode.isBlock) {
                        // except set custom directives.
                        for (let i = 0; i < node.props.length; i++) {
                            const p = node.props[i];
                            if (p.type === 7 /* DIRECTIVE */) {
                                constantCache.set(node, 0 /* NOT_CONSTANT */);
                                return 0 /* NOT_CONSTANT */;
                            }
                        }
                        context.removeHelper(OPEN_BLOCK);
                        context.removeHelper(getVNodeBlockHelper(context.inSSR, codegenNode.isComponent));
                        codegenNode.isBlock = false;
                        context.helper(getVNodeHelper(context.inSSR, codegenNode.isComponent));
                    }
                    constantCache.set(node, returnType);
                    return returnType;
                }
                else {
                    constantCache.set(node, 0 /* NOT_CONSTANT */);
                    return 0 /* NOT_CONSTANT */;
                }
            case 2 /* TEXT */:
            case 3 /* COMMENT */:
                return 3 /* CAN_STRINGIFY */;
            case 9 /* IF */:
            case 11 /* FOR */:
            case 10 /* IF_BRANCH */:
                return 0 /* NOT_CONSTANT */;
            case 5 /* INTERPOLATION */:
            case 12 /* TEXT_CALL */:
                return getConstantType(node.content, context);
            case 4 /* SIMPLE_EXPRESSION */:
                return node.constType;
            case 8 /* COMPOUND_EXPRESSION */:
                let returnType = 3 /* CAN_STRINGIFY */;
                for (let i = 0; i < node.children.length; i++) {
                    const child = node.children[i];
                    if (isString(child) || isSymbol(child)) {
                        continue;
                    }
                    const childType = getConstantType(child, context);
                    if (childType === 0 /* NOT_CONSTANT */) {
                        return 0 /* NOT_CONSTANT */;
                    }
                    else if (childType < returnType) {
                        returnType = childType;
                    }
                }
                return returnType;
            default:
                return 0 /* NOT_CONSTANT */;
        }
    }
    const allowHoistedHelperSet = new Set([
        NORMALIZE_CLASS,
        NORMALIZE_STYLE,
        NORMALIZE_PROPS,
        GUARD_REACTIVE_PROPS
    ]);
    function getConstantTypeOfHelperCall(value, context) {
        if (value.type === 14 /* JS_CALL_EXPRESSION */ &&
            !isString(value.callee) &&
            allowHoistedHelperSet.has(value.callee)) {
            const arg = value.arguments[0];
            if (arg.type === 4 /* SIMPLE_EXPRESSION */) {
                return getConstantType(arg, context);
            }
            else if (arg.type === 14 /* JS_CALL_EXPRESSION */) {
                // in the case of nested helper call, e.g. `normalizeProps(guardReactiveProps(exp))`
                return getConstantTypeOfHelperCall(arg, context);
            }
        }
        return 0 /* NOT_CONSTANT */;
    }
    function getGeneratedPropsConstantType(node, context) {
        let returnType = 3 /* CAN_STRINGIFY */;
        const props = getNodeProps(node);
        if (props && props.type === 15 /* JS_OBJECT_EXPRESSION */) {
            const { properties } = props;
            for (let i = 0; i < properties.length; i++) {
                const { key, value } = properties[i];
                const keyType = getConstantType(key, context);
                if (keyType === 0 /* NOT_CONSTANT */) {
                    return keyType;
                }
                if (keyType < returnType) {
                    returnType = keyType;
                }
                let valueType;
                if (value.type === 4 /* SIMPLE_EXPRESSION */) {
                    valueType = getConstantType(value, context);
                }
                else if (value.type === 14 /* JS_CALL_EXPRESSION */) {
                    // some helper calls can be hoisted,
                    // such as the `normalizeProps` generated by the compiler for pre-normalize class,
                    // in this case we need to respect the ConstantType of the helper's arguments
                    valueType = getConstantTypeOfHelperCall(value, context);
                }
                else {
                    valueType = 0 /* NOT_CONSTANT */;
                }
                if (valueType === 0 /* NOT_CONSTANT */) {
                    return valueType;
                }
                if (valueType < returnType) {
                    returnType = valueType;
                }
            }
        }
        return returnType;
    }
    function getNodeProps(node) {
        const codegenNode = node.codegenNode;
        if (codegenNode.type === 13 /* VNODE_CALL */) {
            return codegenNode.props;
        }
    }
    function getPatchFlag(node) {
        const flag = node.patchFlag;
        return flag ? parseInt(flag, 10) : undefined;
    }

    function createTransformContext(root, { filename = '', prefixIdentifiers = false, hoistStatic = false, cacheHandlers = false, nodeTransforms = [], directiveTransforms = {}, transformHoist = null, isBuiltInComponent = NOOP, isCustomElement = NOOP, expressionPlugins = [], scopeId = null, slotted = true, ssr = false, inSSR = false, ssrCssVars = ``, bindingMetadata = EMPTY_OBJ, inline = false, isTS = false, onError = defaultOnError, onWarn = defaultOnWarn, compatConfig }) {
        const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/);
        const context = {
            // options
            selfName: nameMatch && capitalize(camelize(nameMatch[1])),
            prefixIdentifiers,
            hoistStatic,
            cacheHandlers,
            nodeTransforms,
            directiveTransforms,
            transformHoist,
            isBuiltInComponent,
            isCustomElement,
            expressionPlugins,
            scopeId,
            slotted,
            ssr,
            inSSR,
            ssrCssVars,
            bindingMetadata,
            inline,
            isTS,
            onError,
            onWarn,
            compatConfig,
            // state
            root,
            helpers: new Map(),
            components: new Set(),
            directives: new Set(),
            hoists: [],
            imports: [],
            constantCache: new Map(),
            temps: 0,
            cached: 0,
            identifiers: Object.create(null),
            scopes: {
                vFor: 0,
                vSlot: 0,
                vPre: 0,
                vOnce: 0
            },
            parent: null,
            currentNode: root,
            childIndex: 0,
            inVOnce: false,
            // methods
            helper(name) {
                const count = context.helpers.get(name) || 0;
                context.helpers.set(name, count + 1);
                return name;
            },
            removeHelper(name) {
                const count = context.helpers.get(name);
                if (count) {
                    const currentCount = count - 1;
                    if (!currentCount) {
                        context.helpers.delete(name);
                    }
                    else {
                        context.helpers.set(name, currentCount);
                    }
                }
            },
            helperString(name) {
                return `_${helperNameMap[context.helper(name)]}`;
            },
            replaceNode(node) {
                /* istanbul ignore if */
                {
                    if (!context.currentNode) {
                        throw new Error(`Node being replaced is already removed.`);
                    }
                    if (!context.parent) {
                        throw new Error(`Cannot replace root node.`);
                    }
                }
                context.parent.children[context.childIndex] = context.currentNode = node;
            },
            removeNode(node) {
                if (!context.parent) {
                    throw new Error(`Cannot remove root node.`);
                }
                const list = context.parent.children;
                const removalIndex = node
                    ? list.indexOf(node)
                    : context.currentNode
                        ? context.childIndex
                        : -1;
                /* istanbul ignore if */
                if (removalIndex < 0) {
                    throw new Error(`node being removed is not a child of current parent`);
                }
                if (!node || node === context.currentNode) {
                    // current node removed
                    context.currentNode = null;
                    context.onNodeRemoved();
                }
                else {
                    // sibling node removed
                    if (context.childIndex > removalIndex) {
                        context.childIndex--;
                        context.onNodeRemoved();
                    }
                }
                context.parent.children.splice(removalIndex, 1);
            },
            onNodeRemoved: () => { },
            addIdentifiers(exp) {
            },
            removeIdentifiers(exp) {
            },
            hoist(exp) {
                if (isString(exp))
                    exp = createSimpleExpression(exp);
                context.hoists.push(exp);
                const identifier = createSimpleExpression(`_hoisted_${context.hoists.length}`, false, exp.loc, 2 /* CAN_HOIST */);
                identifier.hoisted = exp;
                return identifier;
            },
            cache(exp, isVNode = false) {
                return createCacheExpression(context.cached++, exp, isVNode);
            }
        };
        return context;
    }
    function transform$2(root, options) {
        const context = createTransformContext(root, options);
        traverseNode(root, context);
        if (options.hoistStatic) {
            hoistStatic(root, context);
        }
        if (!options.ssr) {
            createRootCodegen(root, context);
        }
        // finalize meta information
        root.helpers = [...context.helpers.keys()];
        root.components = [...context.components];
        root.directives = [...context.directives];
        root.imports = context.imports;
        root.hoists = context.hoists;
        root.temps = context.temps;
        root.cached = context.cached;
    }
    function createRootCodegen(root, context) {
        const { helper } = context;
        const { children } = root;
        if (children.length === 1) {
            const child = children[0];
            // if the single child is an element, turn it into a block.
            if (isSingleElementRoot(root, child) && child.codegenNode) {
                // single element root is never hoisted so codegenNode will never be
                // SimpleExpressionNode
                const codegenNode = child.codegenNode;
                if (codegenNode.type === 13 /* VNODE_CALL */) {
                    makeBlock(codegenNode, context);
                }
                root.codegenNode = codegenNode;
            }
            else {
                // - single <slot/>, IfNode, ForNode: already blocks.
                // - single text node: always patched.
                // root codegen falls through via genNode()
                root.codegenNode = child;
            }
        }
        else if (children.length > 1) {
            // root has multiple nodes - return a fragment block.
            let patchFlag = 64 /* STABLE_FRAGMENT */;
            let patchFlagText = PatchFlagNames[64 /* STABLE_FRAGMENT */];
            // check if the fragment actually contains a single valid child with
            // the rest being comments
            if (children.filter(c => c.type !== 3 /* COMMENT */).length === 1) {
                patchFlag |= 2048 /* DEV_ROOT_FRAGMENT */;
                patchFlagText += `, ${PatchFlagNames[2048 /* DEV_ROOT_FRAGMENT */]}`;
            }
            root.codegenNode = createVNodeCall(context, helper(FRAGMENT), undefined, root.children, patchFlag + (` /* ${patchFlagText} */` ), undefined, undefined, true, undefined, false /* isComponent */);
        }
        else ;
    }
    function traverseChildren(parent, context) {
        let i = 0;
        const nodeRemoved = () => {
            i--;
        };
        for (; i < parent.children.length; i++) {
            const child = parent.children[i];
            if (isString(child))
                continue;
            context.parent = parent;
            context.childIndex = i;
            context.onNodeRemoved = nodeRemoved;
            traverseNode(child, context);
        }
    }
    function traverseNode(node, context) {
        context.currentNode = node;
        // apply transform plugins
        const { nodeTransforms } = context;
        const exitFns = [];
        for (let i = 0; i < nodeTransforms.length; i++) {
            const onExit = nodeTransforms[i](node, context);
            if (onExit) {
                if (isArray(onExit)) {
                    exitFns.push(...onExit);
                }
                else {
                    exitFns.push(onExit);
                }
            }
            if (!context.currentNode) {
                // node was removed
                return;
            }
            else {
                // node may have been replaced
                node = context.currentNode;
            }
        }
        switch (node.type) {
            case 3 /* COMMENT */:
                if (!context.ssr) {
                    // inject import for the Comment symbol, which is needed for creating
                    // comment nodes with `createVNode`
                    context.helper(CREATE_COMMENT);
                }
                break;
            case 5 /* INTERPOLATION */:
                // no need to traverse, but we need to inject toString helper
                if (!context.ssr) {
                    context.helper(TO_DISPLAY_STRING);
                }
                break;
            // for container types, further traverse downwards
            case 9 /* IF */:
                for (let i = 0; i < node.branches.length; i++) {
                    traverseNode(node.branches[i], context);
                }
                break;
            case 10 /* IF_BRANCH */:
            case 11 /* FOR */:
            case 1 /* ELEMENT */:
            case 0 /* ROOT */:
                traverseChildren(node, context);
                break;
        }
        // exit transforms
        context.currentNode = node;
        let i = exitFns.length;
        while (i--) {
            exitFns[i]();
        }
    }
    function createStructuralDirectiveTransform(name, fn) {
        const matches = isString(name)
            ? (n) => n === name
            : (n) => name.test(n);
        return (node, context) => {
            if (node.type === 1 /* ELEMENT */) {
                const { props } = node;
                // structural directive transforms are not concerned with slots
                // as they are handled separately in vSlot.ts
                if (node.tagType === 3 /* TEMPLATE */ && props.some(isVSlot)) {
                    return;
                }
                const exitFns = [];
                for (let i = 0; i < props.length; i++) {
                    const prop = props[i];
                    if (prop.type === 7 /* DIRECTIVE */ && matches(prop.name)) {
                        // structural directives are removed to avoid infinite recursion
                        // also we remove them *before* applying so that it can further
                        // traverse itself in case it moves the node around
                        props.splice(i, 1);
                        i--;
                        const onExit = fn(node, prop, context);
                        if (onExit)
                            exitFns.push(onExit);
                    }
                }
                return exitFns;
            }
        };
    }

    const PURE_ANNOTATION = `/*#__PURE__*/`;
    const aliasHelper = (s) => `${helperNameMap[s]}: _${helperNameMap[s]}`;
    function createCodegenContext(ast, { mode = 'function', prefixIdentifiers = mode === 'module', sourceMap = false, filename = `template.vue.html`, scopeId = null, optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, ssrRuntimeModuleName = 'vue/server-renderer', ssr = false, isTS = false, inSSR = false }) {
        const context = {
            mode,
            prefixIdentifiers,
            sourceMap,
            filename,
            scopeId,
            optimizeImports,
            runtimeGlobalName,
            runtimeModuleName,
            ssrRuntimeModuleName,
            ssr,
            isTS,
            inSSR,
            source: ast.loc.source,
            code: ``,
            column: 1,
            line: 1,
            offset: 0,
            indentLevel: 0,
            pure: false,
            map: undefined,
            helper(key) {
                return `_${helperNameMap[key]}`;
            },
            push(code, node) {
                context.code += code;
            },
            indent() {
                newline(++context.indentLevel);
            },
            deindent(withoutNewLine = false) {
                if (withoutNewLine) {
                    --context.indentLevel;
                }
                else {
                    newline(--context.indentLevel);
                }
            },
            newline() {
                newline(context.indentLevel);
            }
        };
        function newline(n) {
            context.push('\n' + `  `.repeat(n));
        }
        return context;
    }
    function generate(ast, options = {}) {
        const context = createCodegenContext(ast, options);
        if (options.onContextCreated)
            options.onContextCreated(context);
        const { mode, push, prefixIdentifiers, indent, deindent, newline, scopeId, ssr } = context;
        const hasHelpers = ast.helpers.length > 0;
        const useWithBlock = !prefixIdentifiers && mode !== 'module';
        // preambles
        // in setup() inline mode, the preamble is generated in a sub context
        // and returned separately.
        const preambleContext = context;
        {
            genFunctionPreamble(ast, preambleContext);
        }
        // enter render function
        const functionName = ssr ? `ssrRender` : `render`;
        const args = ssr ? ['_ctx', '_push', '_parent', '_attrs'] : ['_ctx', '_cache'];
        const signature = args.join(', ');
        {
            push(`function ${functionName}(${signature}) {`);
        }
        indent();
        if (useWithBlock) {
            push(`with (_ctx) {`);
            indent();
            // function mode const declarations should be inside with block
            // also they should be renamed to avoid collision with user properties
            if (hasHelpers) {
                push(`const { ${ast.helpers.map(aliasHelper).join(', ')} } = _Vue`);
                push(`\n`);
                newline();
            }
        }
        // generate asset resolution statements
        if (ast.components.length) {
            genAssets(ast.components, 'component', context);
            if (ast.directives.length || ast.temps > 0) {
                newline();
            }
        }
        if (ast.directives.length) {
            genAssets(ast.directives, 'directive', context);
            if (ast.temps > 0) {
                newline();
            }
        }
        if (ast.temps > 0) {
            push(`let `);
            for (let i = 0; i < ast.temps; i++) {
                push(`${i > 0 ? `, ` : ``}_temp${i}`);
            }
        }
        if (ast.components.length || ast.directives.length || ast.temps) {
            push(`\n`);
            newline();
        }
        // generate the VNode tree expression
        if (!ssr) {
            push(`return `);
        }
        if (ast.codegenNode) {
            genNode(ast.codegenNode, context);
        }
        else {
            push(`null`);
        }
        if (useWithBlock) {
            deindent();
            push(`}`);
        }
        deindent();
        push(`}`);
        return {
            ast,
            code: context.code,
            preamble: ``,
            // SourceMapGenerator does have toJSON() method but it's not in the types
            map: context.map ? context.map.toJSON() : undefined
        };
    }
    function genFunctionPreamble(ast, context) {
        const { ssr, prefixIdentifiers, push, newline, runtimeModuleName, runtimeGlobalName, ssrRuntimeModuleName } = context;
        const VueBinding = runtimeGlobalName;
        // Generate const declaration for helpers
        // In prefix mode, we place the const declaration at top so it's done
        // only once; But if we not prefixing, we place the declaration inside the
        // with block so it doesn't incur the `in` check cost for every helper access.
        if (ast.helpers.length > 0) {
            {
                // "with" mode.
                // save Vue in a separate variable to avoid collision
                push(`const _Vue = ${VueBinding}\n`);
                // in "with" mode, helpers are declared inside the with block to avoid
                // has check cost, but hoists are lifted out of the function - we need
                // to provide the helper here.
                if (ast.hoists.length) {
                    const staticHelpers = [
                        CREATE_VNODE,
                        CREATE_ELEMENT_VNODE,
                        CREATE_COMMENT,
                        CREATE_TEXT,
                        CREATE_STATIC
                    ]
                        .filter(helper => ast.helpers.includes(helper))
                        .map(aliasHelper)
                        .join(', ');
                    push(`const { ${staticHelpers} } = _Vue\n`);
                }
            }
        }
        genHoists(ast.hoists, context);
        newline();
        push(`return `);
    }
    function genAssets(assets, type, { helper, push, newline, isTS }) {
        const resolver = helper(type === 'component'
                ? RESOLVE_COMPONENT
                : RESOLVE_DIRECTIVE);
        for (let i = 0; i < assets.length; i++) {
            let id = assets[i];
            // potential component implicit self-reference inferred from SFC filename
            const maybeSelfReference = id.endsWith('__self');
            if (maybeSelfReference) {
                id = id.slice(0, -6);
            }
            push(`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${maybeSelfReference ? `, true` : ``})${isTS ? `!` : ``}`);
            if (i < assets.length - 1) {
                newline();
            }
        }
    }
    function genHoists(hoists, context) {
        if (!hoists.length) {
            return;
        }
        context.pure = true;
        const { push, newline, helper, scopeId, mode } = context;
        newline();
        for (let i = 0; i < hoists.length; i++) {
            const exp = hoists[i];
            if (exp) {
                push(`const _hoisted_${i + 1} = ${``}`);
                genNode(exp, context);
                newline();
            }
        }
        context.pure = false;
    }
    function isText$1(n) {
        return (isString(n) ||
            n.type === 4 /* SIMPLE_EXPRESSION */ ||
            n.type === 2 /* TEXT */ ||
            n.type === 5 /* INTERPOLATION */ ||
            n.type === 8 /* COMPOUND_EXPRESSION */);
    }
    function genNodeListAsArray(nodes, context) {
        const multilines = nodes.length > 3 ||
            (nodes.some(n => isArray(n) || !isText$1(n)));
        context.push(`[`);
        multilines && context.indent();
        genNodeList(nodes, context, multilines);
        multilines && context.deindent();
        context.push(`]`);
    }
    function genNodeList(nodes, context, multilines = false, comma = true) {
        const { push, newline } = context;
        for (let i = 0; i < nodes.length; i++) {
            const node = nodes[i];
            if (isString(node)) {
                push(node);
            }
            else if (isArray(node)) {
                genNodeListAsArray(node, context);
            }
            else {
                genNode(node, context);
            }
            if (i < nodes.length - 1) {
                if (multilines) {
                    comma && push(',');
                    newline();
                }
                else {
                    comma && push(', ');
                }
            }
        }
    }
    function genNode(node, context) {
        if (isString(node)) {
            context.push(node);
            return;
        }
        if (isSymbol(node)) {
            context.push(context.helper(node));
            return;
        }
        switch (node.type) {
            case 1 /* ELEMENT */:
            case 9 /* IF */:
            case 11 /* FOR */:
                assert(node.codegenNode != null, `Codegen node is missing for element/if/for node. ` +
                        `Apply appropriate transforms first.`);
                genNode(node.codegenNode, context);
                break;
            case 2 /* TEXT */:
                genText(node, context);
                break;
            case 4 /* SIMPLE_EXPRESSION */:
                genExpression(node, context);
                break;
            case 5 /* INTERPOLATION */:
                genInterpolation(node, context);
                break;
            case 12 /* TEXT_CALL */:
                genNode(node.codegenNode, context);
                break;
            case 8 /* COMPOUND_EXPRESSION */:
                genCompoundExpression(node, context);
                break;
            case 3 /* COMMENT */:
                genComment(node, context);
                break;
            case 13 /* VNODE_CALL */:
                genVNodeCall(node, context);
                break;
            case 14 /* JS_CALL_EXPRESSION */:
                genCallExpression(node, context);
                break;
            case 15 /* JS_OBJECT_EXPRESSION */:
                genObjectExpression(node, context);
                break;
            case 17 /* JS_ARRAY_EXPRESSION */:
                genArrayExpression(node, context);
                break;
            case 18 /* JS_FUNCTION_EXPRESSION */:
                genFunctionExpression(node, context);
                break;
            case 19 /* JS_CONDITIONAL_EXPRESSION */:
                genConditionalExpression(node, context);
                break;
            case 20 /* JS_CACHE_EXPRESSION */:
                genCacheExpression(node, context);
                break;
            case 21 /* JS_BLOCK_STATEMENT */:
                genNodeList(node.body, context, true, false);
                break;
            // SSR only types
            case 22 /* JS_TEMPLATE_LITERAL */:
                break;
            case 23 /* JS_IF_STATEMENT */:
                break;
            case 24 /* JS_ASSIGNMENT_EXPRESSION */:
                break;
            case 25 /* JS_SEQUENCE_EXPRESSION */:
                break;
            case 26 /* JS_RETURN_STATEMENT */:
                break;
            /* istanbul ignore next */
            case 10 /* IF_BRANCH */:
                // noop
                break;
            default:
                {
                    assert(false, `unhandled codegen node type: ${node.type}`);
                    // make sure we exhaust all possible types
                    const exhaustiveCheck = node;
                    return exhaustiveCheck;
                }
        }
    }
    function genText(node, context) {
        context.push(JSON.stringify(node.content), node);
    }
    function genExpression(node, context) {
        const { content, isStatic } = node;
        context.push(isStatic ? JSON.stringify(content) : content, node);
    }
    function genInterpolation(node, context) {
        const { push, helper, pure } = context;
        if (pure)
            push(PURE_ANNOTATION);
        push(`${helper(TO_DISPLAY_STRING)}(`);
        genNode(node.content, context);
        push(`)`);
    }
    function genCompoundExpression(node, context) {
        for (let i = 0; i < node.children.length; i++) {
            const child = node.children[i];
            if (isString(child)) {
                context.push(child);
            }
            else {
                genNode(child, context);
            }
        }
    }
    function genExpressionAsPropertyKey(node, context) {
        const { push } = context;
        if (node.type === 8 /* COMPOUND_EXPRESSION */) {
            push(`[`);
            genCompoundExpression(node, context);
            push(`]`);
        }
        else if (node.isStatic) {
            // only quote keys if necessary
            const text = isSimpleIdentifier(node.content)
                ? node.content
                : JSON.stringify(node.content);
            push(text, node);
        }
        else {
            push(`[${node.content}]`, node);
        }
    }
    function genComment(node, context) {
        const { push, helper, pure } = context;
        if (pure) {
            push(PURE_ANNOTATION);
        }
        push(`${helper(CREATE_COMMENT)}(${JSON.stringify(node.content)})`, node);
    }
    function genVNodeCall(node, context) {
        const { push, helper, pure } = context;
        const { tag, props, children, patchFlag, dynamicProps, directives, isBlock, disableTracking, isComponent } = node;
        if (directives) {
            push(helper(WITH_DIRECTIVES) + `(`);
        }
        if (isBlock) {
            push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `);
        }
        if (pure) {
            push(PURE_ANNOTATION);
        }
        const callHelper = isBlock
            ? getVNodeBlockHelper(context.inSSR, isComponent)
            : getVNodeHelper(context.inSSR, isComponent);
        push(helper(callHelper) + `(`, node);
        genNodeList(genNullableArgs([tag, props, children, patchFlag, dynamicProps]), context);
        push(`)`);
        if (isBlock) {
            push(`)`);
        }
        if (directives) {
            push(`, `);
            genNode(directives, context);
            push(`)`);
        }
    }
    function genNullableArgs(args) {
        let i = args.length;
        while (i--) {
            if (args[i] != null)
                break;
        }
        return args.slice(0, i + 1).map(arg => arg || `null`);
    }
    // JavaScript
    function genCallExpression(node, context) {
        const { push, helper, pure } = context;
        const callee = isString(node.callee) ? node.callee : helper(node.callee);
        if (pure) {
            push(PURE_ANNOTATION);
        }
        push(callee + `(`, node);
        genNodeList(node.arguments, context);
        push(`)`);
    }
    function genObjectExpression(node, context) {
        const { push, indent, deindent, newline } = context;
        const { properties } = node;
        if (!properties.length) {
            push(`{}`, node);
            return;
        }
        const multilines = properties.length > 1 ||
            (properties.some(p => p.value.type !== 4 /* SIMPLE_EXPRESSION */));
        push(multilines ? `{` : `{ `);
        multilines && indent();
        for (let i = 0; i < properties.length; i++) {
            const { key, value } = properties[i];
            // key
            genExpressionAsPropertyKey(key, context);
            push(`: `);
            // value
            genNode(value, context);
            if (i < properties.length - 1) {
                // will only reach this if it's multilines
                push(`,`);
                newline();
            }
        }
        multilines && deindent();
        push(multilines ? `}` : ` }`);
    }
    function genArrayExpression(node, context) {
        genNodeListAsArray(node.elements, context);
    }
    function genFunctionExpression(node, context) {
        const { push, indent, deindent } = context;
        const { params, returns, body, newline, isSlot } = node;
        if (isSlot) {
            // wrap slot functions with owner context
            push(`_${helperNameMap[WITH_CTX]}(`);
        }
        push(`(`, node);
        if (isArray(params)) {
            genNodeList(params, context);
        }
        else if (params) {
            genNode(params, context);
        }
        push(`) => `);
        if (newline || body) {
            push(`{`);
            indent();
        }
        if (returns) {
            if (newline) {
                push(`return `);
            }
            if (isArray(returns)) {
                genNodeListAsArray(returns, context);
            }
            else {
                genNode(returns, context);
            }
        }
        else if (body) {
            genNode(body, context);
        }
        if (newline || body) {
            deindent();
            push(`}`);
        }
        if (isSlot) {
            push(`)`);
        }
    }
    function genConditionalExpression(node, context) {
        const { test, consequent, alternate, newline: needNewline } = node;
        const { push, indent, deindent, newline } = context;
        if (test.type === 4 /* SIMPLE_EXPRESSION */) {
            const needsParens = !isSimpleIdentifier(test.content);
            needsParens && push(`(`);
            genExpression(test, context);
            needsParens && push(`)`);
        }
        else {
            push(`(`);
            genNode(test, context);
            push(`)`);
        }
        needNewline && indent();
        context.indentLevel++;
        needNewline || push(` `);
        push(`? `);
        genNode(consequent, context);
        context.indentLevel--;
        needNewline && newline();
        needNewline || push(` `);
        push(`: `);
        const isNested = alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */;
        if (!isNested) {
            context.indentLevel++;
        }
        genNode(alternate, context);
        if (!isNested) {
            context.indentLevel--;
        }
        needNewline && deindent(true /* without newline */);
    }
    function genCacheExpression(node, context) {
        const { push, helper, indent, deindent, newline } = context;
        push(`_cache[${node.index}] || (`);
        if (node.isVNode) {
            indent();
            push(`${helper(SET_BLOCK_TRACKING)}(-1),`);
            newline();
        }
        push(`_cache[${node.index}] = `);
        genNode(node.value, context);
        if (node.isVNode) {
            push(`,`);
            newline();
            push(`${helper(SET_BLOCK_TRACKING)}(1),`);
            newline();
            push(`_cache[${node.index}]`);
            deindent();
        }
        push(`)`);
    }

    // these keywords should not appear inside expressions, but operators like
    // typeof, instanceof and in are allowed
    const prohibitedKeywordRE = new RegExp('\\b' +
        ('do,if,for,let,new,try,var,case,else,with,await,break,catch,class,const,' +
            'super,throw,while,yield,delete,export,import,return,switch,default,' +
            'extends,finally,continue,debugger,function,arguments,typeof,void')
            .split(',')
            .join('\\b|\\b') +
        '\\b');
    // strip strings in expressions
    const stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g;
    /**
     * Validate a non-prefixed expression.
     * This is only called when using the in-browser runtime compiler since it
     * doesn't prefix expressions.
     */
    function validateBrowserExpression(node, context, asParams = false, asRawStatements = false) {
        const exp = node.content;
        // empty expressions are validated per-directive since some directives
        // do allow empty expressions.
        if (!exp.trim()) {
            return;
        }
        try {
            new Function(asRawStatements
                ? ` ${exp} `
                : `return ${asParams ? `(${exp}) => {}` : `(${exp})`}`);
        }
        catch (e) {
            let message = e.message;
            const keywordMatch = exp
                .replace(stripStringRE, '')
                .match(prohibitedKeywordRE);
            if (keywordMatch) {
                message = `avoid using JavaScript keyword as property name: "${keywordMatch[0]}"`;
            }
            context.onError(createCompilerError(44 /* X_INVALID_EXPRESSION */, node.loc, undefined, message));
        }
    }

    const transformExpression = (node, context) => {
        if (node.type === 5 /* INTERPOLATION */) {
            node.content = processExpression(node.content, context);
        }
        else if (node.type === 1 /* ELEMENT */) {
            // handle directives on element
            for (let i = 0; i < node.props.length; i++) {
                const dir = node.props[i];
                // do not process for v-on & v-for since they are special handled
                if (dir.type === 7 /* DIRECTIVE */ && dir.name !== 'for') {
                    const exp = dir.exp;
                    const arg = dir.arg;
                    // do not process exp if this is v-on:arg - we need special handling
                    // for wrapping inline statements.
                    if (exp &&
                        exp.type === 4 /* SIMPLE_EXPRESSION */ &&
                        !(dir.name === 'on' && arg)) {
                        dir.exp = processExpression(exp, context, 
                        // slot args must be processed as function params
                        dir.name === 'slot');
                    }
                    if (arg && arg.type === 4 /* SIMPLE_EXPRESSION */ && !arg.isStatic) {
                        dir.arg = processExpression(arg, context);
                    }
                }
            }
        }
    };
    // Important: since this function uses Node.js only dependencies, it should
    // always be used with a leading !true check so that it can be
    // tree-shaken from the browser build.
    function processExpression(node, context, 
    // some expressions like v-slot props & v-for aliases should be parsed as
    // function params
    asParams = false, 
    // v-on handler values may contain multiple statements
    asRawStatements = false, localVars = Object.create(context.identifiers)) {
        {
            {
                // simple in-browser validation (same logic in 2.x)
                validateBrowserExpression(node, context, asParams, asRawStatements);
            }
            return node;
        }
    }

    const transformIf = createStructuralDirectiveTransform(/^(if|else|else-if)$/, (node, dir, context) => {
        return processIf(node, dir, context, (ifNode, branch, isRoot) => {
            // #1587: We need to dynamically increment the key based on the current
            // node's sibling nodes, since chained v-if/else branches are
            // rendered at the same depth
            const siblings = context.parent.children;
            let i = siblings.indexOf(ifNode);
            let key = 0;
            while (i-- >= 0) {
                const sibling = siblings[i];
                if (sibling && sibling.type === 9 /* IF */) {
                    key += sibling.branches.length;
                }
            }
            // Exit callback. Complete the codegenNode when all children have been
            // transformed.
            return () => {
                if (isRoot) {
                    ifNode.codegenNode = createCodegenNodeForBranch(branch, key, context);
                }
                else {
                    // attach this branch's codegen node to the v-if root.
                    const parentCondition = getParentCondition(ifNode.codegenNode);
                    parentCondition.alternate = createCodegenNodeForBranch(branch, key + ifNode.branches.length - 1, context);
                }
            };
        });
    });
    // target-agnostic transform used for both Client and SSR
    function processIf(node, dir, context, processCodegen) {
        if (dir.name !== 'else' &&
            (!dir.exp || !dir.exp.content.trim())) {
            const loc = dir.exp ? dir.exp.loc : node.loc;
            context.onError(createCompilerError(28 /* X_V_IF_NO_EXPRESSION */, dir.loc));
            dir.exp = createSimpleExpression(`true`, false, loc);
        }
        if (dir.exp) {
            validateBrowserExpression(dir.exp, context);
        }
        if (dir.name === 'if') {
            const branch = createIfBranch(node, dir);
            const ifNode = {
                type: 9 /* IF */,
                loc: node.loc,
                branches: [branch]
            };
            context.replaceNode(ifNode);
            if (processCodegen) {
                return processCodegen(ifNode, branch, true);
            }
        }
        else {
            // locate the adjacent v-if
            const siblings = context.parent.children;
            const comments = [];
            let i = siblings.indexOf(node);
            while (i-- >= -1) {
                const sibling = siblings[i];
                if (sibling && sibling.type === 3 /* COMMENT */) {
                    context.removeNode(sibling);
                    comments.unshift(sibling);
                    continue;
                }
                if (sibling &&
                    sibling.type === 2 /* TEXT */ &&
                    !sibling.content.trim().length) {
                    context.removeNode(sibling);
                    continue;
                }
                if (sibling && sibling.type === 9 /* IF */) {
                    // Check if v-else was followed by v-else-if
                    if (dir.name === 'else-if' &&
                        sibling.branches[sibling.branches.length - 1].condition === undefined) {
                        context.onError(createCompilerError(30 /* X_V_ELSE_NO_ADJACENT_IF */, node.loc));
                    }
                    // move the node to the if node's branches
                    context.removeNode();
                    const branch = createIfBranch(node, dir);
                    if (comments.length &&
                        // #3619 ignore comments if the v-if is direct child of <transition>
                        !(context.parent &&
                            context.parent.type === 1 /* ELEMENT */ &&
                            isBuiltInType(context.parent.tag, 'transition'))) {
                        branch.children = [...comments, ...branch.children];
                    }
                    // check if user is forcing same key on different branches
                    {
                        const key = branch.userKey;
                        if (key) {
                            sibling.branches.forEach(({ userKey }) => {
                                if (isSameKey(userKey, key)) {
                                    context.onError(createCompilerError(29 /* X_V_IF_SAME_KEY */, branch.userKey.loc));
                                }
                            });
                        }
                    }
                    sibling.branches.push(branch);
                    const onExit = processCodegen && processCodegen(sibling, branch, false);
                    // since the branch was removed, it will not be traversed.
                    // make sure to traverse here.
                    traverseNode(branch, context);
                    // call on exit
                    if (onExit)
                        onExit();
                    // make sure to reset currentNode after traversal to indicate this
                    // node has been removed.
                    context.currentNode = null;
                }
                else {
                    context.onError(createCompilerError(30 /* X_V_ELSE_NO_ADJACENT_IF */, node.loc));
                }
                break;
            }
        }
    }
    function createIfBranch(node, dir) {
        const isTemplateIf = node.tagType === 3 /* TEMPLATE */;
        return {
            type: 10 /* IF_BRANCH */,
            loc: node.loc,
            condition: dir.name === 'else' ? undefined : dir.exp,
            children: isTemplateIf && !findDir(node, 'for') ? node.children : [node],
            userKey: findProp(node, `key`),
            isTemplateIf
        };
    }
    function createCodegenNodeForBranch(branch, keyIndex, context) {
        if (branch.condition) {
            return createConditionalExpression(branch.condition, createChildrenCodegenNode(branch, keyIndex, context), 
            // make sure to pass in asBlock: true so that the comment node call
            // closes the current block.
            createCallExpression(context.helper(CREATE_COMMENT), [
                '"v-if"' ,
                'true'
            ]));
        }
        else {
            return createChildrenCodegenNode(branch, keyIndex, context);
        }
    }
    function createChildrenCodegenNode(branch, keyIndex, context) {
        const { helper } = context;
        const keyProperty = createObjectProperty(`key`, createSimpleExpression(`${keyIndex}`, false, locStub, 2 /* CAN_HOIST */));
        const { children } = branch;
        const firstChild = children[0];
        const needFragmentWrapper = children.length !== 1 || firstChild.type !== 1 /* ELEMENT */;
        if (needFragmentWrapper) {
            if (children.length === 1 && firstChild.type === 11 /* FOR */) {
                // optimize away nested fragments when child is a ForNode
                const vnodeCall = firstChild.codegenNode;
                injectProp(vnodeCall, keyProperty, context);
                return vnodeCall;
            }
            else {
                let patchFlag = 64 /* STABLE_FRAGMENT */;
                let patchFlagText = PatchFlagNames[64 /* STABLE_FRAGMENT */];
                // check if the fragment actually contains a single valid child with
                // the rest being comments
                if (!branch.isTemplateIf &&
                    children.filter(c => c.type !== 3 /* COMMENT */).length === 1) {
                    patchFlag |= 2048 /* DEV_ROOT_FRAGMENT */;
                    patchFlagText += `, ${PatchFlagNames[2048 /* DEV_ROOT_FRAGMENT */]}`;
                }
                return createVNodeCall(context, helper(FRAGMENT), createObjectExpression([keyProperty]), children, patchFlag + (` /* ${patchFlagText} */` ), undefined, undefined, true, false, false /* isComponent */, branch.loc);
            }
        }
        else {
            const ret = firstChild.codegenNode;
            const vnodeCall = getMemoedVNodeCall(ret);
            // Change createVNode to createBlock.
            if (vnodeCall.type === 13 /* VNODE_CALL */) {
                makeBlock(vnodeCall, context);
            }
            // inject branch key
            injectProp(vnodeCall, keyProperty, context);
            return ret;
        }
    }
    function isSameKey(a, b) {
        if (!a || a.type !== b.type) {
            return false;
        }
        if (a.type === 6 /* ATTRIBUTE */) {
            if (a.value.content !== b.value.content) {
                return false;
            }
        }
        else {
            // directive
            const exp = a.exp;
            const branchExp = b.exp;
            if (exp.type !== branchExp.type) {
                return false;
            }
            if (exp.type !== 4 /* SIMPLE_EXPRESSION */ ||
                exp.isStatic !== branchExp.isStatic ||
                exp.content !== branchExp.content) {
                return false;
            }
        }
        return true;
    }
    function getParentCondition(node) {
        while (true) {
            if (node.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {
                if (node.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {
                    node = node.alternate;
                }
                else {
                    return node;
                }
            }
            else if (node.type === 20 /* JS_CACHE_EXPRESSION */) {
                node = node.value;
            }
        }
    }

    const transformFor = createStructuralDirectiveTransform('for', (node, dir, context) => {
        const { helper, removeHelper } = context;
        return processFor(node, dir, context, forNode => {
            // create the loop render function expression now, and add the
            // iterator on exit after all children have been traversed
            const renderExp = createCallExpression(helper(RENDER_LIST), [
                forNode.source
            ]);
            const isTemplate = isTemplateNode(node);
            const memo = findDir(node, 'memo');
            const keyProp = findProp(node, `key`);
            const keyExp = keyProp &&
                (keyProp.type === 6 /* ATTRIBUTE */
                    ? createSimpleExpression(keyProp.value.content, true)
                    : keyProp.exp);
            const keyProperty = keyProp ? createObjectProperty(`key`, keyExp) : null;
            const isStableFragment = forNode.source.type === 4 /* SIMPLE_EXPRESSION */ &&
                forNode.source.constType > 0 /* NOT_CONSTANT */;
            const fragmentFlag = isStableFragment
                ? 64 /* STABLE_FRAGMENT */
                : keyProp
                    ? 128 /* KEYED_FRAGMENT */
                    : 256 /* UNKEYED_FRAGMENT */;
            forNode.codegenNode = createVNodeCall(context, helper(FRAGMENT), undefined, renderExp, fragmentFlag +
                (` /* ${PatchFlagNames[fragmentFlag]} */` ), undefined, undefined, true /* isBlock */, !isStableFragment /* disableTracking */, false /* isComponent */, node.loc);
            return () => {
                // finish the codegen now that all children have been traversed
                let childBlock;
                const { children } = forNode;
                // check <template v-for> key placement
                if (isTemplate) {
                    node.children.some(c => {
                        if (c.type === 1 /* ELEMENT */) {
                            const key = findProp(c, 'key');
                            if (key) {
                                context.onError(createCompilerError(33 /* X_V_FOR_TEMPLATE_KEY_PLACEMENT */, key.loc));
                                return true;
                            }
                        }
                    });
                }
                const needFragmentWrapper = children.length !== 1 || children[0].type !== 1 /* ELEMENT */;
                const slotOutlet = isSlotOutlet(node)
                    ? node
                    : isTemplate &&
                        node.children.length === 1 &&
                        isSlotOutlet(node.children[0])
                        ? node.children[0] // api-extractor somehow fails to infer this
                        : null;
                if (slotOutlet) {
                    // <slot v-for="..."> or <template v-for="..."><slot/></template>
                    childBlock = slotOutlet.codegenNode;
                    if (isTemplate && keyProperty) {
                        // <template v-for="..." :key="..."><slot/></template>
                        // we need to inject the key to the renderSlot() call.
                        // the props for renderSlot is passed as the 3rd argument.
                        injectProp(childBlock, keyProperty, context);
                    }
                }
                else if (needFragmentWrapper) {
                    // <template v-for="..."> with text or multi-elements
                    // should generate a fragment block for each loop
                    childBlock = createVNodeCall(context, helper(FRAGMENT), keyProperty ? createObjectExpression([keyProperty]) : undefined, node.children, 64 /* STABLE_FRAGMENT */ +
                        (` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`
                            ), undefined, undefined, true, undefined, false /* isComponent */);
                }
                else {
                    // Normal element v-for. Directly use the child's codegenNode
                    // but mark it as a block.
                    childBlock = children[0]
                        .codegenNode;
                    if (isTemplate && keyProperty) {
                        injectProp(childBlock, keyProperty, context);
                    }
                    if (childBlock.isBlock !== !isStableFragment) {
                        if (childBlock.isBlock) {
                            // switch from block to vnode
                            removeHelper(OPEN_BLOCK);
                            removeHelper(getVNodeBlockHelper(context.inSSR, childBlock.isComponent));
                        }
                        else {
                            // switch from vnode to block
                            removeHelper(getVNodeHelper(context.inSSR, childBlock.isComponent));
                        }
                    }
                    childBlock.isBlock = !isStableFragment;
                    if (childBlock.isBlock) {
                        helper(OPEN_BLOCK);
                        helper(getVNodeBlockHelper(context.inSSR, childBlock.isComponent));
                    }
                    else {
                        helper(getVNodeHelper(context.inSSR, childBlock.isComponent));
                    }
                }
                if (memo) {
                    const loop = createFunctionExpression(createForLoopParams(forNode.parseResult, [
                        createSimpleExpression(`_cached`)
                    ]));
                    loop.body = createBlockStatement([
                        createCompoundExpression([`const _memo = (`, memo.exp, `)`]),
                        createCompoundExpression([
                            `if (_cached`,
                            ...(keyExp ? [` && _cached.key === `, keyExp] : []),
                            ` && ${context.helperString(IS_MEMO_SAME)}(_cached, _memo)) return _cached`
                        ]),
                        createCompoundExpression([`const _item = `, childBlock]),
                        createSimpleExpression(`_item.memo = _memo`),
                        createSimpleExpression(`return _item`)
                    ]);
                    renderExp.arguments.push(loop, createSimpleExpression(`_cache`), createSimpleExpression(String(context.cached++)));
                }
                else {
                    renderExp.arguments.push(createFunctionExpression(createForLoopParams(forNode.parseResult), childBlock, true /* force newline */));
                }
            };
        });
    });
    // target-agnostic transform used for both Client and SSR
    function processFor(node, dir, context, processCodegen) {
        if (!dir.exp) {
            context.onError(createCompilerError(31 /* X_V_FOR_NO_EXPRESSION */, dir.loc));
            return;
        }
        const parseResult = parseForExpression(
        // can only be simple expression because vFor transform is applied
        // before expression transform.
        dir.exp, context);
        if (!parseResult) {
            context.onError(createCompilerError(32 /* X_V_FOR_MALFORMED_EXPRESSION */, dir.loc));
            return;
        }
        const { addIdentifiers, removeIdentifiers, scopes } = context;
        const { source, value, key, index } = parseResult;
        const forNode = {
            type: 11 /* FOR */,
            loc: dir.loc,
            source,
            valueAlias: value,
            keyAlias: key,
            objectIndexAlias: index,
            parseResult,
            children: isTemplateNode(node) ? node.children : [node]
        };
        context.replaceNode(forNode);
        // bookkeeping
        scopes.vFor++;
        const onExit = processCodegen && processCodegen(forNode);
        return () => {
            scopes.vFor--;
            if (onExit)
                onExit();
        };
    }
    const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;
    // This regex doesn't cover the case if key or index aliases have destructuring,
    // but those do not make sense in the first place, so this works in practice.
    const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;
    const stripParensRE = /^\(|\)$/g;
    function parseForExpression(input, context) {
        const loc = input.loc;
        const exp = input.content;
        const inMatch = exp.match(forAliasRE);
        if (!inMatch)
            return;
        const [, LHS, RHS] = inMatch;
        const result = {
            source: createAliasExpression(loc, RHS.trim(), exp.indexOf(RHS, LHS.length)),
            value: undefined,
            key: undefined,
            index: undefined
        };
        {
            validateBrowserExpression(result.source, context);
        }
        let valueContent = LHS.trim().replace(stripParensRE, '').trim();
        const trimmedOffset = LHS.indexOf(valueContent);
        const iteratorMatch = valueContent.match(forIteratorRE);
        if (iteratorMatch) {
            valueContent = valueContent.replace(forIteratorRE, '').trim();
            const keyContent = iteratorMatch[1].trim();
            let keyOffset;
            if (keyContent) {
                keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length);
                result.key = createAliasExpression(loc, keyContent, keyOffset);
                {
                    validateBrowserExpression(result.key, context, true);
                }
            }
            if (iteratorMatch[2]) {
                const indexContent = iteratorMatch[2].trim();
                if (indexContent) {
                    result.index = createAliasExpression(loc, indexContent, exp.indexOf(indexContent, result.key
                        ? keyOffset + keyContent.length
                        : trimmedOffset + valueContent.length));
                    {
                        validateBrowserExpression(result.index, context, true);
                    }
                }
            }
        }
        if (valueContent) {
            result.value = createAliasExpression(loc, valueContent, trimmedOffset);
            {
                validateBrowserExpression(result.value, context, true);
            }
        }
        return result;
    }
    function createAliasExpression(range, content, offset) {
        return createSimpleExpression(content, false, getInnerRange(range, offset, content.length));
    }
    function createForLoopParams({ value, key, index }, memoArgs = []) {
        return createParamsList([value, key, index, ...memoArgs]);
    }
    function createParamsList(args) {
        let i = args.length;
        while (i--) {
            if (args[i])
                break;
        }
        return args
            .slice(0, i + 1)
            .map((arg, i) => arg || createSimpleExpression(`_`.repeat(i + 1), false));
    }

    const defaultFallback = createSimpleExpression(`undefined`, false);
    // A NodeTransform that:
    // 1. Tracks scope identifiers for scoped slots so that they don't get prefixed
    //    by transformExpression. This is only applied in non-browser builds with
    //    { prefixIdentifiers: true }.
    // 2. Track v-slot depths so that we know a slot is inside another slot.
    //    Note the exit callback is executed before buildSlots() on the same node,
    //    so only nested slots see positive numbers.
    const trackSlotScopes = (node, context) => {
        if (node.type === 1 /* ELEMENT */ &&
            (node.tagType === 1 /* COMPONENT */ ||
                node.tagType === 3 /* TEMPLATE */)) {
            // We are only checking non-empty v-slot here
            // since we only care about slots that introduce scope variables.
            const vSlot = findDir(node, 'slot');
            if (vSlot) {
                vSlot.exp;
                context.scopes.vSlot++;
                return () => {
                    context.scopes.vSlot--;
                };
            }
        }
    };
    const buildClientSlotFn = (props, children, loc) => createFunctionExpression(props, children, false /* newline */, true /* isSlot */, children.length ? children[0].loc : loc);
    // Instead of being a DirectiveTransform, v-slot processing is called during
    // transformElement to build the slots object for a component.
    function buildSlots(node, context, buildSlotFn = buildClientSlotFn) {
        context.helper(WITH_CTX);
        const { children, loc } = node;
        const slotsProperties = [];
        const dynamicSlots = [];
        // If the slot is inside a v-for or another v-slot, force it to be dynamic
        // since it likely uses a scope variable.
        let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0;
        // 1. Check for slot with slotProps on component itself.
        //    <Comp v-slot="{ prop }"/>
        const onComponentSlot = findDir(node, 'slot', true);
        if (onComponentSlot) {
            const { arg, exp } = onComponentSlot;
            if (arg && !isStaticExp(arg)) {
                hasDynamicSlots = true;
            }
            slotsProperties.push(createObjectProperty(arg || createSimpleExpression('default', true), buildSlotFn(exp, children, loc)));
        }
        // 2. Iterate through children and check for template slots
        //    <template v-slot:foo="{ prop }">
        let hasTemplateSlots = false;
        let hasNamedDefaultSlot = false;
        const implicitDefaultChildren = [];
        const seenSlotNames = new Set();
        for (let i = 0; i < children.length; i++) {
            const slotElement = children[i];
            let slotDir;
            if (!isTemplateNode(slotElement) ||
                !(slotDir = findDir(slotElement, 'slot', true))) {
                // not a <template v-slot>, skip.
                if (slotElement.type !== 3 /* COMMENT */) {
                    implicitDefaultChildren.push(slotElement);
                }
                continue;
            }
            if (onComponentSlot) {
                // already has on-component slot - this is incorrect usage.
                context.onError(createCompilerError(37 /* X_V_SLOT_MIXED_SLOT_USAGE */, slotDir.loc));
                break;
            }
            hasTemplateSlots = true;
            const { children: slotChildren, loc: slotLoc } = slotElement;
            const { arg: slotName = createSimpleExpression(`default`, true), exp: slotProps, loc: dirLoc } = slotDir;
            // check if name is dynamic.
            let staticSlotName;
            if (isStaticExp(slotName)) {
                staticSlotName = slotName ? slotName.content : `default`;
            }
            else {
                hasDynamicSlots = true;
            }
            const slotFunction = buildSlotFn(slotProps, slotChildren, slotLoc);
            // check if this slot is conditional (v-if/v-for)
            let vIf;
            let vElse;
            let vFor;
            if ((vIf = findDir(slotElement, 'if'))) {
                hasDynamicSlots = true;
                dynamicSlots.push(createConditionalExpression(vIf.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback));
            }
            else if ((vElse = findDir(slotElement, /^else(-if)?$/, true /* allowEmpty */))) {
                // find adjacent v-if
                let j = i;
                let prev;
                while (j--) {
                    prev = children[j];
                    if (prev.type !== 3 /* COMMENT */) {
                        break;
                    }
                }
                if (prev && isTemplateNode(prev) && findDir(prev, 'if')) {
                    // remove node
                    children.splice(i, 1);
                    i--;
                    // attach this slot to previous conditional
                    let conditional = dynamicSlots[dynamicSlots.length - 1];
                    while (conditional.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {
                        conditional = conditional.alternate;
                    }
                    conditional.alternate = vElse.exp
                        ? createConditionalExpression(vElse.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback)
                        : buildDynamicSlot(slotName, slotFunction);
                }
                else {
                    context.onError(createCompilerError(30 /* X_V_ELSE_NO_ADJACENT_IF */, vElse.loc));
                }
            }
            else if ((vFor = findDir(slotElement, 'for'))) {
                hasDynamicSlots = true;
                const parseResult = vFor.parseResult ||
                    parseForExpression(vFor.exp, context);
                if (parseResult) {
                    // Render the dynamic slots as an array and add it to the createSlot()
                    // args. The runtime knows how to handle it appropriately.
                    dynamicSlots.push(createCallExpression(context.helper(RENDER_LIST), [
                        parseResult.source,
                        createFunctionExpression(createForLoopParams(parseResult), buildDynamicSlot(slotName, slotFunction), true /* force newline */)
                    ]));
                }
                else {
                    context.onError(createCompilerError(32 /* X_V_FOR_MALFORMED_EXPRESSION */, vFor.loc));
                }
            }
            else {
                // check duplicate static names
                if (staticSlotName) {
                    if (seenSlotNames.has(staticSlotName)) {
                        context.onError(createCompilerError(38 /* X_V_SLOT_DUPLICATE_SLOT_NAMES */, dirLoc));
                        continue;
                    }
                    seenSlotNames.add(staticSlotName);
                    if (staticSlotName === 'default') {
                        hasNamedDefaultSlot = true;
                    }
                }
                slotsProperties.push(createObjectProperty(slotName, slotFunction));
            }
        }
        if (!onComponentSlot) {
            const buildDefaultSlotProperty = (props, children) => {
                const fn = buildSlotFn(props, children, loc);
                return createObjectProperty(`default`, fn);
            };
            if (!hasTemplateSlots) {
                // implicit default slot (on component)
                slotsProperties.push(buildDefaultSlotProperty(undefined, children));
            }
            else if (implicitDefaultChildren.length &&
                // #3766
                // with whitespace: 'preserve', whitespaces between slots will end up in
                // implicitDefaultChildren. Ignore if all implicit children are whitespaces.
                implicitDefaultChildren.some(node => isNonWhitespaceContent(node))) {
                // implicit default slot (mixed with named slots)
                if (hasNamedDefaultSlot) {
                    context.onError(createCompilerError(39 /* X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN */, implicitDefaultChildren[0].loc));
                }
                else {
                    slotsProperties.push(buildDefaultSlotProperty(undefined, implicitDefaultChildren));
                }
            }
        }
        const slotFlag = hasDynamicSlots
            ? 2 /* DYNAMIC */
            : hasForwardedSlots(node.children)
                ? 3 /* FORWARDED */
                : 1 /* STABLE */;
        let slots = createObjectExpression(slotsProperties.concat(createObjectProperty(`_`, 
        // 2 = compiled but dynamic = can skip normalization, but must run diff
        // 1 = compiled and static = can skip normalization AND diff as optimized
        createSimpleExpression(slotFlag + (` /* ${slotFlagsText[slotFlag]} */` ), false))), loc);
        if (dynamicSlots.length) {
            slots = createCallExpression(context.helper(CREATE_SLOTS), [
                slots,
                createArrayExpression(dynamicSlots)
            ]);
        }
        return {
            slots,
            hasDynamicSlots
        };
    }
    function buildDynamicSlot(name, fn) {
        return createObjectExpression([
            createObjectProperty(`name`, name),
            createObjectProperty(`fn`, fn)
        ]);
    }
    function hasForwardedSlots(children) {
        for (let i = 0; i < children.length; i++) {
            const child = children[i];
            switch (child.type) {
                case 1 /* ELEMENT */:
                    if (child.tagType === 2 /* SLOT */ ||
                        hasForwardedSlots(child.children)) {
                        return true;
                    }
                    break;
                case 9 /* IF */:
                    if (hasForwardedSlots(child.branches))
                        return true;
                    break;
                case 10 /* IF_BRANCH */:
                case 11 /* FOR */:
                    if (hasForwardedSlots(child.children))
                        return true;
                    break;
            }
        }
        return false;
    }
    function isNonWhitespaceContent(node) {
        if (node.type !== 2 /* TEXT */ && node.type !== 12 /* TEXT_CALL */)
            return true;
        return node.type === 2 /* TEXT */
            ? !!node.content.trim()
            : isNonWhitespaceContent(node.content);
    }

    // some directive transforms (e.g. v-model) may return a symbol for runtime
    // import, which should be used instead of a resolveDirective call.
    const directiveImportMap = new WeakMap();
    // generate a JavaScript AST for this element's codegen
    const transformElement = (node, context) => {
        // perform the work on exit, after all child expressions have been
        // processed and merged.
        return function postTransformElement() {
            node = context.currentNode;
            if (!(node.type === 1 /* ELEMENT */ &&
                (node.tagType === 0 /* ELEMENT */ ||
                    node.tagType === 1 /* COMPONENT */))) {
                return;
            }
            const { tag, props } = node;
            const isComponent = node.tagType === 1 /* COMPONENT */;
            // The goal of the transform is to create a codegenNode implementing the
            // VNodeCall interface.
            let vnodeTag = isComponent
                ? resolveComponentType(node, context)
                : `"${tag}"`;
            const isDynamicComponent = isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT;
            let vnodeProps;
            let vnodeChildren;
            let vnodePatchFlag;
            let patchFlag = 0;
            let vnodeDynamicProps;
            let dynamicPropNames;
            let vnodeDirectives;
            let shouldUseBlock = 
            // dynamic component may resolve to plain elements
            isDynamicComponent ||
                vnodeTag === TELEPORT ||
                vnodeTag === SUSPENSE ||
                (!isComponent &&
                    // <svg> and <foreignObject> must be forced into blocks so that block
                    // updates inside get proper isSVG flag at runtime. (#639, #643)
                    // This is technically web-specific, but splitting the logic out of core
                    // leads to too much unnecessary complexity.
                    (tag === 'svg' || tag === 'foreignObject'));
            // props
            if (props.length > 0) {
                const propsBuildResult = buildProps(node, context, undefined, isComponent, isDynamicComponent);
                vnodeProps = propsBuildResult.props;
                patchFlag = propsBuildResult.patchFlag;
                dynamicPropNames = propsBuildResult.dynamicPropNames;
                const directives = propsBuildResult.directives;
                vnodeDirectives =
                    directives && directives.length
                        ? createArrayExpression(directives.map(dir => buildDirectiveArgs(dir, context)))
                        : undefined;
                if (propsBuildResult.shouldUseBlock) {
                    shouldUseBlock = true;
                }
            }
            // children
            if (node.children.length > 0) {
                if (vnodeTag === KEEP_ALIVE) {
                    // Although a built-in component, we compile KeepAlive with raw children
                    // instead of slot functions so that it can be used inside Transition
                    // or other Transition-wrapping HOCs.
                    // To ensure correct updates with block optimizations, we need to:
                    // 1. Force keep-alive into a block. This avoids its children being
                    //    collected by a parent block.
                    shouldUseBlock = true;
                    // 2. Force keep-alive to always be updated, since it uses raw children.
                    patchFlag |= 1024 /* DYNAMIC_SLOTS */;
                    if (node.children.length > 1) {
                        context.onError(createCompilerError(45 /* X_KEEP_ALIVE_INVALID_CHILDREN */, {
                            start: node.children[0].loc.start,
                            end: node.children[node.children.length - 1].loc.end,
                            source: ''
                        }));
                    }
                }
                const shouldBuildAsSlots = isComponent &&
                    // Teleport is not a real component and has dedicated runtime handling
                    vnodeTag !== TELEPORT &&
                    // explained above.
                    vnodeTag !== KEEP_ALIVE;
                if (shouldBuildAsSlots) {
                    const { slots, hasDynamicSlots } = buildSlots(node, context);
                    vnodeChildren = slots;
                    if (hasDynamicSlots) {
                        patchFlag |= 1024 /* DYNAMIC_SLOTS */;
                    }
                }
                else if (node.children.length === 1 && vnodeTag !== TELEPORT) {
                    const child = node.children[0];
                    const type = child.type;
                    // check for dynamic text children
                    const hasDynamicTextChild = type === 5 /* INTERPOLATION */ ||
                        type === 8 /* COMPOUND_EXPRESSION */;
                    if (hasDynamicTextChild &&
                        getConstantType(child, context) === 0 /* NOT_CONSTANT */) {
                        patchFlag |= 1 /* TEXT */;
                    }
                    // pass directly if the only child is a text node
                    // (plain / interpolation / expression)
                    if (hasDynamicTextChild || type === 2 /* TEXT */) {
                        vnodeChildren = child;
                    }
                    else {
                        vnodeChildren = node.children;
                    }
                }
                else {
                    vnodeChildren = node.children;
                }
            }
            // patchFlag & dynamicPropNames
            if (patchFlag !== 0) {
                {
                    if (patchFlag < 0) {
                        // special flags (negative and mutually exclusive)
                        vnodePatchFlag = patchFlag + ` /* ${PatchFlagNames[patchFlag]} */`;
                    }
                    else {
                        // bitwise flags
                        const flagNames = Object.keys(PatchFlagNames)
                            .map(Number)
                            .filter(n => n > 0 && patchFlag & n)
                            .map(n => PatchFlagNames[n])
                            .join(`, `);
                        vnodePatchFlag = patchFlag + ` /* ${flagNames} */`;
                    }
                }
                if (dynamicPropNames && dynamicPropNames.length) {
                    vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames);
                }
            }
            node.codegenNode = createVNodeCall(context, vnodeTag, vnodeProps, vnodeChildren, vnodePatchFlag, vnodeDynamicProps, vnodeDirectives, !!shouldUseBlock, false /* disableTracking */, isComponent, node.loc);
        };
    };
    function resolveComponentType(node, context, ssr = false) {
        let { tag } = node;
        // 1. dynamic component
        const isExplicitDynamic = isComponentTag(tag);
        const isProp = findProp(node, 'is');
        if (isProp) {
            if (isExplicitDynamic ||
                (false )) {
                const exp = isProp.type === 6 /* ATTRIBUTE */
                    ? isProp.value && createSimpleExpression(isProp.value.content, true)
                    : isProp.exp;
                if (exp) {
                    return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
                        exp
                    ]);
                }
            }
            else if (isProp.type === 6 /* ATTRIBUTE */ &&
                isProp.value.content.startsWith('vue:')) {
                // <button is="vue:xxx">
                // if not <component>, only is value that starts with "vue:" will be
                // treated as component by the parse phase and reach here, unless it's
                // compat mode where all is values are considered components
                tag = isProp.value.content.slice(4);
            }
        }
        // 1.5 v-is (TODO: Deprecate)
        const isDir = !isExplicitDynamic && findDir(node, 'is');
        if (isDir && isDir.exp) {
            return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
                isDir.exp
            ]);
        }
        // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
        const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);
        if (builtIn) {
            // built-ins are simply fallthroughs / have special handling during ssr
            // so we don't need to import their runtime equivalents
            if (!ssr)
                context.helper(builtIn);
            return builtIn;
        }
        // 5. user component (resolve)
        context.helper(RESOLVE_COMPONENT);
        context.components.add(tag);
        return toValidAssetId(tag, `component`);
    }
    function buildProps(node, context, props = node.props, isComponent, isDynamicComponent, ssr = false) {
        const { tag, loc: elementLoc, children } = node;
        let properties = [];
        const mergeArgs = [];
        const runtimeDirectives = [];
        const hasChildren = children.length > 0;
        let shouldUseBlock = false;
        // patchFlag analysis
        let patchFlag = 0;
        let hasRef = false;
        let hasClassBinding = false;
        let hasStyleBinding = false;
        let hasHydrationEventBinding = false;
        let hasDynamicKeys = false;
        let hasVnodeHook = false;
        const dynamicPropNames = [];
        const analyzePatchFlag = ({ key, value }) => {
            if (isStaticExp(key)) {
                const name = key.content;
                const isEventHandler = isOn(name);
                if (isEventHandler &&
                    (!isComponent || isDynamicComponent) &&
                    // omit the flag for click handlers because hydration gives click
                    // dedicated fast path.
                    name.toLowerCase() !== 'onclick' &&
                    // omit v-model handlers
                    name !== 'onUpdate:modelValue' &&
                    // omit onVnodeXXX hooks
                    !isReservedProp(name)) {
                    hasHydrationEventBinding = true;
                }
                if (isEventHandler && isReservedProp(name)) {
                    hasVnodeHook = true;
                }
                if (value.type === 20 /* JS_CACHE_EXPRESSION */ ||
                    ((value.type === 4 /* SIMPLE_EXPRESSION */ ||
                        value.type === 8 /* COMPOUND_EXPRESSION */) &&
                        getConstantType(value, context) > 0)) {
                    // skip if the prop is a cached handler or has constant value
                    return;
                }
                if (name === 'ref') {
                    hasRef = true;
                }
                else if (name === 'class') {
                    hasClassBinding = true;
                }
                else if (name === 'style') {
                    hasStyleBinding = true;
                }
                else if (name !== 'key' && !dynamicPropNames.includes(name)) {
                    dynamicPropNames.push(name);
                }
                // treat the dynamic class and style binding of the component as dynamic props
                if (isComponent &&
                    (name === 'class' || name === 'style') &&
                    !dynamicPropNames.includes(name)) {
                    dynamicPropNames.push(name);
                }
            }
            else {
                hasDynamicKeys = true;
            }
        };
        for (let i = 0; i < props.length; i++) {
            // static attribute
            const prop = props[i];
            if (prop.type === 6 /* ATTRIBUTE */) {
                const { loc, name, value } = prop;
                let isStatic = true;
                if (name === 'ref') {
                    hasRef = true;
                    if (context.scopes.vFor > 0) {
                        properties.push(createObjectProperty(createSimpleExpression('ref_for', true), createSimpleExpression('true')));
                    }
                }
                // skip is on <component>, or is="vue:xxx"
                if (name === 'is' &&
                    (isComponentTag(tag) ||
                        (value && value.content.startsWith('vue:')) ||
                        (false ))) {
                    continue;
                }
                properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));
            }
            else {
                // directives
                const { name, arg, exp, loc } = prop;
                const isVBind = name === 'bind';
                const isVOn = name === 'on';
                // skip v-slot - it is handled by its dedicated transform.
                if (name === 'slot') {
                    if (!isComponent) {
                        context.onError(createCompilerError(40 /* X_V_SLOT_MISPLACED */, loc));
                    }
                    continue;
                }
                // skip v-once/v-memo - they are handled by dedicated transforms.
                if (name === 'once' || name === 'memo') {
                    continue;
                }
                // skip v-is and :is on <component>
                if (name === 'is' ||
                    (isVBind &&
                        isStaticArgOf(arg, 'is') &&
                        (isComponentTag(tag) ||
                            (false )))) {
                    continue;
                }
                // skip v-on in SSR compilation
                if (isVOn && ssr) {
                    continue;
                }
                if (
                // #938: elements with dynamic keys should be forced into blocks
                (isVBind && isStaticArgOf(arg, 'key')) ||
                    // inline before-update hooks need to force block so that it is invoked
                    // before children
                    (isVOn && hasChildren && isStaticArgOf(arg, 'vue:before-update'))) {
                    shouldUseBlock = true;
                }
                if (isVBind && isStaticArgOf(arg, 'ref') && context.scopes.vFor > 0) {
                    properties.push(createObjectProperty(createSimpleExpression('ref_for', true), createSimpleExpression('true')));
                }
                // special case for v-bind and v-on with no argument
                if (!arg && (isVBind || isVOn)) {
                    hasDynamicKeys = true;
                    if (exp) {
                        if (properties.length) {
                            mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));
                            properties = [];
                        }
                        if (isVBind) {
                            mergeArgs.push(exp);
                        }
                        else {
                            // v-on="obj" -> toHandlers(obj)
                            mergeArgs.push({
                                type: 14 /* JS_CALL_EXPRESSION */,
                                loc,
                                callee: context.helper(TO_HANDLERS),
                                arguments: [exp]
                            });
                        }
                    }
                    else {
                        context.onError(createCompilerError(isVBind
                            ? 34 /* X_V_BIND_NO_EXPRESSION */
                            : 35 /* X_V_ON_NO_EXPRESSION */, loc));
                    }
                    continue;
                }
                const directiveTransform = context.directiveTransforms[name];
                if (directiveTransform) {
                    // has built-in directive transform.
                    const { props, needRuntime } = directiveTransform(prop, node, context);
                    !ssr && props.forEach(analyzePatchFlag);
                    properties.push(...props);
                    if (needRuntime) {
                        runtimeDirectives.push(prop);
                        if (isSymbol(needRuntime)) {
                            directiveImportMap.set(prop, needRuntime);
                        }
                    }
                }
                else if (!isBuiltInDirective(name)) {
                    // no built-in transform, this is a user custom directive.
                    runtimeDirectives.push(prop);
                    // custom dirs may use beforeUpdate so they need to force blocks
                    // to ensure before-update gets called before children update
                    if (hasChildren) {
                        shouldUseBlock = true;
                    }
                }
            }
        }
        let propsExpression = undefined;
        // has v-bind="object" or v-on="object", wrap with mergeProps
        if (mergeArgs.length) {
            if (properties.length) {
                mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));
            }
            if (mergeArgs.length > 1) {
                propsExpression = createCallExpression(context.helper(MERGE_PROPS), mergeArgs, elementLoc);
            }
            else {
                // single v-bind with nothing else - no need for a mergeProps call
                propsExpression = mergeArgs[0];
            }
        }
        else if (properties.length) {
            propsExpression = createObjectExpression(dedupeProperties(properties), elementLoc);
        }
        // patchFlag analysis
        if (hasDynamicKeys) {
            patchFlag |= 16 /* FULL_PROPS */;
        }
        else {
            if (hasClassBinding && !isComponent) {
                patchFlag |= 2 /* CLASS */;
            }
            if (hasStyleBinding && !isComponent) {
                patchFlag |= 4 /* STYLE */;
            }
            if (dynamicPropNames.length) {
                patchFlag |= 8 /* PROPS */;
            }
            if (hasHydrationEventBinding) {
                patchFlag |= 32 /* HYDRATE_EVENTS */;
            }
        }
        if (!shouldUseBlock &&
            (patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */) &&
            (hasRef || hasVnodeHook || runtimeDirectives.length > 0)) {
            patchFlag |= 512 /* NEED_PATCH */;
        }
        // pre-normalize props, SSR is skipped for now
        if (!context.inSSR && propsExpression) {
            switch (propsExpression.type) {
                case 15 /* JS_OBJECT_EXPRESSION */:
                    // means that there is no v-bind,
                    // but still need to deal with dynamic key binding
                    let classKeyIndex = -1;
                    let styleKeyIndex = -1;
                    let hasDynamicKey = false;
                    for (let i = 0; i < propsExpression.properties.length; i++) {
                        const key = propsExpression.properties[i].key;
                        if (isStaticExp(key)) {
                            if (key.content === 'class') {
                                classKeyIndex = i;
                            }
                            else if (key.content === 'style') {
                                styleKeyIndex = i;
                            }
                        }
                        else if (!key.isHandlerKey) {
                            hasDynamicKey = true;
                        }
                    }
                    const classProp = propsExpression.properties[classKeyIndex];
                    const styleProp = propsExpression.properties[styleKeyIndex];
                    // no dynamic key
                    if (!hasDynamicKey) {
                        if (classProp && !isStaticExp(classProp.value)) {
                            classProp.value = createCallExpression(context.helper(NORMALIZE_CLASS), [classProp.value]);
                        }
                        if (styleProp &&
                            // the static style is compiled into an object,
                            // so use `hasStyleBinding` to ensure that it is a dynamic style binding
                            (hasStyleBinding ||
                                (styleProp.value.type === 4 /* SIMPLE_EXPRESSION */ &&
                                    styleProp.value.content.trim()[0] === `[`) ||
                                // v-bind:style and style both exist,
                                // v-bind:style with static literal object
                                styleProp.value.type === 17 /* JS_ARRAY_EXPRESSION */)) {
                            styleProp.value = createCallExpression(context.helper(NORMALIZE_STYLE), [styleProp.value]);
                        }
                    }
                    else {
                        // dynamic key binding, wrap with `normalizeProps`
                        propsExpression = createCallExpression(context.helper(NORMALIZE_PROPS), [propsExpression]);
                    }
                    break;
                case 14 /* JS_CALL_EXPRESSION */:
                    // mergeProps call, do nothing
                    break;
                default:
                    // single v-bind
                    propsExpression = createCallExpression(context.helper(NORMALIZE_PROPS), [
                        createCallExpression(context.helper(GUARD_REACTIVE_PROPS), [
                            propsExpression
                        ])
                    ]);
                    break;
            }
        }
        return {
            props: propsExpression,
            directives: runtimeDirectives,
            patchFlag,
            dynamicPropNames,
            shouldUseBlock
        };
    }
    // Dedupe props in an object literal.
    // Literal duplicated attributes would have been warned during the parse phase,
    // however, it's possible to encounter duplicated `onXXX` handlers with different
    // modifiers. We also need to merge static and dynamic class / style attributes.
    // - onXXX handlers / style: merge into array
    // - class: merge into single expression with concatenation
    function dedupeProperties(properties) {
        const knownProps = new Map();
        const deduped = [];
        for (let i = 0; i < properties.length; i++) {
            const prop = properties[i];
            // dynamic keys are always allowed
            if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {
                deduped.push(prop);
                continue;
            }
            const name = prop.key.content;
            const existing = knownProps.get(name);
            if (existing) {
                if (name === 'style' || name === 'class' || isOn(name)) {
                    mergeAsArray$1(existing, prop);
                }
                // unexpected duplicate, should have emitted error during parse
            }
            else {
                knownProps.set(name, prop);
                deduped.push(prop);
            }
        }
        return deduped;
    }
    function mergeAsArray$1(existing, incoming) {
        if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {
            existing.value.elements.push(incoming.value);
        }
        else {
            existing.value = createArrayExpression([existing.value, incoming.value], existing.loc);
        }
    }
    function buildDirectiveArgs(dir, context) {
        const dirArgs = [];
        const runtime = directiveImportMap.get(dir);
        if (runtime) {
            // built-in directive with runtime
            dirArgs.push(context.helperString(runtime));
        }
        else {
            {
                // inject statement for resolving directive
                context.helper(RESOLVE_DIRECTIVE);
                context.directives.add(dir.name);
                dirArgs.push(toValidAssetId(dir.name, `directive`));
            }
        }
        const { loc } = dir;
        if (dir.exp)
            dirArgs.push(dir.exp);
        if (dir.arg) {
            if (!dir.exp) {
                dirArgs.push(`void 0`);
            }
            dirArgs.push(dir.arg);
        }
        if (Object.keys(dir.modifiers).length) {
            if (!dir.arg) {
                if (!dir.exp) {
                    dirArgs.push(`void 0`);
                }
                dirArgs.push(`void 0`);
            }
            const trueExpression = createSimpleExpression(`true`, false, loc);
            dirArgs.push(createObjectExpression(dir.modifiers.map(modifier => createObjectProperty(modifier, trueExpression)), loc));
        }
        return createArrayExpression(dirArgs, dir.loc);
    }
    function stringifyDynamicPropNames(props) {
        let propsNamesString = `[`;
        for (let i = 0, l = props.length; i < l; i++) {
            propsNamesString += JSON.stringify(props[i]);
            if (i < l - 1)
                propsNamesString += ', ';
        }
        return propsNamesString + `]`;
    }
    function isComponentTag(tag) {
        return tag === 'component' || tag === 'Component';
    }

    const transformSlotOutlet = (node, context) => {
        if (isSlotOutlet(node)) {
            const { children, loc } = node;
            const { slotName, slotProps } = processSlotOutlet(node, context);
            const slotArgs = [
                context.prefixIdentifiers ? `_ctx.$slots` : `$slots`,
                slotName,
                '{}',
                'undefined',
                'true'
            ];
            let expectedLen = 2;
            if (slotProps) {
                slotArgs[2] = slotProps;
                expectedLen = 3;
            }
            if (children.length) {
                slotArgs[3] = createFunctionExpression([], children, false, false, loc);
                expectedLen = 4;
            }
            if (context.scopeId && !context.slotted) {
                expectedLen = 5;
            }
            slotArgs.splice(expectedLen); // remove unused arguments
            node.codegenNode = createCallExpression(context.helper(RENDER_SLOT), slotArgs, loc);
        }
    };
    function processSlotOutlet(node, context) {
        let slotName = `"default"`;
        let slotProps = undefined;
        const nonNameProps = [];
        for (let i = 0; i < node.props.length; i++) {
            const p = node.props[i];
            if (p.type === 6 /* ATTRIBUTE */) {
                if (p.value) {
                    if (p.name === 'name') {
                        slotName = JSON.stringify(p.value.content);
                    }
                    else {
                        p.name = camelize(p.name);
                        nonNameProps.push(p);
                    }
                }
            }
            else {
                if (p.name === 'bind' && isStaticArgOf(p.arg, 'name')) {
                    if (p.exp)
                        slotName = p.exp;
                }
                else {
                    if (p.name === 'bind' && p.arg && isStaticExp(p.arg)) {
                        p.arg.content = camelize(p.arg.content);
                    }
                    nonNameProps.push(p);
                }
            }
        }
        if (nonNameProps.length > 0) {
            const { props, directives } = buildProps(node, context, nonNameProps, false, false);
            slotProps = props;
            if (directives.length) {
                context.onError(createCompilerError(36 /* X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET */, directives[0].loc));
            }
        }
        return {
            slotName,
            slotProps
        };
    }

    const fnExpRE = /^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/;
    const transformOn = (dir, node, context, augmentor) => {
        const { loc, modifiers, arg } = dir;
        if (!dir.exp && !modifiers.length) {
            context.onError(createCompilerError(35 /* X_V_ON_NO_EXPRESSION */, loc));
        }
        let eventName;
        if (arg.type === 4 /* SIMPLE_EXPRESSION */) {
            if (arg.isStatic) {
                let rawName = arg.content;
                // TODO deprecate @vnodeXXX usage
                if (rawName.startsWith('vue:')) {
                    rawName = `vnode-${rawName.slice(4)}`;
                }
                // for all event listeners, auto convert it to camelCase. See issue #2249
                eventName = createSimpleExpression(toHandlerKey(camelize(rawName)), true, arg.loc);
            }
            else {
                // #2388
                eventName = createCompoundExpression([
                    `${context.helperString(TO_HANDLER_KEY)}(`,
                    arg,
                    `)`
                ]);
            }
        }
        else {
            // already a compound expression.
            eventName = arg;
            eventName.children.unshift(`${context.helperString(TO_HANDLER_KEY)}(`);
            eventName.children.push(`)`);
        }
        // handler processing
        let exp = dir.exp;
        if (exp && !exp.content.trim()) {
            exp = undefined;
        }
        let shouldCache = context.cacheHandlers && !exp && !context.inVOnce;
        if (exp) {
            const isMemberExp = isMemberExpression(exp.content);
            const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content));
            const hasMultipleStatements = exp.content.includes(`;`);
            {
                validateBrowserExpression(exp, context, false, hasMultipleStatements);
            }
            if (isInlineStatement || (shouldCache && isMemberExp)) {
                // wrap inline statement in a function expression
                exp = createCompoundExpression([
                    `${isInlineStatement
                    ? `$event`
                    : `${``}(...args)`} => ${hasMultipleStatements ? `{` : `(`}`,
                    exp,
                    hasMultipleStatements ? `}` : `)`
                ]);
            }
        }
        let ret = {
            props: [
                createObjectProperty(eventName, exp || createSimpleExpression(`() => {}`, false, loc))
            ]
        };
        // apply extended compiler augmentor
        if (augmentor) {
            ret = augmentor(ret);
        }
        if (shouldCache) {
            // cache handlers so that it's always the same handler being passed down.
            // this avoids unnecessary re-renders when users use inline handlers on
            // components.
            ret.props[0].value = context.cache(ret.props[0].value);
        }
        // mark the key as handler for props normalization check
        ret.props.forEach(p => (p.key.isHandlerKey = true));
        return ret;
    };

    // v-bind without arg is handled directly in ./transformElements.ts due to it affecting
    // codegen for the entire props object. This transform here is only for v-bind
    // *with* args.
    const transformBind = (dir, _node, context) => {
        const { exp, modifiers, loc } = dir;
        const arg = dir.arg;
        if (arg.type !== 4 /* SIMPLE_EXPRESSION */) {
            arg.children.unshift(`(`);
            arg.children.push(`) || ""`);
        }
        else if (!arg.isStatic) {
            arg.content = `${arg.content} || ""`;
        }
        // .sync is replaced by v-model:arg
        if (modifiers.includes('camel')) {
            if (arg.type === 4 /* SIMPLE_EXPRESSION */) {
                if (arg.isStatic) {
                    arg.content = camelize(arg.content);
                }
                else {
                    arg.content = `${context.helperString(CAMELIZE)}(${arg.content})`;
                }
            }
            else {
                arg.children.unshift(`${context.helperString(CAMELIZE)}(`);
                arg.children.push(`)`);
            }
        }
        if (!context.inSSR) {
            if (modifiers.includes('prop')) {
                injectPrefix(arg, '.');
            }
            if (modifiers.includes('attr')) {
                injectPrefix(arg, '^');
            }
        }
        if (!exp ||
            (exp.type === 4 /* SIMPLE_EXPRESSION */ && !exp.content.trim())) {
            context.onError(createCompilerError(34 /* X_V_BIND_NO_EXPRESSION */, loc));
            return {
                props: [createObjectProperty(arg, createSimpleExpression('', true, loc))]
            };
        }
        return {
            props: [createObjectProperty(arg, exp)]
        };
    };
    const injectPrefix = (arg, prefix) => {
        if (arg.type === 4 /* SIMPLE_EXPRESSION */) {
            if (arg.isStatic) {
                arg.content = prefix + arg.content;
            }
            else {
                arg.content = `\`${prefix}\${${arg.content}}\``;
            }
        }
        else {
            arg.children.unshift(`'${prefix}' + (`);
            arg.children.push(`)`);
        }
    };

    // Merge adjacent text nodes and expressions into a single expression
    // e.g. <div>abc {{ d }} {{ e }}</div> should have a single expression node as child.
    const transformText = (node, context) => {
        if (node.type === 0 /* ROOT */ ||
            node.type === 1 /* ELEMENT */ ||
            node.type === 11 /* FOR */ ||
            node.type === 10 /* IF_BRANCH */) {
            // perform the transform on node exit so that all expressions have already
            // been processed.
            return () => {
                const children = node.children;
                let currentContainer = undefined;
                let hasText = false;
                for (let i = 0; i < children.length; i++) {
                    const child = children[i];
                    if (isText(child)) {
                        hasText = true;
                        for (let j = i + 1; j < children.length; j++) {
                            const next = children[j];
                            if (isText(next)) {
                                if (!currentContainer) {
                                    currentContainer = children[i] = createCompoundExpression([child], child.loc);
                                }
                                // merge adjacent text node into current
                                currentContainer.children.push(` + `, next);
                                children.splice(j, 1);
                                j--;
                            }
                            else {
                                currentContainer = undefined;
                                break;
                            }
                        }
                    }
                }
                if (!hasText ||
                    // if this is a plain element with a single text child, leave it
                    // as-is since the runtime has dedicated fast path for this by directly
                    // setting textContent of the element.
                    // for component root it's always normalized anyway.
                    (children.length === 1 &&
                        (node.type === 0 /* ROOT */ ||
                            (node.type === 1 /* ELEMENT */ &&
                                node.tagType === 0 /* ELEMENT */ &&
                                // #3756
                                // custom directives can potentially add DOM elements arbitrarily,
                                // we need to avoid setting textContent of the element at runtime
                                // to avoid accidentally overwriting the DOM elements added
                                // by the user through custom directives.
                                !node.props.find(p => p.type === 7 /* DIRECTIVE */ &&
                                    !context.directiveTransforms[p.name]) &&
                                // in compat mode, <template> tags with no special directives
                                // will be rendered as a fragment so its children must be
                                // converted into vnodes.
                                !(false ))))) {
                    return;
                }
                // pre-convert text nodes into createTextVNode(text) calls to avoid
                // runtime normalization.
                for (let i = 0; i < children.length; i++) {
                    const child = children[i];
                    if (isText(child) || child.type === 8 /* COMPOUND_EXPRESSION */) {
                        const callArgs = [];
                        // createTextVNode defaults to single whitespace, so if it is a
                        // single space the code could be an empty call to save bytes.
                        if (child.type !== 2 /* TEXT */ || child.content !== ' ') {
                            callArgs.push(child);
                        }
                        // mark dynamic text with flag so it gets patched inside a block
                        if (!context.ssr &&
                            getConstantType(child, context) === 0 /* NOT_CONSTANT */) {
                            callArgs.push(1 /* TEXT */ +
                                (` /* ${PatchFlagNames[1 /* TEXT */]} */` ));
                        }
                        children[i] = {
                            type: 12 /* TEXT_CALL */,
                            content: child,
                            loc: child.loc,
                            codegenNode: createCallExpression(context.helper(CREATE_TEXT), callArgs)
                        };
                    }
                }
            };
        }
    };

    const seen = new WeakSet();
    const transformOnce = (node, context) => {
        if (node.type === 1 /* ELEMENT */ && findDir(node, 'once', true)) {
            if (seen.has(node) || context.inVOnce) {
                return;
            }
            seen.add(node);
            context.inVOnce = true;
            context.helper(SET_BLOCK_TRACKING);
            return () => {
                context.inVOnce = false;
                const cur = context.currentNode;
                if (cur.codegenNode) {
                    cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */);
                }
            };
        }
    };

    const transformModel = (dir, node, context) => {
        const { exp, arg } = dir;
        if (!exp) {
            context.onError(createCompilerError(41 /* X_V_MODEL_NO_EXPRESSION */, dir.loc));
            return createTransformProps();
        }
        const rawExp = exp.loc.source;
        const expString = exp.type === 4 /* SIMPLE_EXPRESSION */ ? exp.content : rawExp;
        // im SFC <script setup> inline mode, the exp may have been transformed into
        // _unref(exp)
        context.bindingMetadata[rawExp];
        const maybeRef = !true    /* SETUP_CONST */;
        if (!expString.trim() ||
            (!isMemberExpression(expString) && !maybeRef)) {
            context.onError(createCompilerError(42 /* X_V_MODEL_MALFORMED_EXPRESSION */, exp.loc));
            return createTransformProps();
        }
        const propName = arg ? arg : createSimpleExpression('modelValue', true);
        const eventName = arg
            ? isStaticExp(arg)
                ? `onUpdate:${arg.content}`
                : createCompoundExpression(['"onUpdate:" + ', arg])
            : `onUpdate:modelValue`;
        let assignmentExp;
        const eventArg = context.isTS ? `($event: any)` : `$event`;
        {
            assignmentExp = createCompoundExpression([
                `${eventArg} => ((`,
                exp,
                `) = $event)`
            ]);
        }
        const props = [
            // modelValue: foo
            createObjectProperty(propName, dir.exp),
            // "onUpdate:modelValue": $event => (foo = $event)
            createObjectProperty(eventName, assignmentExp)
        ];
        // modelModifiers: { foo: true, "bar-baz": true }
        if (dir.modifiers.length && node.tagType === 1 /* COMPONENT */) {
            const modifiers = dir.modifiers
                .map(m => (isSimpleIdentifier(m) ? m : JSON.stringify(m)) + `: true`)
                .join(`, `);
            const modifiersKey = arg
                ? isStaticExp(arg)
                    ? `${arg.content}Modifiers`
                    : createCompoundExpression([arg, ' + "Modifiers"'])
                : `modelModifiers`;
            props.push(createObjectProperty(modifiersKey, createSimpleExpression(`{ ${modifiers} }`, false, dir.loc, 2 /* CAN_HOIST */)));
        }
        return createTransformProps(props);
    };
    function createTransformProps(props = []) {
        return { props };
    }

    const seen$1 = new WeakSet();
    const transformMemo = (node, context) => {
        if (node.type === 1 /* ELEMENT */) {
            const dir = findDir(node, 'memo');
            if (!dir || seen$1.has(node)) {
                return;
            }
            seen$1.add(node);
            return () => {
                const codegenNode = node.codegenNode ||
                    context.currentNode.codegenNode;
                if (codegenNode && codegenNode.type === 13 /* VNODE_CALL */) {
                    // non-component sub tree should be turned into a block
                    if (node.tagType !== 1 /* COMPONENT */) {
                        makeBlock(codegenNode, context);
                    }
                    node.codegenNode = createCallExpression(context.helper(WITH_MEMO), [
                        dir.exp,
                        createFunctionExpression(undefined, codegenNode),
                        `_cache`,
                        String(context.cached++)
                    ]);
                }
            };
        }
    };

    function getBaseTransformPreset(prefixIdentifiers) {
        return [
            [
                transformOnce,
                transformIf,
                transformMemo,
                transformFor,
                ...([]),
                ...([transformExpression]
                        ),
                transformSlotOutlet,
                transformElement,
                trackSlotScopes,
                transformText
            ],
            {
                on: transformOn,
                bind: transformBind,
                model: transformModel
            }
        ];
    }
    // we name it `baseCompile` so that higher order compilers like
    // @vue/compiler-dom can export `compile` while re-exporting everything else.
    function baseCompile(template, options = {}) {
        const onError = options.onError || defaultOnError;
        const isModuleMode = options.mode === 'module';
        /* istanbul ignore if */
        {
            if (options.prefixIdentifiers === true) {
                onError(createCompilerError(46 /* X_PREFIX_ID_NOT_SUPPORTED */));
            }
            else if (isModuleMode) {
                onError(createCompilerError(47 /* X_MODULE_MODE_NOT_SUPPORTED */));
            }
        }
        const prefixIdentifiers = !true ;
        if (options.cacheHandlers) {
            onError(createCompilerError(48 /* X_CACHE_HANDLER_NOT_SUPPORTED */));
        }
        if (options.scopeId && !isModuleMode) {
            onError(createCompilerError(49 /* X_SCOPE_ID_NOT_SUPPORTED */));
        }
        const ast = isString(template) ? baseParse(template, options) : template;
        const [nodeTransforms, directiveTransforms] = getBaseTransformPreset();
        transform$2(ast, extend({}, options, {
            prefixIdentifiers,
            nodeTransforms: [
                ...nodeTransforms,
                ...(options.nodeTransforms || []) // user transforms
            ],
            directiveTransforms: extend({}, directiveTransforms, options.directiveTransforms || {} // user transforms
            )
        }));
        return generate(ast, extend({}, options, {
            prefixIdentifiers
        }));
    }

    const noopDirectiveTransform = () => ({ props: [] });

    const V_MODEL_RADIO = Symbol(`vModelRadio` );
    const V_MODEL_CHECKBOX = Symbol(`vModelCheckbox` );
    const V_MODEL_TEXT = Symbol(`vModelText` );
    const V_MODEL_SELECT = Symbol(`vModelSelect` );
    const V_MODEL_DYNAMIC = Symbol(`vModelDynamic` );
    const V_ON_WITH_MODIFIERS = Symbol(`vOnModifiersGuard` );
    const V_ON_WITH_KEYS = Symbol(`vOnKeysGuard` );
    const V_SHOW = Symbol(`vShow` );
    const TRANSITION$1 = Symbol(`Transition` );
    const TRANSITION_GROUP = Symbol(`TransitionGroup` );
    registerRuntimeHelpers({
        [V_MODEL_RADIO]: `vModelRadio`,
        [V_MODEL_CHECKBOX]: `vModelCheckbox`,
        [V_MODEL_TEXT]: `vModelText`,
        [V_MODEL_SELECT]: `vModelSelect`,
        [V_MODEL_DYNAMIC]: `vModelDynamic`,
        [V_ON_WITH_MODIFIERS]: `withModifiers`,
        [V_ON_WITH_KEYS]: `withKeys`,
        [V_SHOW]: `vShow`,
        [TRANSITION$1]: `Transition`,
        [TRANSITION_GROUP]: `TransitionGroup`
    });

    /* eslint-disable no-restricted-globals */
    let decoder;
    function decodeHtmlBrowser(raw, asAttr = false) {
        if (!decoder) {
            decoder = document.createElement('div');
        }
        if (asAttr) {
            decoder.innerHTML = `<div foo="${raw.replace(/"/g, '&quot;')}">`;
            return decoder.children[0].getAttribute('foo');
        }
        else {
            decoder.innerHTML = raw;
            return decoder.textContent;
        }
    }

    const isRawTextContainer = /*#__PURE__*/ makeMap('style,iframe,script,noscript', true);
    const parserOptions = {
        isVoidTag,
        isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag),
        isPreTag: tag => tag === 'pre',
        decodeEntities: decodeHtmlBrowser ,
        isBuiltInComponent: (tag) => {
            if (isBuiltInType(tag, `Transition`)) {
                return TRANSITION$1;
            }
            else if (isBuiltInType(tag, `TransitionGroup`)) {
                return TRANSITION_GROUP;
            }
        },
        // https://html.spec.whatwg.org/multipage/parsing.html#tree-construction-dispatcher
        getNamespace(tag, parent) {
            let ns = parent ? parent.ns : 0 /* HTML */;
            if (parent && ns === 2 /* MATH_ML */) {
                if (parent.tag === 'annotation-xml') {
                    if (tag === 'svg') {
                        return 1 /* SVG */;
                    }
                    if (parent.props.some(a => a.type === 6 /* ATTRIBUTE */ &&
                        a.name === 'encoding' &&
                        a.value != null &&
                        (a.value.content === 'text/html' ||
                            a.value.content === 'application/xhtml+xml'))) {
                        ns = 0 /* HTML */;
                    }
                }
                else if (/^m(?:[ions]|text)$/.test(parent.tag) &&
                    tag !== 'mglyph' &&
                    tag !== 'malignmark') {
                    ns = 0 /* HTML */;
                }
            }
            else if (parent && ns === 1 /* SVG */) {
                if (parent.tag === 'foreignObject' ||
                    parent.tag === 'desc' ||
                    parent.tag === 'title') {
                    ns = 0 /* HTML */;
                }
            }
            if (ns === 0 /* HTML */) {
                if (tag === 'svg') {
                    return 1 /* SVG */;
                }
                if (tag === 'math') {
                    return 2 /* MATH_ML */;
                }
            }
            return ns;
        },
        // https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-fragments
        getTextMode({ tag, ns }) {
            if (ns === 0 /* HTML */) {
                if (tag === 'textarea' || tag === 'title') {
                    return 1 /* RCDATA */;
                }
                if (isRawTextContainer(tag)) {
                    return 2 /* RAWTEXT */;
                }
            }
            return 0 /* DATA */;
        }
    };

    // Parse inline CSS strings for static style attributes into an object.
    // This is a NodeTransform since it works on the static `style` attribute and
    // converts it into a dynamic equivalent:
    // style="color: red" -> :style='{ "color": "red" }'
    // It is then processed by `transformElement` and included in the generated
    // props.
    const transformStyle = node => {
        if (node.type === 1 /* ELEMENT */) {
            node.props.forEach((p, i) => {
                if (p.type === 6 /* ATTRIBUTE */ && p.name === 'style' && p.value) {
                    // replace p with an expression node
                    node.props[i] = {
                        type: 7 /* DIRECTIVE */,
                        name: `bind`,
                        arg: createSimpleExpression(`style`, true, p.loc),
                        exp: parseInlineCSS(p.value.content, p.loc),
                        modifiers: [],
                        loc: p.loc
                    };
                }
            });
        }
    };
    const parseInlineCSS = (cssText, loc) => {
        const normalized = parseStringStyle(cssText);
        return createSimpleExpression(JSON.stringify(normalized), false, loc, 3 /* CAN_STRINGIFY */);
    };

    function createDOMCompilerError(code, loc) {
        return createCompilerError(code, loc, DOMErrorMessages );
    }
    const DOMErrorMessages = {
        [50 /* X_V_HTML_NO_EXPRESSION */]: `v-html is missing expression.`,
        [51 /* X_V_HTML_WITH_CHILDREN */]: `v-html will override element children.`,
        [52 /* X_V_TEXT_NO_EXPRESSION */]: `v-text is missing expression.`,
        [53 /* X_V_TEXT_WITH_CHILDREN */]: `v-text will override element children.`,
        [54 /* X_V_MODEL_ON_INVALID_ELEMENT */]: `v-model can only be used on <input>, <textarea> and <select> elements.`,
        [55 /* X_V_MODEL_ARG_ON_ELEMENT */]: `v-model argument is not supported on plain elements.`,
        [56 /* X_V_MODEL_ON_FILE_INPUT_ELEMENT */]: `v-model cannot be used on file inputs since they are read-only. Use a v-on:change listener instead.`,
        [57 /* X_V_MODEL_UNNECESSARY_VALUE */]: `Unnecessary value binding used alongside v-model. It will interfere with v-model's behavior.`,
        [58 /* X_V_SHOW_NO_EXPRESSION */]: `v-show is missing expression.`,
        [59 /* X_TRANSITION_INVALID_CHILDREN */]: `<Transition> expects exactly one child element or component.`,
        [60 /* X_IGNORED_SIDE_EFFECT_TAG */]: `Tags with side effect (<script> and <style>) are ignored in client component templates.`
    };

    const transformVHtml = (dir, node, context) => {
        const { exp, loc } = dir;
        if (!exp) {
            context.onError(createDOMCompilerError(50 /* X_V_HTML_NO_EXPRESSION */, loc));
        }
        if (node.children.length) {
            context.onError(createDOMCompilerError(51 /* X_V_HTML_WITH_CHILDREN */, loc));
            node.children.length = 0;
        }
        return {
            props: [
                createObjectProperty(createSimpleExpression(`innerHTML`, true, loc), exp || createSimpleExpression('', true))
            ]
        };
    };

    const transformVText = (dir, node, context) => {
        const { exp, loc } = dir;
        if (!exp) {
            context.onError(createDOMCompilerError(52 /* X_V_TEXT_NO_EXPRESSION */, loc));
        }
        if (node.children.length) {
            context.onError(createDOMCompilerError(53 /* X_V_TEXT_WITH_CHILDREN */, loc));
            node.children.length = 0;
        }
        return {
            props: [
                createObjectProperty(createSimpleExpression(`textContent`, true), exp
                    ? getConstantType(exp, context) > 0
                        ? exp
                        : createCallExpression(context.helperString(TO_DISPLAY_STRING), [exp], loc)
                    : createSimpleExpression('', true))
            ]
        };
    };

    const transformModel$1 = (dir, node, context) => {
        const baseResult = transformModel(dir, node, context);
        // base transform has errors OR component v-model (only need props)
        if (!baseResult.props.length || node.tagType === 1 /* COMPONENT */) {
            return baseResult;
        }
        if (dir.arg) {
            context.onError(createDOMCompilerError(55 /* X_V_MODEL_ARG_ON_ELEMENT */, dir.arg.loc));
        }
        function checkDuplicatedValue() {
            const value = findProp(node, 'value');
            if (value) {
                context.onError(createDOMCompilerError(57 /* X_V_MODEL_UNNECESSARY_VALUE */, value.loc));
            }
        }
        const { tag } = node;
        const isCustomElement = context.isCustomElement(tag);
        if (tag === 'input' ||
            tag === 'textarea' ||
            tag === 'select' ||
            isCustomElement) {
            let directiveToUse = V_MODEL_TEXT;
            let isInvalidType = false;
            if (tag === 'input' || isCustomElement) {
                const type = findProp(node, `type`);
                if (type) {
                    if (type.type === 7 /* DIRECTIVE */) {
                        // :type="foo"
                        directiveToUse = V_MODEL_DYNAMIC;
                    }
                    else if (type.value) {
                        switch (type.value.content) {
                            case 'radio':
                                directiveToUse = V_MODEL_RADIO;
                                break;
                            case 'checkbox':
                                directiveToUse = V_MODEL_CHECKBOX;
                                break;
                            case 'file':
                                isInvalidType = true;
                                context.onError(createDOMCompilerError(56 /* X_V_MODEL_ON_FILE_INPUT_ELEMENT */, dir.loc));
                                break;
                            default:
                                // text type
                                checkDuplicatedValue();
                                break;
                        }
                    }
                }
                else if (hasDynamicKeyVBind(node)) {
                    // element has bindings with dynamic keys, which can possibly contain
                    // "type".
                    directiveToUse = V_MODEL_DYNAMIC;
                }
                else {
                    // text type
                    checkDuplicatedValue();
                }
            }
            else if (tag === 'select') {
                directiveToUse = V_MODEL_SELECT;
            }
            else {
                // textarea
                checkDuplicatedValue();
            }
            // inject runtime directive
            // by returning the helper symbol via needRuntime
            // the import will replaced a resolveDirective call.
            if (!isInvalidType) {
                baseResult.needRuntime = context.helper(directiveToUse);
            }
        }
        else {
            context.onError(createDOMCompilerError(54 /* X_V_MODEL_ON_INVALID_ELEMENT */, dir.loc));
        }
        // native vmodel doesn't need the `modelValue` props since they are also
        // passed to the runtime as `binding.value`. removing it reduces code size.
        baseResult.props = baseResult.props.filter(p => !(p.key.type === 4 /* SIMPLE_EXPRESSION */ &&
            p.key.content === 'modelValue'));
        return baseResult;
    };

    const isEventOptionModifier = /*#__PURE__*/ makeMap(`passive,once,capture`);
    const isNonKeyModifier = /*#__PURE__*/ makeMap(
    // event propagation management
`stop,prevent,self,`     +
        // system modifiers + exact
        `ctrl,shift,alt,meta,exact,` +
        // mouse
        `middle`);
    // left & right could be mouse or key modifiers based on event type
    const maybeKeyModifier = /*#__PURE__*/ makeMap('left,right');
    const isKeyboardEvent = /*#__PURE__*/ makeMap(`onkeyup,onkeydown,onkeypress`, true);
    const resolveModifiers = (key, modifiers, context, loc) => {
        const keyModifiers = [];
        const nonKeyModifiers = [];
        const eventOptionModifiers = [];
        for (let i = 0; i < modifiers.length; i++) {
            const modifier = modifiers[i];
            if (isEventOptionModifier(modifier)) {
                // eventOptionModifiers: modifiers for addEventListener() options,
                // e.g. .passive & .capture
                eventOptionModifiers.push(modifier);
            }
            else {
                // runtimeModifiers: modifiers that needs runtime guards
                if (maybeKeyModifier(modifier)) {
                    if (isStaticExp(key)) {
                        if (isKeyboardEvent(key.content)) {
                            keyModifiers.push(modifier);
                        }
                        else {
                            nonKeyModifiers.push(modifier);
                        }
                    }
                    else {
                        keyModifiers.push(modifier);
                        nonKeyModifiers.push(modifier);
                    }
                }
                else {
                    if (isNonKeyModifier(modifier)) {
                        nonKeyModifiers.push(modifier);
                    }
                    else {
                        keyModifiers.push(modifier);
                    }
                }
            }
        }
        return {
            keyModifiers,
            nonKeyModifiers,
            eventOptionModifiers
        };
    };
    const transformClick = (key, event) => {
        const isStaticClick = isStaticExp(key) && key.content.toLowerCase() === 'onclick';
        return isStaticClick
            ? createSimpleExpression(event, true)
            : key.type !== 4 /* SIMPLE_EXPRESSION */
                ? createCompoundExpression([
                    `(`,
                    key,
                    `) === "onClick" ? "${event}" : (`,
                    key,
                    `)`
                ])
                : key;
    };
    const transformOn$1 = (dir, node, context) => {
        return transformOn(dir, node, context, baseResult => {
            const { modifiers } = dir;
            if (!modifiers.length)
                return baseResult;
            let { key, value: handlerExp } = baseResult.props[0];
            const { keyModifiers, nonKeyModifiers, eventOptionModifiers } = resolveModifiers(key, modifiers, context, dir.loc);
            // normalize click.right and click.middle since they don't actually fire
            if (nonKeyModifiers.includes('right')) {
                key = transformClick(key, `onContextmenu`);
            }
            if (nonKeyModifiers.includes('middle')) {
                key = transformClick(key, `onMouseup`);
            }
            if (nonKeyModifiers.length) {
                handlerExp = createCallExpression(context.helper(V_ON_WITH_MODIFIERS), [
                    handlerExp,
                    JSON.stringify(nonKeyModifiers)
                ]);
            }
            if (keyModifiers.length &&
                // if event name is dynamic, always wrap with keys guard
                (!isStaticExp(key) || isKeyboardEvent(key.content))) {
                handlerExp = createCallExpression(context.helper(V_ON_WITH_KEYS), [
                    handlerExp,
                    JSON.stringify(keyModifiers)
                ]);
            }
            if (eventOptionModifiers.length) {
                const modifierPostfix = eventOptionModifiers.map(capitalize).join('');
                key = isStaticExp(key)
                    ? createSimpleExpression(`${key.content}${modifierPostfix}`, true)
                    : createCompoundExpression([`(`, key, `) + "${modifierPostfix}"`]);
            }
            return {
                props: [createObjectProperty(key, handlerExp)]
            };
        });
    };

    const transformShow = (dir, node, context) => {
        const { exp, loc } = dir;
        if (!exp) {
            context.onError(createDOMCompilerError(58 /* X_V_SHOW_NO_EXPRESSION */, loc));
        }
        return {
            props: [],
            needRuntime: context.helper(V_SHOW)
        };
    };

    const transformTransition = (node, context) => {
        if (node.type === 1 /* ELEMENT */ &&
            node.tagType === 1 /* COMPONENT */) {
            const component = context.isBuiltInComponent(node.tag);
            if (component === TRANSITION$1) {
                return () => {
                    if (!node.children.length) {
                        return;
                    }
                    // warn multiple transition children
                    if (hasMultipleChildren(node)) {
                        context.onError(createDOMCompilerError(59 /* X_TRANSITION_INVALID_CHILDREN */, {
                            start: node.children[0].loc.start,
                            end: node.children[node.children.length - 1].loc.end,
                            source: ''
                        }));
                    }
                    // check if it's s single child w/ v-show
                    // if yes, inject "persisted: true" to the transition props
                    const child = node.children[0];
                    if (child.type === 1 /* ELEMENT */) {
                        for (const p of child.props) {
                            if (p.type === 7 /* DIRECTIVE */ && p.name === 'show') {
                                node.props.push({
                                    type: 6 /* ATTRIBUTE */,
                                    name: 'persisted',
                                    value: undefined,
                                    loc: node.loc
                                });
                            }
                        }
                    }
                };
            }
        }
    };
    function hasMultipleChildren(node) {
        // #1352 filter out potential comment nodes.
        const children = (node.children = node.children.filter(c => c.type !== 3 /* COMMENT */ &&
            !(c.type === 2 /* TEXT */ && !c.content.trim())));
        const child = children[0];
        return (children.length !== 1 ||
            child.type === 11 /* FOR */ ||
            (child.type === 9 /* IF */ && child.branches.some(hasMultipleChildren)));
    }

    const ignoreSideEffectTags = (node, context) => {
        if (node.type === 1 /* ELEMENT */ &&
            node.tagType === 0 /* ELEMENT */ &&
            (node.tag === 'script' || node.tag === 'style')) {
            context.onError(createDOMCompilerError(60 /* X_IGNORED_SIDE_EFFECT_TAG */, node.loc));
            context.removeNode();
        }
    };

    const DOMNodeTransforms = [
        transformStyle,
        ...([transformTransition] )
    ];
    const DOMDirectiveTransforms = {
        cloak: noopDirectiveTransform,
        html: transformVHtml,
        text: transformVText,
        model: transformModel$1,
        on: transformOn$1,
        show: transformShow
    };
    function compile$1(template, options = {}) {
        return baseCompile(template, extend({}, parserOptions, options, {
            nodeTransforms: [
                // ignore <script> and <tag>
                // this is not put inside DOMNodeTransforms because that list is used
                // by compiler-ssr to generate vnode fallback branches
                ignoreSideEffectTags,
                ...DOMNodeTransforms,
                ...(options.nodeTransforms || [])
            ],
            directiveTransforms: extend({}, DOMDirectiveTransforms, options.directiveTransforms || {}),
            transformHoist: null 
        }));
    }

    // This entry is the "full-build" that includes both the runtime
    {
        initDev();
    }
    const compileCache = Object.create(null);
    function compileToFunction(template, options) {
        if (!isString(template)) {
            if (template.nodeType) {
                template = template.innerHTML;
            }
            else {
                warn$1(`invalid template option: `, template);
                return NOOP;
            }
        }
        const key = template;
        const cached = compileCache[key];
        if (cached) {
            return cached;
        }
        if (template[0] === '#') {
            const el = document.querySelector(template);
            if (!el) {
                warn$1(`Template element not found or is empty: ${template}`);
            }
            // __UNSAFE__
            // Reason: potential execution of JS expressions in in-DOM template.
            // The user must make sure the in-DOM template is trusted. If it's rendered
            // by the server, the template should not contain any user data.
            template = el ? el.innerHTML : ``;
        }
        const { code } = compile$1(template, extend({
            hoistStatic: true,
            onError: onError ,
            onWarn: e => onError(e, true) 
        }, options));
        function onError(err, asWarning = false) {
            const message = asWarning
                ? err.message
                : `Template compilation error: ${err.message}`;
            const codeFrame = err.loc &&
                generateCodeFrame(template, err.loc.start.offset, err.loc.end.offset);
            warn$1(codeFrame ? `${message}\n${codeFrame}` : message);
        }
        // The wildcard import results in a huge object with every export
        // with keys that cannot be mangled, and can be quite heavy size-wise.
        // In the global build we know `Vue` is available globally so we can avoid
        // the wildcard object.
        const render = (new Function('Vue', code)(runtimeDom));
        render._rc = true;
        return (compileCache[key] = render);
    }
    registerRuntimeCompiler(compileToFunction);

    var Vue = /*#__PURE__*/Object.freeze({
        __proto__: null,
        BaseTransition: BaseTransition,
        Comment: Comment,
        EffectScope: EffectScope,
        Fragment: Fragment,
        KeepAlive: KeepAlive,
        ReactiveEffect: ReactiveEffect,
        Static: Static,
        Suspense: Suspense,
        Teleport: Teleport,
        Text: Text,
        Transition: Transition,
        TransitionGroup: TransitionGroup,
        VueElement: VueElement,
        callWithAsyncErrorHandling: callWithAsyncErrorHandling,
        callWithErrorHandling: callWithErrorHandling,
        camelize: camelize,
        capitalize: capitalize,
        cloneVNode: cloneVNode,
        compatUtils: compatUtils,
        compile: compileToFunction,
        computed: computed$1,
        createApp: createApp,
        createBlock: createBlock,
        createCommentVNode: createCommentVNode,
        createElementBlock: createElementBlock,
        createElementVNode: createBaseVNode,
        createHydrationRenderer: createHydrationRenderer,
        createPropsRestProxy: createPropsRestProxy,
        createRenderer: createRenderer,
        createSSRApp: createSSRApp,
        createSlots: createSlots,
        createStaticVNode: createStaticVNode,
        createTextVNode: createTextVNode,
        createVNode: createVNode,
        customRef: customRef,
        defineAsyncComponent: defineAsyncComponent,
        defineComponent: defineComponent,
        defineCustomElement: defineCustomElement,
        defineEmits: defineEmits,
        defineExpose: defineExpose,
        defineProps: defineProps,
        defineSSRCustomElement: defineSSRCustomElement,
        get devtools () { return devtools; },
        effect: effect,
        effectScope: effectScope,
        getCurrentInstance: getCurrentInstance,
        getCurrentScope: getCurrentScope,
        getTransitionRawChildren: getTransitionRawChildren,
        guardReactiveProps: guardReactiveProps,
        h: h$2,
        handleError: handleError,
        hydrate: hydrate,
        initCustomFormatter: initCustomFormatter,
        initDirectivesForSSR: initDirectivesForSSR,
        inject: inject,
        isMemoSame: isMemoSame,
        isProxy: isProxy,
        isReactive: isReactive,
        isReadonly: isReadonly,
        isRef: isRef,
        isRuntimeOnly: isRuntimeOnly,
        isShallow: isShallow,
        isVNode: isVNode,
        markRaw: markRaw,
        mergeDefaults: mergeDefaults,
        mergeProps: mergeProps,
        nextTick: nextTick,
        normalizeClass: normalizeClass,
        normalizeProps: normalizeProps,
        normalizeStyle: normalizeStyle,
        onActivated: onActivated,
        onBeforeMount: onBeforeMount,
        onBeforeUnmount: onBeforeUnmount,
        onBeforeUpdate: onBeforeUpdate,
        onDeactivated: onDeactivated,
        onErrorCaptured: onErrorCaptured,
        onMounted: onMounted,
        onRenderTracked: onRenderTracked,
        onRenderTriggered: onRenderTriggered,
        onScopeDispose: onScopeDispose,
        onServerPrefetch: onServerPrefetch,
        onUnmounted: onUnmounted,
        onUpdated: onUpdated,
        openBlock: openBlock,
        popScopeId: popScopeId,
        provide: provide,
        proxyRefs: proxyRefs,
        pushScopeId: pushScopeId,
        queuePostFlushCb: queuePostFlushCb,
        reactive: reactive,
        readonly: readonly,
        ref: ref,
        registerRuntimeCompiler: registerRuntimeCompiler,
        render: render$l,
        renderList: renderList,
        renderSlot: renderSlot,
        resolveComponent: resolveComponent,
        resolveDirective: resolveDirective,
        resolveDynamicComponent: resolveDynamicComponent,
        resolveFilter: resolveFilter,
        resolveTransitionHooks: resolveTransitionHooks,
        setBlockTracking: setBlockTracking,
        setDevtoolsHook: setDevtoolsHook,
        setTransitionHooks: setTransitionHooks,
        shallowReactive: shallowReactive,
        shallowReadonly: shallowReadonly,
        shallowRef: shallowRef,
        ssrContextKey: ssrContextKey,
        ssrUtils: ssrUtils,
        stop: stop,
        toDisplayString: toDisplayString,
        toHandlerKey: toHandlerKey,
        toHandlers: toHandlers,
        toRaw: toRaw,
        toRef: toRef,
        toRefs: toRefs,
        transformVNodeArgs: transformVNodeArgs,
        triggerRef: triggerRef,
        unref: unref,
        useAttrs: useAttrs,
        useCssModule: useCssModule,
        useCssVars: useCssVars,
        useSSRContext: useSSRContext,
        useSlots: useSlots,
        useTransitionState: useTransitionState,
        vModelCheckbox: vModelCheckbox,
        vModelDynamic: vModelDynamic,
        vModelRadio: vModelRadio,
        vModelSelect: vModelSelect,
        vModelText: vModelText,
        vShow: vShow,
        version: version,
        warn: warn$1,
        watch: watch,
        watchEffect: watchEffect,
        watchPostEffect: watchPostEffect,
        watchSyncEffect: watchSyncEffect,
        withAsyncContext: withAsyncContext,
        withCtx: withCtx,
        withDefaults: withDefaults,
        withDirectives: withDirectives,
        withKeys: withKeys,
        withMemo: withMemo,
        withModifiers: withModifiers,
        withScopeId: withScopeId
    });

    let filters_const_dict = {};

    async function get_filter_const$1(url_request) {
        if (filters_const_dict[url_request] == null) {
    	filters_const_dict[url_request] = ntopng_utility.http_request(url_request);
        }
        let filter_consts = await filters_const_dict[url_request];
        return filter_consts;
    }

    const tag_operator_label_dict = {
        "eq": "=",
        "neq": "!=",
        "lt": "<",
        "gt": ">",
        "gte": ">=",
        "lte": "<=",
        "in": "contains",
        "nin": "does not contain"
    };

    const load_filters_data$1 = async function (filters_const) {
        filters_const.filter((x) => x.label == null).forEach((x) => { console.error(`label not defined for filter ${JSON.stringify(x)}`); x.label = ""; });
        filters_const.sort((a, b) => a.label.localeCompare(b.label));
        i18n_ext.tags = {};
        TAG_OPERATORS = {};
        DEFINED_TAGS = {};
        filters_const.forEach((f_def) => {
            i18n_ext.tags[f_def.id] = f_def.label;
            f_def.operators.forEach((op) => TAG_OPERATORS[op.id] = op.label);
            DEFINED_TAGS[f_def.id] = f_def.operators.map((op) => op.id);
        });
        let entries = ntopng_url_manager.get_url_entries();
        let filters = [];
        for (const [key, value] of entries) {
            let filter_def = FILTERS_CONST.find((fc) => fc.id == key);
            if (filter_def != null) {
                let options_string = value.split(",");
                options_string.forEach((opt_stirng) => {
                    let [value, operator] = opt_stirng.split(";");
                    if (
                        operator == null || value == null || operator == ""
                        || (filter_def.options != null && filter_def.options.find((opt) => opt.value == value) == null)
                    ) {
                        return;
                    }
    		let value_label = value;
    		if (filter_def.value_type == "array") {
    		    value_label = filter_def?.options?.find((opt) => opt.value == value)?.label;
    		}
                    filters.push({ id: filter_def.id, operator: operator, value: value, label: filter_def.label, value_label });
                });
            }
        }
        return filters;
        // "l7proto=XXX;eq"
    };


    function get_filters_object(filters) {
        let filters_groups = {};
        filters.forEach((f) => {
            let group = filters_groups[f.id];
            if (group == null) {
                group = [];
                filters_groups[f.id] = group;
            }
            group.push(f);
        });
        let filters_object = {};
        for (let f_id in filters_groups) {
            let group = filters_groups[f_id];
            let filter_values = group.filter((f) => f.value != null && f.operator != null && f.operator != "").map((f) => `${f.value};${f.operator}`).join(",");
            filters_object[f_id] = filter_values;
        }
        return filters_object;
    }

    const filtersManager = function () {
        return {
    	get_filter_const: get_filter_const$1,
    	get_filters_object,
    	load_filters_data: load_filters_data$1,
    	tag_operator_label_dict,
        };
    }();

    var script$2K = defineComponent({
        components: {
        },
        props: {
    	id: String,
    	main_title: Object,
      secondary_title_list: Array,
    	help_link: String,
    	items_table: Array,
        },
        emits: ["click_item"],
        /** This method is the first method of the component called, it's called before html template creation. */
        created() {
          },
        data() {
    	return {
    	    //i18n: (t) => i18n(t),
    	};
        },
        /** This method is the first method called after html template creation. */
        mounted() {
    	ntopng_sync.ready(this.$props["id"]);
        },
        methods: {
        },
    });

    const _hoisted_1$2t = { class: "navbar navbar-shadow navbar-expand-lg navbar-light bg-light px-2 mb-2" };
    const _hoisted_2$2e = {
      class: "me-1 text-nowrap",
      style: {"font-size":"1.1rem"}
    };
    const _hoisted_3$20 = ["href", "title"];
    const _hoisted_4$1X = ["title"];
    const _hoisted_5$1E = ["href", "title"];
    const _hoisted_6$1o = ["title"];
    const _hoisted_7$1h = {
      class: "collapse navbar-collapse scroll-x",
      id: "navbarNav"
    };
    const _hoisted_8$16 = { class: "navbar-nav" };
    const _hoisted_9$X = ["onClick"];
    const _hoisted_10$R = {
      key: 0,
      class: "badge rounded-pill bg-dark",
      style: {"float":"right","margin-bottom":"-10px"}
    };
    const _hoisted_11$L = ["onClick"];
    const _hoisted_12$G = {
      key: 0,
      class: "badge rounded-pill bg-dark",
      style: {"float":"right","margin-bottom":"-10px"}
    };
    const _hoisted_13$z = ["onClick"];
    const _hoisted_14$u = {
      key: 0,
      class: "badge rounded-pill bg-dark",
      style: {"float":"right","margin-bottom":"-10px"}
    };
    const _hoisted_15$r = { class: "navbar-nav ms-auto" };
    const _hoisted_16$r = ["href"];

    function render$k(_ctx, _cache, $props, $setup, $data, $options) {
      return (openBlock(), createElementBlock("nav", _hoisted_1$2t, [
        createBaseVNode("span", _hoisted_2$2e, [
          createBaseVNode("i", {
            class: normalizeClass(_ctx.main_title.icon)
          }, null, 2 /* CLASS */),
          (_ctx.main_title.href)
            ? (openBlock(), createElementBlock("a", {
                key: 0,
                href: _ctx.main_title.href,
                title: _ctx.main_title.title
              }, toDisplayString(_ctx.main_title.label), 9 /* TEXT, PROPS */, _hoisted_3$20))
            : (openBlock(), createElementBlock("span", {
                key: 1,
                title: _ctx.main_title.title
              }, toDisplayString(_ctx.main_title.label), 9 /* TEXT, PROPS */, _hoisted_4$1X)),
          (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.secondary_title_list, (item) => {
            return (openBlock(), createElementBlock(Fragment, null, [
              _cache[0] || (_cache[0] = createTextVNode(" / ", -1 /* CACHED */)),
              (item.href)
                ? (openBlock(), createElementBlock("a", {
                    key: 0,
                    class: normalizeClass(item.class),
                    href: item.href,
                    title: item.title
                  }, [
                    createTextVNode(toDisplayString(item.label) + " ", 1 /* TEXT */),
                    (item.icon)
                      ? (openBlock(), createElementBlock("i", {
                          key: 0,
                          class: normalizeClass(item.icon)
                        }, null, 2 /* CLASS */))
                      : createCommentVNode("v-if", true)
                  ], 10 /* CLASS, PROPS */, _hoisted_5$1E))
                : (openBlock(), createElementBlock("span", {
                    key: 1,
                    title: item.title
                  }, toDisplayString(item.label), 9 /* TEXT, PROPS */, _hoisted_6$1o))
            ], 64 /* STABLE_FRAGMENT */))
          }), 256 /* UNKEYED_FRAGMENT */))
        ]),
        _cache[3] || (_cache[3] = createBaseVNode("span", { class: "text-muted ms-1 d-none d-lg-inline d-md-none" }, "|", -1 /* CACHED */)),
        _cache[4] || (_cache[4] = createBaseVNode("button", {
          class: "navbar-toggler",
          type: "button"
        }, [
          createBaseVNode("span", { class: "navbar-toggler-icon" })
        ], -1 /* CACHED */)),
        createBaseVNode("div", _hoisted_7$1h, [
          createBaseVNode("ul", _hoisted_8$16, [
            (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.items_table, (item) => {
              return (openBlock(), createElementBlock(Fragment, null, [
                (item.active)
                  ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                      (item.active)
                        ? (openBlock(), createElementBlock("li", {
                            key: 0,
                            onClick: $event => (this.$emit('click_item', item)),
                            class: normalizeClass(["nav-item nav-link active", item.class])
                          }, [
                            (item.badge_num > 0)
                              ? (openBlock(), createElementBlock("span", _hoisted_10$R, toDisplayString(item.badge_num), 1 /* TEXT */))
                              : createCommentVNode("v-if", true),
                            createBaseVNode("b", null, [
                              createBaseVNode("i", {
                                class: normalizeClass(item.icon)
                              }, null, 2 /* CLASS */),
                              createTextVNode(" " + toDisplayString(item.label), 1 /* TEXT */)
                            ])
                          ], 10 /* CLASS, PROPS */, _hoisted_9$X))
                        : (openBlock(), createElementBlock("li", {
                            key: 1,
                            onClick: $event => (this.$emit('click_item', item)),
                            class: normalizeClass(["nav-item nav-link", item.class])
                          }, [
                            (item.badge_num > 0)
                              ? (openBlock(), createElementBlock("span", _hoisted_12$G, toDisplayString(item.badge_num), 1 /* TEXT */))
                              : createCommentVNode("v-if", true),
                            createBaseVNode("b", null, [
                              createBaseVNode("i", {
                                class: normalizeClass(item.icon)
                              }, null, 2 /* CLASS */),
                              createTextVNode(" " + toDisplayString(item.label), 1 /* TEXT */)
                            ])
                          ], 10 /* CLASS, PROPS */, _hoisted_11$L))
                    ], 64 /* STABLE_FRAGMENT */))
                  : (openBlock(), createElementBlock("a", {
                      key: 1,
                      onClick: $event => (this.$emit('click_item', item)),
                      href: "#",
                      class: normalizeClass(["nav-item nav-link", item.class])
                    }, [
                      (item.badge_num > 0)
                        ? (openBlock(), createElementBlock("span", _hoisted_14$u, toDisplayString(item.badge_num), 1 /* TEXT */))
                        : createCommentVNode("v-if", true),
                      createBaseVNode("i", {
                        class: normalizeClass(item.icon)
                      }, null, 2 /* CLASS */),
                      createTextVNode(" " + toDisplayString(item.label), 1 /* TEXT */)
                    ], 10 /* CLASS, PROPS */, _hoisted_13$z))
              ], 64 /* STABLE_FRAGMENT */))
            }), 256 /* UNKEYED_FRAGMENT */))
          ]),
          createBaseVNode("ul", _hoisted_15$r, [
            _cache[2] || (_cache[2] = createBaseVNode("a", {
              href: "javascript:history.back()",
              class: "nav-item nav-link text-muted"
            }, [
              createBaseVNode("i", { class: "fas fa-arrow-left" })
            ], -1 /* CACHED */)),
            createBaseVNode("a", {
              target: "_newtab",
              href: _ctx.help_link,
              class: "nav-item nav-link text-muted"
            }, [...(_cache[1] || (_cache[1] = [
              createBaseVNode("i", { class: "fas fa-question-circle" }, null, -1 /* CACHED */)
            ]))], 8 /* PROPS */, _hoisted_16$r)
          ])
        ])
      ]))
    }

    script$2K.render = render$k;
    script$2K.__file = "http_src/vue/page-navbar.vue";

    var script$2J = defineComponent({
        components: {
        },
        props: {
    	id: String,
    	global: Boolean,
    	no_close_button: Boolean,
        },
        emits: [],
        /** This method is the first method of the component called, it's called before html template creation. */
        created() {
        },
        data() {
    	return {
    	    show_alert: false,
    	    i18n: (t) => i18n(t),
    	    body: "",
    	    alert_type: "alert-success",
    	};
        },
        /** This method is the first method called after html template creation. */
        mounted() {
    	if (this.global == true) {
    	    ntopng_events_manager.on_custom_event(this.$props["id"], ntopng_custom_events.SHOW_GLOBAL_ALERT_INFO, (info) => {
    		if (info.type != null) {
    		    this.alert_type = info.type;
    		}
    		if (info.timeout != null) {
    		    setTimeout(() => { this.close(); }, 1000 * info.timeout);
    		}
    		this.show(info.text_html);
    	    });	
    	}
        },
        methods: {
    	close: function() {
    	    this.show_alert = false;
    	},
    	show: function(body, alert_type) {
    	    this.show_alert = true;
    	    this.body = body;
    	    if (alert_type != null) {
    		this.alert_type = alert_type;
    	    }
    	},
        },
    });

    const _hoisted_1$2s = ["innerHTML"];

    function render$j(_ctx, _cache, $props, $setup, $data, $options) {
      return (_ctx.show_alert == true)
        ? (openBlock(), createElementBlock("div", {
            key: 0,
            style: {"width":"100%"},
            class: normalizeClass(["alert alert-dismissable", _ctx.alert_type])
          }, [
            createBaseVNode("span", { innerHTML: _ctx.body }, null, 8 /* PROPS */, _hoisted_1$2s),
            (!_ctx.no_close_button)
              ? (openBlock(), createElementBlock("button", {
                  key: 0,
                  type: "button",
                  onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.close && _ctx.close(...args))),
                  class: "btn-close",
                  "aria-label": "Close"
                }))
              : createCommentVNode("v-if", true)
          ], 2 /* CLASS */))
        : createCommentVNode("v-if", true)
    }

    script$2J.render = render$j;
    script$2J.__file = "http_src/vue/alert-info.vue";

    var script$2I = {
    	components: {
    	},
    	props: {
    		id: String,
    		chart_type: String,
    		register_on_status_change: Boolean,
    		not_emit_global_status_update: Boolean,
    		base_url_request: String,
    		get_params_url_request: Function,
    		get_custom_chart_options: Function,
    		map_chart_options: Function,
    		min_time_interval_id: String,
    		round_time: Boolean, //if min_time_interval_id != null round time by min_time_interval_id	
    	},
    	emits: ["apply", "hidden", "showed", "chart_reloaded", "zoom"],
    	/** This method is the first method of the component called, it's called before html template creation. */
    	created() {
    	},
    	beforeUnmount() {
    		this.chart.destroyChart();
    	},
    	data() {
    		return {
    			chart: null,
    			chart_options: null,
    			from_zoom: false,
    			//i18n: (t) => i18n(t),
    		};
    	},
    	/** This method is the first method called after html template creation. */
    	async mounted() {
    		await this.init();
    		ntopng_sync.ready(this.$props["id"]);
    	},
    	methods: {
    		init: async function () {
    			let status = ntopng_status_manager.get_status();
    			let url_request = this.get_url_request(status);
    			if (this.register_on_status_change) {
    				this.register_status(status);
    			}
    			await this.draw_chart(url_request);
    		},
    		get_data_uri: async function (options) {
    			if (this.chart == null) { return null; }
    			let data_uri = await this.chart.to_data_uri();
    			return data_uri;
    		},
    		download_chart_png: async function (file_name, options) {
    			if (this.chart == null) { return; }
    			let data_uri = await this.chart.to_data_uri();
    			downloadURI(data_uri, file_name);
    		},
    		register_status: function (status) {
    			let url_request = this.get_url_request(status);
    			ntopng_status_manager.on_status_change(this.id, (new_status) => {
    				if (this.from_zoom == true) {
    					this.from_zoom = false;
    					//return;
    				}
    				let new_url_request = this.get_url_request(new_status);
    				if (new_url_request == url_request) {
    					url_request = new_url_request;
    					return;
    				}
    				url_request = new_url_request;
    				this.update_chart(new_url_request);
    			}, false);
    		},
    		get_url_request: function (status) {
    			let url_params;
    			if (this.get_params_url_request != null) {
    				if (status == null) {
    					status = ntopng_status_manager.get_status();
    				}
    				status = {
    					id: this.$props.id,
    					...status
    				};
    				url_params = this.get_params_url_request(status);
    			} else {
    				url_params = ntopng_url_manager$1.get_url_params();
    			}

    			return `${this.base_url_request}?${url_params}`;
    		},
    		draw_chart: async function (url_request) {
    			let chartApex = ntopChartApex;
    			let chart_type = this.chart_type;
    			if (chart_type == null) {
    				chart_type = chartApex.typeChart.TS_STACKED;
    			}
    			this.chart = chartApex.newChart(chart_type);
    			let me = this;
    			this.chart.registerEvent("beforeZoom", function (chart_context, axis) {
    				me.on_before_zoom(chart_context, axis);
    			});
    			this.chart.registerEvent("zoomed", function (chart_context, axis) {
    				me.on_zoomed(chart_context, axis);
    			});
    			let chart_options = await this.get_chart_options(url_request);
                this.chart.drawChart(this.$refs["chart"], chart_options);
    		},
    		map_tooltip: async function(options) {
    			if (options?.tooltip?.y?.formatter) {
    				options.tooltip.y.formatter = DEFINED_TOOLTIP[options.tooltip.y.formatter] 
    					|| options.tooltip.y.formatter;
    			}
    			return options;
    		},
    		update_chart: async function (url_request) {
    			if (url_request == null) {
    				url_request = this.get_url_request();
    			}
    			let chart_options = await this.get_chart_options(url_request);
                if (chart_options?.series) {
                    this.update_chart_series(chart_options.series);
                } else {
    			    this.chart.updateChart(chart_options);
                }
    		},
    		update_chart_options: function (chart_options) {
    			this.chart.updateChart(chart_options);
    		},
    		update_chart_series: function (series) {
    			if (series == null) { return; }
    			this.chart.updateSeries(series);
    		},
    		get_chart_options: async function (url_request) {
    			let chart_options;
    			if (this.get_custom_chart_options == null) {
    				chart_options = await ntopng_utility$1.http_request(url_request);
    			} else {
    				chart_options = await this.get_custom_chart_options(url_request);
    			}
    			if (this.map_chart_options != null) {
    				chart_options = this.map_chart_options(chart_options);
    			} else {
    				chart_options = this.map_tooltip(chart_options);
    			}
    			this.$emit('chart_reloaded', chart_options);
    			return chart_options;
    		},
    		on_before_zoom: function (chart_context, { xaxis, yaxis }) {
    			let new_epoch_status = this.get_epoch_from_xaxis_event(xaxis);
    			if (this.min_time_interval_id != null) {
    				const min_time_interval = ntopng_utility$1.get_timeframe_from_timeframe_id(this.min_time_interval_id);
    				if (new_epoch_status.epoch_end - new_epoch_status.epoch_begin < min_time_interval) {

    					new_epoch_status.epoch_end = new_epoch_status.epoch_end + min_time_interval;
    					new_epoch_status.epoch_end = new_epoch_status.epoch_end - (new_epoch_status.epoch_end % min_time_interval);
    					new_epoch_status.epoch_begin = new_epoch_status.epoch_end - min_time_interval;
    				}
    				if (this.round_time == true) {
    					new_epoch_status.epoch_begin = ntopng_utility$1.round_time_by_timeframe_id(new_epoch_status.epoch_begin, this.min_time_interval_id);
    					new_epoch_status.epoch_end = ntopng_utility$1.round_time_by_timeframe_id(new_epoch_status.epoch_end, this.min_time_interval_id);
    				}

    				xaxis.min = new_epoch_status.epoch_begin * 1000;
    				xaxis.max = new_epoch_status.epoch_end * 1000;
    			}
    			return xaxis;
    		},
    		on_zoomed: function (chart_context, { xaxis, yaxis }) {
    			this.from_zoom = true;
    			const new_epoch_status = this.get_epoch_from_xaxis_event(xaxis);
    			// the timestamps are in milliseconds, convert them into seconds
    			if (!this.not_emit_global_status_update) {
    				ntopng_events_manager$1.emit_event(ntopng_events.EPOCH_CHANGE, new_epoch_status, this.id);
    			}
    			this.$emit('zoom', new_epoch_status);
    		},
    		get_epoch_from_xaxis_event: function (xaxis) {
    			const begin = moment(xaxis.min);
    			const end = moment(xaxis.max);
    			let new_epoch_status = { epoch_begin: Number.parseInt(begin.unix()), epoch_end: Number.parseInt(end.unix()) };
    			return new_epoch_status;
    		}
    	},
    };

    const _hoisted_1$2r = {
      style: {"width":"100%"},
      ref: "chart"
    };

    function render$i(_ctx, _cache, $props, $setup, $data, $options) {
      return (openBlock(), createElementBlock("div", _hoisted_1$2r, null, 512 /* NEED_PATCH */))
    }

    script$2I.render = render$i;
    script$2I.__file = "http_src/vue/chart.vue";

    const _hoisted_1$2q = ["multiple", "disabled"];
    const _hoisted_2$2d = ["selected", "value", "disabled"];
    const _hoisted_3$1$ = ["label"];
    const _hoisted_4$1W = ["selected", "value", "disabled"];


    var script$2H = {
      __name: 'select-search',
      props: {
        id: String,
        options: Array,
        selected_option: Object,
        selected_options: Array,
        multiple: Boolean,
        add_tag: Boolean,
        disable_change: Boolean,
        theme: String,
        dropdown_size: String,
        disabled: Boolean
    },
      emits: ['update:selected_option', 'update:selected_options', 'select_option', 'unselect_option', 'change_selected_options'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const select2 = ref(null);

    // const selected2_option = ref({});

    const emit = __emit;

    const options_2 = ref([]);
    const groups_options_2 = ref([]);
    const selected_option_2 = ref({});
    const selected_values = ref([]); // used only if multiple == true
    const refresh_options = ref(0);

    const props = __props;

    let first_time_render = true;

    onMounted(() => {
        if (!props.options) { return; }
        if (!props.disable_change || !first_time_render) {
        	set_input();
        }
    });

    watch(() => props.selected_option, (cur_value, old_value) => {
        set_selected_option(cur_value);
        change_select_2_selected_value();
    }, { flush: 'pre'});

    watch(() => props.selected_options, (cur_value, old_value) => {
        set_selected_values();
        change_select_2_selected_value();
    }, { flush: 'pre'});


    watch([refresh_options], (cur_value, old_value) => {
        render();
    }, { flush: 'post'});


    watch(() => props.options, (current_value, old_value) => {
        if (props.disable_change == true || current_value == null) { return; }
        set_input();
    }, { flush: 'pre'});


    function set_input() {
        set_options();
        set_selected_option();
        set_selected_values();
    }

    function set_options() {
        options_2.value = [];
        groups_options_2.value = [];
        
        if (props.options == null) { return; }
        let groups_dict = {};
        props.options.forEach((option) => {
    	let opt_2 = { ...option };
    	if (opt_2.value == null) {
    	    opt_2.value = opt_2.label;
    	}
    	if (option.group == null) {
    	    options_2.value.push(opt_2);
    	} else {
    	    if (groups_dict[option.group] == null) {
    		groups_dict[option.group] = { group: opt_2.group, options: [] };
    	    }
    	    groups_dict[option.group].options.push(opt_2);
    	}
        });
        groups_options_2.value = ntopng_utility.object_to_array(groups_dict);
        refresh_options.value += 1;
    }


    const render = () => {
        let select2Div = select2.value;
        if (first_time_render == false) {
    	destroy();
        }
        if (!$(select2Div).hasClass("select2-hidden-accessible")) {
    	$(select2Div).select2({
    	    width: '100%',
    	    theme: props.theme ? props.theme : 'bootstrap-5',
    	    dropdownParent: $(select2Div).parent(),
    	    dropdownAutoWidth : true,
            tags: props.add_tag && !props.multiple,
            selectionCssClass: props.dropdown_size == "small" ? 'select2--small' : '',
            dropdownCssClass: props.dropdown_size == "small" ? 'select2--small' : ''
    	});
    	$(select2Div).on('select2:select', function (e) {
            let data = e.params.data;
                if (data.element === null) {
                    //TODO: implement for multiselect
                    let option = { label: data.text, value: data.id };
    		emit('update:selected_option', option);
    		emit('select_option', option);
                    return;
                } 
    	    let value = data.element._value;
    	    let option = find_option_from_value_or_label(value);
    	    if (value !== props.selected_option) {
    		emit('update:selected_option', option);
    		emit('select_option', option);
                }
    	    if (!props.multiple) {
    		return;
    	    }
    	    selected_values.value = selected_values.value.filter((v) => v != value);
    	    selected_values.value.push(value);
    	    let options = find_options_from_values(selected_values.value);
    	    emit('update:selected_options', options);
    	    emit('change_selected_options', options);		 
    	});
    	$(select2Div).on('select2:unselect', function (e) {
    	    let data = e.params.data;
    	    let value = data.element._value;
    	    if (!props.multiple) {
    		return;
    	    }
    	    selected_values.value = selected_values.value.filter((v) => v != value);
    	    let option = find_option_from_value_or_label(value);
    	    let options = find_options_from_values(selected_values.value);
    	    emit('unselect_option', option);
    	    emit('update:selected_options', options);
    	    emit('change_selected_options', options);		 
    	});
        }
        first_time_render = false;
        // this.$forceUpdate();
        change_select_2_selected_value();
    };

    function change_select_2_selected_value() {
        let select2Div = select2.value;
        if (!props.multiple) {
    	let value = get_value_from_selected_option(props.selected_option);
    	$(select2Div).val(value);
    	$(select2Div).trigger("change");
        } else {
    	$(select2Div).val(selected_values.value);
    	$(select2Div).trigger("change");
        }
    }

    function is_selected(item) {
        if (!props.multiple) {
    	const is_zero_value = selected_option_2.value.value == 0 ||selected_option_2.value.value == "0";
    	return item.value == selected_option_2.value.value || (is_zero_value && item.label == selected_option_2.value.label);
    }
       return selected_values.value.find((v) => v == item.value) != null || item.selected; 
    }

    function set_selected_values() {
        if (props.selected_options == null || !props.multiple) {
            return;
        }
        selected_values.value = [];
        props.selected_options.forEach((opt) => {
            let value = opt.value || opt.label;
            selected_values.value.push(value);
        });
    }

    function set_selected_option(selected_option) {
        if (selected_option == null && !props.multiple) {
    	selected_option = get_props_selected_option();
        }
        selected_option_2.value = selected_option;
    }

    function get_props_selected_option() {
        if (props.selected_option == null) {
    	return props.options[0];
        }
        return props.selected_option;
    }

    function get_value_from_selected_option(selected_option) {
        if (selected_option == null) {
    	selected_option = get_props_selected_option();
        }
        let value;
        if (selected_option.value != null) {
    	value = selected_option.value;
        } else {
    	value = selected_option.label;
        }
        return value;
    }

    function find_options_from_values(values) {
        let options = values.map((v) => find_option_from_value_or_label(v));
        return options;
    }

    function find_option_from_value_or_label(value) {
        let option_2 = find_option_2_from_value(value);
        let option = props.options.find((o) => (o.value === option_2.value) || (o.label == option_2.label));
        return option;
    }

    function find_option_2_from_value(value) {
        if (value == null) {
    	value = get_value_from_selected_option();
        }
        // let option = options_2.value.find((o) => o.value == value);
        let option = options_2.value.find((o) => o.value === value);
        if (option != null) { return option; }
        for (let i = 0; i < groups_options_2.value.length; i += 1) {
    	let g = groups_options_2.value[i];
    	option = g.options.find((o) => o.value === value);
    	if (option != null) {
    	    return option;
    	}
        }
        return null;
    }
        
    __expose({ render });

    function destroy() {
        try {
    	$(select2.value).select2('destroy');
    	$(select2.value).off('select2:select');    
        } catch(err) {
    	console.error("Destroy select-search catch error:");
    	console.error(err);
        }
    }

    onBeforeUnmount(() => {
        destroy();
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("select", {
        class: "select2 form-select",
        ref_key: "select2",
        ref: select2,
        required: "",
        name: "filter_type",
        multiple: __props.multiple,
        disabled: __props.disabled
      }, [
        (openBlock(true), createElementBlock(Fragment, null, renderList(options_2.value, (item, i) => {
          return (openBlock(), createElementBlock("option", {
            class: "no-wrap p-0",
            selected: is_selected(item),
            value: item.value,
            disabled: item.disabled
          }, toDisplayString(item.label), 9 /* TEXT, PROPS */, _hoisted_2$2d))
        }), 256 /* UNKEYED_FRAGMENT */)),
        (openBlock(true), createElementBlock(Fragment, null, renderList(groups_options_2.value, (item, i) => {
          return (openBlock(), createElementBlock("optgroup", {
            label: item.group
          }, [
            (openBlock(true), createElementBlock(Fragment, null, renderList(item.options, (opt, j) => {
              return (openBlock(), createElementBlock("option", {
                selected: is_selected(opt),
                value: opt.value,
                disabled: opt.disabled
              }, toDisplayString(opt.label), 9 /* TEXT, PROPS */, _hoisted_4$1W))
            }), 256 /* UNKEYED_FRAGMENT */))
          ], 8 /* PROPS */, _hoisted_3$1$))
        }), 256 /* UNKEYED_FRAGMENT */))
      ], 8 /* PROPS */, _hoisted_1$2q))
    }
    }

    };

    script$2H.__file = "http_src/vue/select-search.vue";

    var script$2G = {
        components: {
            'select-search': script$2H,
        },
        props: {
            id: String,
            enable_refresh: Boolean,
            disabled_date_picker: Boolean,
            min_time_interval_id: String,
            round_time: Boolean, //if min_time_interval_id != null round time by min_time_interval_id
            custom_time_interval_list: Array,
            custom_change_select_time: Function,
        },
        computed: {
            // a computed getter
            invalid_date_message: function () {
                if (this.wrong_date) {
                    return this.i18n('wrong_date_range');
                }
                else if (this.wrong_min_interval) {
                    let msg = this.i18n('wrong_min_interval');
                    msg.replace('%time_interval', this.i18n(`show_alerts.presets.${this.min_time_interval_id}`));

                    return msg
                }
            }
        },
        watch: {
            "enable_refresh": function (val, oldVal) {
                if (val == true) {
                    this.start_refresh();
                } else if (this.refresh_interval != null) {
                    clearInterval(this.refresh_interval);
                    this.refresh_interval = null;
                }
            },
            "min_time_interval_id": function () {
                // todo
            },
            "round_time": function () {
                // todo
            },
        },
        emits: ["epoch_change"],
        /** This method is the first method of the component called, it's called before html template creation. */
        created() {
        },
        beforeMount() {
            if (this.$props.custom_time_interval_list != null) {
                this.time_preset_list = this.$props.custom_time_interval_list;
            }
            // filter interval
            if (this.min_time_interval_id == null) {
                this.time_preset_list_filtered = this.time_preset_list;
                return;
            }
            const timeframes_dict = this.get_timeframes_available();
            const min_time_interval = timeframes_dict[this.min_time_interval_id];
            this.time_preset_list_filtered = this.time_preset_list.filter((elem) => {
                if (elem.value == "custom") {
                    return true;
                }
                return min_time_interval == null || timeframes_dict[elem.value] >= min_time_interval;
            });
        },
        /** This method is the first method called after html template creation. */
        mounted() {
            let epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
            let epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");
            if (epoch_begin != null && epoch_end != null) {
                // update the status

                this.emit_epoch_change({ epoch_begin: Number.parseInt(epoch_begin), epoch_end: Number.parseInt(epoch_end) }, this.$props.id, true);
            }
            let me = this;
            let f_set_picker = (picker, var_name) => {
                return flatpickr($(this.$refs[picker]), {
                    enableTime: true,
                    dateFormat: "d/m/Y H:i",
                    //altInput: true,
                    //dateFormat: "YYYY-MM-DD HH:mm",
                    //altFormat: "d-m-Y H:i",
                    //locale: "it",
                    time_24hr: true,
                    clickOpens: true,
                    //mode: "range",
                    //static: true,
                    onChange: function (selectedDates, dateStr, instance) {
                        me.enable_apply = true;
                        me.wrong_date = me.flat_begin_date.selectedDates[0].getTime() > me.flat_end_date.selectedDates[0].getTime();
                        me.wrong_min_interval = me.min_time_interval_id && me.get_utc_seconds((me.flat_end_date.selectedDates[0].getTime() - me.flat_begin_date.selectedDates[0].getTime()) < ntopng_utility$1.get_timeframe_from_timeframe_id(me.min_time_interval_id));
                        //me.a[data] = d;
                    },
                });
            };
            this.flat_begin_date = f_set_picker("begin-date");
            this.flat_end_date = f_set_picker("end-date");
            ntopng_events_manager$1.on_event_change(this.$props.id, ntopng_events.EPOCH_CHANGE, (new_status) => this.on_status_updated(new_status), true);

            // notifies that component is ready
            //console.log(this.$props["id"]);
            ntopng_sync.ready(this.$props["id"]);
            if (this.enable_refresh) {
                this.start_refresh();
            }
        },

        /** Methods of the component. */
        methods: {
            start_refresh: function () {
                this.refresh_interval = setInterval(() => {
                    let value = this.selected_time_option?.value;
                    if (this.enable_refresh && value != null && value != "custom") {
                        this.update_from_interval = true;
                        this.change_select_time(true);
                    }
                }, this.refresh_interval_seconds * 1000);
                // }, 10* 1000);
            },
            on_status_updated: function (status) {
                if (!status.epoch_begin && !status.epoch_end) {
                    // First iteration, choose from the time_preset_list based on the currently_active parameter
                    const default_selected = this.time_preset_list.find((elem) => (elem.currently_active === true));
                    if (default_selected) {
                        let s_values = this.get_timeframes_available();
                        let interval_s = s_values[default_selected.value];
                        status.epoch_end = this.get_utc_seconds(Date.now());
                        status.epoch_begin = status.epoch_end - interval_s;
                    }
                }

                let end_date_time_utc = Date.now();
                // default begin date time now - 30 minutes
                let begin_date_time_utc = end_date_time_utc - 30 * 60 * 1000;
                if (status.epoch_end != null && status.epoch_begin != null
                    && Number.parseInt(status.epoch_end) >= Number.parseInt(status.epoch_begin)) {
                    status.epoch_begin = Number.parseInt(status.epoch_begin);
                    status.epoch_end = Number.parseInt(status.epoch_end);
                    end_date_time_utc = status.epoch_end * 1000;
                    begin_date_time_utc = status.epoch_begin * 1000;
                } else {
                    status.epoch_end = this.get_utc_seconds(end_date_time_utc);
                    status.epoch_begin = this.get_utc_seconds(begin_date_time_utc);
                    ntopng_url_manager$1.add_obj_to_url(status);
                    this.emit_epoch_change(status, this.$props.id);
                }
                // this.flat_begin_date.setDate(new Date(status.epoch_begin * 1000));
                // this.flat_end_date.setDate(new Date(status.epoch_end * 1000));
                this.flat_begin_date.setDate(formatterUtils.utc_s_to_server_date(status.epoch_begin));
                this.flat_end_date.setDate(formatterUtils.utc_s_to_server_date(status.epoch_end));
                // this.set_date_time("begin-date", begin_date_time_utc, false);
                // this.set_date_time("begin-time", begin_date_time_utc, true);
                // this.set_date_time("end-date", end_date_time_utc, false);
                // this.set_date_time("end-time", end_date_time_utc, true);
                this.set_select_time_value(begin_date_time_utc, end_date_time_utc);
                this.epoch_status = { epoch_begin: status.epoch_begin, epoch_end: status.epoch_end };
                if (this.update_from_interval == false) {
                    this.add_status_in_history(this.epoch_status);
                }
                this.enable_apply = false;
                this.update_from_interval = false;
                ntopng_url_manager$1.add_obj_to_url(this.epoch_status);
            },
            set_select_time_value: function (begin_utc, end_utc) {
                const timeframes_dict = this.get_timeframes_available();
                const tolerance = 60;
                let now = this.get_utc_seconds(Date.now());
                if (this.round_time == true && this.min_time_interval_id != null) {
                    now = this.round_time_by_min_interval(now);
                }
                const end_utc_s = this.get_utc_seconds(end_utc);
                const begin_utc_s = this.get_utc_seconds(begin_utc);

                if (this.is_between(end_utc_s, now, tolerance)) {
                    this.select_time_value = null;
                    for (let time_id in timeframes_dict) {
                        if (this.is_between(begin_utc_s, now - timeframes_dict[time_id], tolerance)) {
                            this.select_time_value = time_id;
                        }
                    }
                    if (this.select_time_value == null) {
                        this.select_time_value = "custom";
                    }
                } else {
                    this.select_time_value = "custom";
                }
                this.time_preset_list_filtered.forEach(element => {
                    element.currently_active = false;
                    if (element.value == this.select_time_value) {
                        this.selected_time_option = element;
                        element.currently_active = true;
                    }
                });
            },
            apply: function () {
                // let date_begin = this.$refs["begin-date"].valueAsDate;
                // let d_time_begin = this.$refs["begin-time"].valueAsDate;
                // date_begin.setHours(d_time_begin.getHours());
                // date_begin.setMinutes(d_time_begin.getMinutes() + d_time_begin.getTimezoneOffset());
                // date_begin.setSeconds(d_time_begin.getSeconds());

                // let date_end = this.$refs["end-date"].valueAsDate;
                // let d_time_end = this.$refs["end-time"].valueAsDate;
                // date_end.setHours(d_time_end.getHours());
                // date_end.setMinutes(d_time_end.getMinutes() + d_time_end.getTimezoneOffset());
                // date_end.setSeconds(d_time_end.getSeconds());
                // let epoch_begin = this.get_utc_seconds(date_begin.valueOf());
                // let epoch_end = this.get_utc_seconds(date_end.valueOf());
                let now_s = this.get_utc_seconds(Date.now());
                let begin_date = formatterUtils.server_date_to_date(this.flat_begin_date.selectedDates[0]);
                let epoch_begin = this.get_utc_seconds(begin_date.getTime());
                let end_date = formatterUtils.server_date_to_date(this.flat_end_date.selectedDates[0]);
                let epoch_end = this.get_utc_seconds(end_date.getTime());
                if (epoch_end > now_s) {
                    epoch_end = now_s;
                }
                let status = { epoch_begin, epoch_end };
                this.emit_epoch_change(status);
            },
            // set_date_time: function(ref_name, utc_ts, is_time) {
            //     utc_ts = this.get_utc_seconds(utc_ts) * 1000;        
            //     let date_time = new Date(utc_ts);
            //     date_time.setMinutes(date_time.getMinutes() - date_time.getTimezoneOffset());
            //     if (is_time) {
            // 	this.$refs[ref_name].value = date_time.toISOString().substring(11,16);
            //     } else {
            // 	this.$refs[ref_name].value = date_time.toISOString().substring(0,10);
            //     }
            // },
            change_select_time: function (refresh_data) {
                let epoch_end;
                let epoch_begin;
                if (this.$props.custom_change_select_time) {
                    [epoch_begin, epoch_end] = this.$props.custom_change_select_time(this.selected_time_option.value);
                } else {
                    let s_values = this.get_timeframes_available();
                    let interval_s = s_values[this.selected_time_option.value];
                    epoch_end = this.get_utc_seconds(Date.now());
                    epoch_begin = epoch_end - interval_s;
                }
                let status = { epoch_begin: epoch_begin, epoch_end: epoch_end, timeframe_id: this.selected_time_option.value, refresh_data };
                this.emit_epoch_change(status);
            },
            get_timeframes_available: function () {
                const timeframes_dict = ntopng_utility$1.get_timeframes_dict();
                const timeframes_ids = this.time_preset_list.map((ts) => ts.value);
                let timeframes_available = {};
                timeframes_ids.forEach((tf_id) => {
                    timeframes_available[tf_id] = timeframes_dict[tf_id];
                });
                return timeframes_available;
            },
            get_utc_seconds: function (utc_ms) {
                return ntopng_utility$1.get_utc_seconds(utc_ms);
            },
            is_between: function (x, y, tolerance) {
                return x >= y - tolerance && x <= y;
            },
            zoom: function (scale) {
                if (this.epoch_status == null) { return; }
                let interval = (this.epoch_status.epoch_end - this.epoch_status.epoch_begin) / scale;
                let center = (this.epoch_status.epoch_end / 2 + this.epoch_status.epoch_begin / 2);
                this.epoch_status.epoch_begin = center - interval / 2;
                this.epoch_status.epoch_end = center + interval / 2;
                let now = this.get_utc_seconds(Date.now());
                if (this.epoch_status.epoch_end > now) {
                    this.epoch_status.epoch_end = now;
                }
                this.epoch_status.epoch_end = Number.parseInt(this.epoch_status.epoch_end);
                this.epoch_status.epoch_begin = Number.parseInt(this.epoch_status.epoch_begin);
                if (this.epoch_status.epoch_begin == this.epoch_status.epoch_end) {
                    this.epoch_status.epoch_begin -= 2;
                }
                this.emit_epoch_change(this.epoch_status);
            },
            jump_time_back: function () {
                if (this.epoch_status == null) { return; }
                const min = 60;
                this.epoch_status.epoch_begin -= (30 * min);
                this.epoch_status.epoch_end -= (30 * min);
                this.emit_epoch_change(this.epoch_status);
            },
            jump_time_ahead: function () {
                if (this.epoch_status == null) { return; }
                const min = 60;
                let previous_end = this.epoch_status.epoch_end;
                let now = this.get_utc_seconds(Date.now());

                this.epoch_status.epoch_end += (30 * min);
                if (this.epoch_status.epoch_end > now) {
                    this.epoch_status.epoch_end = now;
                }
                this.epoch_status.epoch_begin += (this.epoch_status.epoch_end - previous_end);
                this.emit_epoch_change(this.epoch_status);
            },
            emit_epoch_change: function (epoch_status, id, emit_only_global_event) {
                if (epoch_status.epoch_end == null || epoch_status.epoch_begin == null) { return; }            this.wrong_date = false;
                if (epoch_status.epoch_begin > epoch_status.epoch_end) {
                    this.wrong_date = true;
                    return;
                }
                if (this.min_time_interval_id && this.round_time == true) {
                    epoch_status.epoch_begin = this.round_time_by_min_interval(epoch_status.epoch_begin);
                    epoch_status.epoch_end = this.round_time_by_min_interval(epoch_status.epoch_end);
                }

                if (id != this.id) {
                    this.on_status_updated(epoch_status);
                }
                ntopng_events_manager$1.emit_event(ntopng_events.EPOCH_CHANGE, epoch_status, this.id);
                if (emit_only_global_event) {
                    return;
                }
                this.$emit("epoch_change", epoch_status);
            },
            round_time_by_min_interval: function (ts) {
                return ntopng_utility$1.round_time_by_timeframe_id(ts, this.min_time_interval_id);
            },
            add_status_in_history: function (epoch_status) {
                this.history_last_status = this.history[this.history.length - 1];
                if (this.history.length > 5) {
                    this.history.shift();
                }
                this.history.push(epoch_status);
            },

            apply_status_by_history: function () {
                if (this.history_last_status == null) { return; }
                this.history.pop();
                this.history.pop();
                this.emit_epoch_change(this.history_last_status);
            },
        },
        /**
           Private date of vue component.
        */
        data() {
            return {
                i18n: (t) => i18n(t),
                //status_id: "date-time-range-picker" + this.$props.id,
                epoch_status: null,
                refresh_interval: null,
                refresh_interval_seconds: 60,
                update_from_interval: false,
                history: [],
                history_last_status: null,
                enable_apply: false,
                select_time_value: "5_min",
                selected_time_option: { value: "5_min", label: i18n('show_alerts.presets.5_min'), currently_active: false },
                wrong_date: false,
                wrong_min_interval: false,
                flat_begin_date: null,
                flat_end_date: null,
                time_preset_list: [
                    { value: "5_min", label: i18n('show_alerts.presets.5_min'), currently_active: false },
                    { value: "10_min", label: i18n('show_alerts.presets.10_min'), currently_active: false },
                    { value: "30_min", label: i18n('show_alerts.presets.30_min'), currently_active: true },
                    { value: "hour", label: i18n('show_alerts.presets.hour'), currently_active: false },
                    { value: "2_hours", label: i18n('show_alerts.presets.2_hours'), currently_active: false },
                    { value: "6_hours", label: i18n('show_alerts.presets.6_hours'), currently_active: false },
                    { value: "12_hours", label: i18n('show_alerts.presets.12_hours'), currently_active: false },
                    { value: "day", label: i18n('show_alerts.presets.day'), currently_active: false },
                    { value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
                    { value: "month", label: i18n('show_alerts.presets.month'), currently_active: false },
                    { value: "year", label: i18n('show_alerts.presets.year'), currently_active: false },
                    { value: "custom", label: i18n('show_alerts.presets.custom'), currently_active: false, disabled: true, },
                ],
                time_preset_list_filtered: [],
            };
        },
    };

    const _hoisted_1$2p = { class: "input-group" };
    const _hoisted_2$2c = { class: "form-group" };
    const _hoisted_3$1_ = { class: "controls" };
    const _hoisted_4$1V = { class: "btn-group me-auto btn-group-sm flex-wrap d-flex" };
    const _hoisted_5$1D = { class: "btn-group ms-2" };
    const _hoisted_6$1n = ["disabled"];
    const _hoisted_7$1g = ["disabled"];
    const _hoisted_8$15 = ["title"];
    const _hoisted_9$W = { class: "d-flex align-items-center ms-2" };
    const _hoisted_10$Q = ["disabled"];
    const _hoisted_11$K = { class: "btn-group" };
    const _hoisted_12$F = ["disabled", "title"];

    function render$h(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_select_search = resolveComponent("select-search");

      return (openBlock(), createElementBlock("div", _hoisted_1$2p, [
        createBaseVNode("div", _hoisted_2$2c, [
          createBaseVNode("div", _hoisted_3$1_, [
            createBaseVNode("div", _hoisted_4$1V, [
              renderSlot(_ctx.$slots, "begin"),
              createBaseVNode("div", null, [
                createVNode(_component_select_search, {
                  disabled: $props.disabled_date_picker,
                  selected_option: $data.selected_time_option,
                  "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => (($data.selected_time_option) = $event)),
                  id: 'time_preset_range_picker',
                  options: $data.time_preset_list_filtered,
                  onSelect_option: _cache[1] || (_cache[1] = $event => ($options.change_select_time(null)))
                }, null, 8 /* PROPS */, ["disabled", "selected_option", "options"])
              ]),
              createBaseVNode("div", _hoisted_5$1D, [
                createBaseVNode("input", {
                  disabled: $props.disabled_date_picker,
                  class: "flatpickr flatpickr-input form-control",
                  type: "text",
                  placeholder: "Choose a date..",
                  "data-id": "datetime",
                  ref: "begin-date",
                  style: {"width":"10rem"}
                }, null, 8 /* PROPS */, _hoisted_6$1n),
                createCommentVNode(" <input ref=\"begin-date\" @change=\"enable_apply=true\" @change=\"change_begin_date\" type=\"date\" class=\"date_time_input begin-timepicker form-control border-right-0 fix-safari-input\"> "),
                createCommentVNode(" <input ref=\"begin-time\" @change=\"enable_apply=true\" type=\"time\" class=\"date_time_input begin-timepicker form-control border-right-0 fix-safari-input\"> "),
                _cache[5] || (_cache[5] = createBaseVNode("span", { class: "input-group-text" }, [
                  createBaseVNode("i", { class: "fas fa-long-arrow-alt-right" })
                ], -1 /* CACHED */)),
                createBaseVNode("input", {
                  disabled: $props.disabled_date_picker,
                  class: "flatpickr flatpickr-input form-control",
                  type: "text",
                  placeholder: "Choose a date..",
                  "data-id": "datetime",
                  ref: "end-date",
                  style: {"width":"10rem"}
                }, null, 8 /* PROPS */, _hoisted_7$1g),
                createCommentVNode(" <input ref=\"end-date\" @change=\"enable_apply=true\" type=\"date\" class=\"date_time_input end-timepicker form-control border-left-0 fix-safari-input\" style=\"width: 2.5rem;\"> "),
                createCommentVNode(" <input ref=\"end-time\" @change=\"enable_apply=true\" type=\"time\" class=\"date_time_input end-timepicker form-control border-left-0 fix-safari-input\"> "),
                withDirectives(createBaseVNode("span", {
                  title: $options.invalid_date_message,
                  style: {"margin-left":"0.2rem","color":"red"}
                }, [...(_cache[4] || (_cache[4] = [
                  createBaseVNode("i", { class: "fas fa-exclamation-circle" }, null, -1 /* CACHED */)
                ]))], 8 /* PROPS */, _hoisted_8$15), [
                  [vShow, $data.wrong_date || $data.wrong_min_interval]
                ])
              ]),
              createBaseVNode("div", _hoisted_9$W, [
                createBaseVNode("button", {
                  disabled: !$data.enable_apply || $data.wrong_date || $data.wrong_min_interval,
                  onClick: _cache[2] || (_cache[2] = (...args) => ($options.apply && $options.apply(...args))),
                  type: "button",
                  class: "btn btn-sm btn-primary"
                }, toDisplayString($data.i18n('apply')), 9 /* TEXT, PROPS */, _hoisted_10$Q),
                createBaseVNode("div", _hoisted_11$K, [
                  createBaseVNode("button", {
                    disabled: $data.select_time_value == 'custom' || $props.disabled_date_picker,
                    onClick: _cache[3] || (_cache[3] = $event => ($options.change_select_time())),
                    type: "button",
                    class: "btn btn-sm btn-link",
                    "data-bs-toggle": "tooltip",
                    "data-bs-placement": "top",
                    title: $data.i18n('date_time_range_picker.btn_refresh')
                  }, [...(_cache[6] || (_cache[6] = [
                    createBaseVNode("i", { class: "fas fa-sync" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_12$F),
                  renderSlot(_ctx.$slots, "extra_buttons")
                ])
              ])
            ])
          ])
        ])
      ]))
    }

    function styleInject(css, ref) {
      if ( ref === void 0 ) ref = {};
      var insertAt = ref.insertAt;

      if (!css || typeof document === 'undefined') { return; }

      var head = document.head || document.getElementsByTagName('head')[0];
      var style = document.createElement('style');
      style.type = 'text/css';

      if (insertAt === 'top') {
        if (head.firstChild) {
          head.insertBefore(style, head.firstChild);
        } else {
          head.appendChild(style);
        }
      } else {
        head.appendChild(style);
      }

      if (style.styleSheet) {
        style.styleSheet.cssText = css;
      } else {
        style.appendChild(document.createTextNode(css));
      }
    }

    var css_248z$D = "\n.date_time_input[data-v-6ae24a28] {\n    width: 10.5rem;\n    max-width: 10.5rem;\n    min-width: 10.5rem;\n}\n";
    styleInject(css_248z$D);

    script$2G.render = render$h;
    script$2G.__scopeId = "data-v-6ae24a28";
    script$2G.__file = "http_src/vue/date-time-range-picker.vue";

    var script$2F = defineComponent({
        components: {
        },
        props: {
            id: String,
            size: Number, /* Number from 0 to 3, the higher the larger */
        },
        emits: ["hidden", "showed", "closeModal", "openModal"],
        /** This method is the first method of the component called, it's called before html template creation. */
        created() {
        },
        data() {
            return {
                modal_size: "modal-lg"
                //i18n: (t) => i18n(t),
            };
        },
        /** This method is the first method called after html template creation. */
        mounted() {
            if (this.$props["size"]) {
                const tmp_size = this.$props["size"];
                if (tmp_size === 0) {
                    this.modal_size = "modal-sm";
                } else if (tmp_size === 1) {
                    this.modal_size = "";
                } else if (tmp_size === 2) {
                    this.modal_size = "modal-lg";
                } else if (tmp_size === 3) {
                    this.modal_size = "modal-xl";
                } else {
                    this.modal_size = "modal-lg";
                }
            } else {
                this.modal_size = "modal-lg";
            }
            let me = this;
            $(this.$refs["modal_id"]).on('shown.bs.modal', function (e) {
                me.$emit("showed");
            });
            $(this.$refs["modal_id"]).on('hidden.bs.modal', function (e) {
                me.$emit("hidden");
            });
            // notifies that component is ready
            ntopng_sync.ready(this.$props["id"]);
        },
        methods: {
            show: function () {
                $(this.$refs["modal_id"]).modal("show");
                // emit openmodal to disable the autorefresh on vs page.
                this.$emit("openModal");
            },
            preventEnter: function () { },
            close: function () {
                $(this.$refs["modal_id"]).modal("hide");
                // emit closemodal to enable (eventually if autorefresh variable is true)
                // the autorefresh on vs page.
                this.$emit("closeModal");
            },
        },
    });

    const _hoisted_1$2o = { class: "modal-content" };
    const _hoisted_2$2b = { class: "modal-header" };
    const _hoisted_3$1Z = { class: "modal-title" };
    const _hoisted_4$1U = { class: "modal-body" };
    const _hoisted_5$1C = { class: "modal-footer" };

    function render$g(_ctx, _cache, $props, $setup, $data, $options) {
      return (openBlock(), createElementBlock("div", {
        onSubmit: _cache[0] || (_cache[0] = withModifiers((...args) => (_ctx.preventEnter && _ctx.preventEnter(...args)), ["prevent"])),
        class: "modal fade",
        ref: "modal_id",
        tabindex: "-1",
        role: "dialog",
        "aria-labelledby": "dt-add-filter-modal-title",
        "aria-hidden": "true"
      }, [
        createBaseVNode("div", {
          class: normalizeClass(["modal-dialog modal-dialog-centered", _ctx.modal_size]),
          role: "document"
        }, [
          createBaseVNode("div", _hoisted_1$2o, [
            createBaseVNode("div", _hoisted_2$2b, [
              createBaseVNode("h5", _hoisted_3$1Z, [
                renderSlot(_ctx.$slots, "title")
              ]),
              _cache[1] || (_cache[1] = createBaseVNode("div", { class: "modal-close ms-auto" }, [
                createBaseVNode("button", {
                  type: "button",
                  class: "btn-close",
                  "data-bs-dismiss": "modal",
                  "aria-label": "Close"
                })
              ], -1 /* CACHED */))
            ]),
            createBaseVNode("div", _hoisted_4$1U, [
              renderSlot(_ctx.$slots, "body")
            ]),
            createBaseVNode("div", _hoisted_5$1C, [
              _cache[2] || (_cache[2] = createBaseVNode("div", { class: "mr-auto" }, null, -1 /* CACHED */)),
              renderSlot(_ctx.$slots, "footer"),
              _cache[3] || (_cache[3] = createBaseVNode("div", {
                class: "alert alert-info test-feedback w-100",
                style: {"display":"none"}
              }, null, -1 /* CACHED */))
            ])
          ])
        ], 2 /* CLASS */)
      ], 544 /* NEED_HYDRATION, NEED_PATCH */))
    }

    script$2F.render = render$g;
    script$2F.__file = "http_src/vue/modal.vue";

    /*
     * (C) 2013-23 - ntop.org
     */

    /* ****************************************************** */

    const regexes = {
        ipv4: String.raw`^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`,
        ipv6: String.raw`^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$`,
        mac_address: String.raw`^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$`,
        comma_separted_port_regex: String.raw`^(\d{1,5})(,\s*\d{1,5})*$`,
        port_range_regex: String.raw`^(\d{1,5})-(\d{1,5})$`,
        host_name: String.raw`^(?!\s*$)[a-zA-Z0-9._: \-\/]{1,250}|^[a-zA-Z0-9._: \-\/]{1,250}@[0-9]{0,5}`,
        singleword: String.raw`^(?=[a-zA-Z0-9._:\-]{3,253}$)(?!.*[_.:\-]{2})[^_.:\-].*[^_.:\-]$`,
        url: String.raw`^(https?\:\/\/[^\/\s]+(\/.*)?)$`,
    };

    /* ****************************************************** */

    const validateIP = (ip) => {
        return (validateIPv4(ip) || validateIPv6(ip));
    };

    /* ****************************************************** */

    function validateUInt32(number) {
        const val = Number(number);
        return Number.isInteger(val) && val >= 0 && val <= 0xFFFFFFFF;
    }

    /* ****************************************************** */

    const validateNumber = (number) => {
        return (!isNaN(parseFloat(number)) && isFinite(number));
    };

    /* ****************************************************** */

    const validateCIDR = (network) => {
        const network_split = network.split('/');
        if (network_split.length == 2) {
            const subnet = network_split[1];
            const ip = network_split[0];
            const num = Number(subnet);
            if (validateIPv4(ip) || validateIPv6(ip)) {
                return (!isNaN(num) && num >= 1 && num <= 128 && Number.isInteger(num));
            }
        }
        return false;
    };

    /* ****************************************************** */

    const validateIPv4 = (ip) => {
        const ipv4 = new RegExp(regexes.ipv4);

        return ipv4.test(ip);
    };

    /* ****************************************************** */

    const validateIPv6 = (ip) => {
        const ipv6 = new RegExp(regexes.ipv6);

        return ipv6.test(ip);
    };

    const validateHostName = (host_name) => {
        const host_name_regexp = new RegExp(regexes.host_name);

        return host_name_regexp.test(host_name);
    };

    /* ****************************************************** */

    const validateCommaSeparatedPortList = (ports) => {
        const port_list = new RegExp(regexes.comma_separted_port_regex);

        return port_list.test(ports);
    };

    const validateSingleWord = (word) => {
        const singelWordRegex = new RegExp(regexes.singleword);

        return singelWordRegex.test(word);
    };

    const validateURL = (url) => {
        const urlRegex = new RegExp(regexes.url);

        return urlRegex.test(url);
    };

    const validateMAC = (mac_address) => {
        const macRegex = new RegExp(regexes.mac_address);

        return macRegex.test(mac_address);
    };

    /* ****************************************************** */

    const validatePortRange = (ports) => {
        const port_array = ports.split("-", 2);
        const low = Number(port_array[0]);
        const high = Number(port_array[1]);

        if ((isNaN(low)) || (isNaN(high)))
            return false;

        if ((low > 0) && (low < high) && (high < 65536))
            return true;
        else
            return false;
    };

    const Utils = NtopUtils$1;
    function get_data_pattern(type) {
        if (type == "text") {
            return `.*`;
        } else if (type == "vlan") {
            let vlan = String.raw`@(([1-9])|([1-9][0-9]{1,2})|([1-3][0-9]{3})|(40[0-8][0-9])|(409[0-5]))`;
            return vlan;
        } else if (type == "ip" || type == "cidr") {
            let vlan = get_data_pattern("vlan");
            let r_ipv4 = Utils.REGEXES.ipv4;
            let r_ipv4_vlan = r_ipv4.replaceAll("$", `${vlan}$`);
            let r_ipv6 = Utils.REGEXES.ipv6;
            let r_ipv6_vlan = r_ipv6.replaceAll("$", `${vlan}$`);
            if (type == "cidr") {
                let network_ipv4 = String.raw`(\/(([1-9])|([1-2][0-9])|(3[0-2])))`;
                let ipv4_cidr = r_ipv4.replaceAll("$", `${network_ipv4}$`);
                let ipv4_cidr_vlan = r_ipv4.replaceAll("$", `${network_ipv4}${vlan}$`);
                let network_ipv6 = String.raw`(\/(([1-9])|([1-9][0-9])|(1[0-1][0-9])|(12[0-8])))`;
                let ipv6_cidr = r_ipv6.replaceAll("$", `${network_ipv6}$`);
                let ipv6_cidr_vlan = r_ipv6.replaceAll("$", `${network_ipv6}${vlan}$`);
                return `(${ipv4_cidr}|${ipv4_cidr_vlan}|${ipv6_cidr}|${ipv6_cidr_vlan})`;
            }
            return `(${r_ipv4})|(${r_ipv4_vlan})|(${r_ipv6})|(${r_ipv6_vlan})`;
        } else if (type == "mac") {
            return Utils.REGEXES["macAddress"];
        } else if (type == "ip,cidr") {
            let ip = get_data_pattern("ip");
            let cidr = get_data_pattern("cidr");
            return `(${ip})|(${cidr})`;
        }
        return Utils.REGEXES[type];
    }

    const regexValidation = function () {
        return {
            get_data_pattern,
            validateIP,
            validateIPv4,
            validateIPv6,
            validateHostName,
            validateCommaSeparatedPortList,
            validatePortRange,
            validateSingleWord,
            validateURL,
            validateCIDR,
            validateMAC,
            validateNumber,
            validateUInt32,
        };
    }();

    var script$2E = {
        components: {
            'modal': script$2F,
            'select-search': script$2H,
        },
        props: {
            id: String,
            filters_options: Array,
        },
        updated() {
        },
        data() {
            return {
                i18n: (t) => i18n(t),
                jQuery: $,
                id_modal: `${this.$props.id}_modal`,
                filter_type_selected: {},
                filter_type_label_selected: null,
                operator_selected: {},
                option_selected: {},
                input_value: null,
                input_type: null,
                data_pattern_selected: null,
                input_required: false,
                options_to_show: null,
                operators_to_show: [],
            };
        },
        emits: ["apply"],
        created() { },
        /** This method is the first method called after html template creation. */
        async mounted() {
            await ntopng_sync.on_ready(this.id_modal);
            ntopng_events_manager.on_custom_event(this.$props["id"], ntopng_custom_events.SHOW_MODAL_FILTERS, (filter) => this.show(filter));
            // notifies that component is ready
            ntopng_sync.ready(this.$props["id"]);
        },
        methods: {
            show: function (filter) {
                if (this.$props.filters_options == null || this.$props.filters_options.length == 0) {
                    return;
                }
                if (filter != null) {
                    this.filter_type_selected = filter;
                    this.change_filter(filter);
                } else {
                    this.filter_type_selected = this.$props.filters_options[0];
                    this.change_filter();
                }
                this.$refs["modal"].show();
            },
            post_change: function (filter, value_type) {
                if (filter.id && this.$props.filters_options) {
                    /* Filter type selected, e.g. Alert Type, Application, ecc. */
                    this.filter_type_selected = this.$props.filters_options.find((fo) => fo.id == filter.id);
                }
                if (filter.value != null) {
                    /* Filter selected for the type, e.g. DNS, ICMP, ecc. */
                    if (this.options_to_show) {
                        this.option_selected = this.options_to_show.find((fo) => fo.value == filter.value);
                    } else {
                        this.option_selected = {};
                        this.input_value = filter.value;
                        this.data_pattern_selected = this.get_data_pattern(value_type);
                    }
                }
                if (filter.operator && this.operators_to_show) {
                    /* Operator filter selected, e.g. =, !=, ecc. */
                    this.operator_selected = this.operators_to_show.find((fo) => fo.id == filter.operator);
                }
            },
            change_operator_type: function (selected_operator_type) {
                this.operator_selected = selected_operator_type;
            },
            change_data_filter: function (selected_filter) {
                this.option_selected = selected_filter;
            },
            change_input_value: function (input) {
                this.input_value = input;
            },
            change_filter: function (selected_filter) {
                this.options_to_show = null;
                this.option_selected = null;
                this.input_value = null;
                let filters_options = this.$props.filters_options;
                /* Search the filter selected */
                let filter = filters_options.find((fo) => fo.id == this.filter_type_selected.id);
                if (filter == null) {
                    return;
                }
                /* Set the correct filters to display */
                this.input_type = filter.type;
                this.operators_to_show = filter.operators;
                this.filter_type_label_selected = filter.label;
                if (this.input_type == 'select') {
                    this.options_to_show = filter.options.sort((a, b) => {
                        if (a == null || a.label == null) { return -1; }
                        if (b == null || b.label == null) { return 1; }
                        return a.label.toString().localeCompare(b.label.toString());
                    });
                    this.option_selected = this.options_to_show[0];
                } else if (this.input_type == 'select-with-input') {
                    this.options_to_show = filter.options?.sort((a, b) => {
                        if (a == null || a.label == null) { return -1; }
                        if (b == null || b.label == null) { return 1; }
                        return a.label.toString().localeCompare(b.label.toString());
                    });
                    this.option_selected = this.options_to_show[0];
                    this.data_pattern_selected = this.get_data_pattern(filter.value_type);
                } else {
                    this.options_to_show = null;
                    this.data_pattern_selected = this.get_data_pattern(filter.value_type);
                }

                if (filter.operators && this.operator_selected?.id == null) {
                    this.operator_selected = filter.operators[0];
                }

                if (selected_filter != null) {
                    this.post_change(selected_filter, filter.value_type);
                }
            },
            get_data_pattern: function (value_type) {
                this.input_required = true;
                if (value_type == "text") {
                    this.input_required = false;
                    return `.*`;
                }
                return regexValidation.get_data_pattern(value_type);

                // else if (value_type == "ip") {
                // 	let r_ipv4 = NtopUtils.REGEXES.ipv4;
                // 	let r_ipv4_vlan = r_ipv4.replace("$", "@[0-9]{0,5}$");
                // 	let r_ipv6 = NtopUtils.REGEXES.ipv6;
                // 	let r_ipv6_vlan = r_ipv6.replaceAll("$", "@[0-9]{0,5}$");
                // 	return `(${r_ipv4})|(${r_ipv4_vlan})|(${r_ipv6})|(${r_ipv6_vlan})`;
                // } else if (value_type == "cidr") {
                // 	console.log("test");
                // }

                // return NtopUtils.REGEXES[value_type];
            },
            check_disable_apply: function () {
                let regex = new RegExp(this.data_pattern_selected);
                let disable_apply = !this.options_to_show && (
                    (this.input_required && (this.input_value == null || this.input_value == ""))
                    || (regex.test(this.input_value) == false)
                );
                return disable_apply;
            },
            apply: function () {
                let value = this.input_value;
                let value_label = this.input_value;
                if (value == null || (this.option_selected?.value != null)) {
                    let filter = this.filters_options.find((fo) => fo.id == this.filter_type_selected.id);
                    let option = filter?.options?.find((o) => o.value == this.option_selected.value);
                    if (option == null) {
                        option = this.option_selected;
                    }
                    if (!this.$refs["operator"]) {
                        value = "";
                    } else {
                        value = option.value;
                        value_label = option.value_label || option.label;
                    }
                }
                let params = {
                    id: this.filter_type_selected.id,
                    label: this.filter_type_label_selected,
                    operator: this.operator_selected.id,
                    value: value,
                    value_label: value_label,
                };
                this.$emit("apply", params);
                ntopng_events_manager.emit_custom_event(ntopng_custom_events.MODAL_FILTERS_APPLY, params);
                this.close();
            },
            close: function () {
                this.$refs["modal"].close();
            },
        },
    };

    const _hoisted_1$2n = { autocomplete: "off" };
    const _hoisted_2$2a = { class: "form-group row" };
    const _hoisted_3$1Y = { class: "col-sm-8" };
    const _hoisted_4$1T = { class: "dt-filter-template-container form-group row" };
    const _hoisted_5$1B = { class: "col-form-label col-sm-3" };
    const _hoisted_6$1m = { class: "col-sm-8" };
    const _hoisted_7$1f = { class: "input-group mb-3" };
    const _hoisted_8$14 = { class: "input-group-prepend col-sm-3" };
    const _hoisted_9$V = {
      key: 0,
      class: "col-sm-9"
    };
    const _hoisted_10$P = {
      key: 1,
      class: "col-sm-9"
    };
    const _hoisted_11$J = ["pattern", "required"];
    const _hoisted_12$E = {
      style: {"margin":"0px","padding":"0"},
      class: "alert invalid-feedback"
    };
    const _hoisted_13$y = ["disabled"];

    function render$f(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_select_search = resolveComponent("select-search");
      const _component_modal = resolveComponent("modal");

      return (openBlock(), createBlock(_component_modal, {
        id: $data.id_modal,
        ref: "modal"
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString($data.i18n('alerts_dashboard.add_filter')), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("form", _hoisted_1$2n, [
            createBaseVNode("div", _hoisted_2$2a, [
              _cache[7] || (_cache[7] = createBaseVNode("label", {
                class: "col-form-label col-sm-3",
                for: "dt-filter-type-select"
              }, [
                createBaseVNode("b", null, "Filter")
              ], -1 /* CACHED */)),
              createBaseVNode("div", _hoisted_3$1Y, [
                createVNode(_component_select_search, {
                  selected_option: $data.filter_type_selected,
                  "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => (($data.filter_type_selected) = $event)),
                  id: 'filter_type',
                  options: $props.filters_options,
                  onSelect_option: _cache[1] || (_cache[1] = $event => ($options.change_filter()))
                }, null, 8 /* PROPS */, ["selected_option", "options"])
              ])
            ]),
            _cache[8] || (_cache[8] = createBaseVNode("hr", null, null, -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1T, [
              createBaseVNode("label", _hoisted_5$1B, [
                createBaseVNode("b", null, toDisplayString($data.filter_type_label_selected), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_6$1m, [
                createBaseVNode("div", _hoisted_7$1f, [
                  createBaseVNode("div", _hoisted_8$14, [
                    createVNode(_component_select_search, {
                      selected_option: $data.operator_selected,
                      "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => (($data.operator_selected) = $event)),
                      id: 'operator_filter',
                      options: $data.operators_to_show,
                      onSelect_option: $options.change_operator_type
                    }, null, 8 /* PROPS */, ["selected_option", "options", "onSelect_option"])
                  ]),
                  ($data.input_type == 'select')
                    ? (openBlock(), createElementBlock("div", _hoisted_9$V, [
                        createVNode(_component_select_search, {
                          ref: "operator",
                          selected_option: $data.option_selected,
                          "onUpdate:selected_option": _cache[3] || (_cache[3] = $event => (($data.option_selected) = $event)),
                          id: 'data_filter',
                          options: $data.options_to_show,
                          onSelect_option: $options.change_data_filter
                        }, null, 8 /* PROPS */, ["selected_option", "options", "onSelect_option"])
                      ]))
                    : ($data.input_type == 'select-with-input')
                      ? (openBlock(), createElementBlock("div", _hoisted_10$P, [
                          createVNode(_component_select_search, {
                            ref: "operator",
                            selected_option: $data.option_selected,
                            "onUpdate:selected_option": _cache[4] || (_cache[4] = $event => (($data.option_selected) = $event)),
                            id: 'data_filter',
                            add_tag: true,
                            pattern: $data.data_pattern_selected,
                            options: $data.options_to_show,
                            onSelect_option: $options.change_data_filter
                          }, null, 8 /* PROPS */, ["selected_option", "pattern", "options", "onSelect_option"])
                        ]))
                      : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
                          ($data.operator_selected?.id != 'empty' && $data.operator_selected?.id != 'nempty')
                            ? withDirectives((openBlock(), createElementBlock("input", {
                                key: 0,
                                "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => (($data.input_value) = $event)),
                                pattern: $data.data_pattern_selected,
                                name: "value",
                                required: $data.input_required,
                                type: "text",
                                class: "form-control"
                              }, null, 8 /* PROPS */, _hoisted_11$J)), [
                                [vModelText, $data.input_value]
                              ])
                            : createCommentVNode("v-if", true),
                          createBaseVNode("span", _hoisted_12$E, toDisplayString($data.i18n('invalid_value')), 1 /* TEXT */)
                        ], 64 /* STABLE_FRAGMENT */))
                ]),
                createCommentVNode(" end div input-group mb-3 ")
              ]),
              createCommentVNode(" end div form-group-row ")
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            disabled: $options.check_disable_apply(),
            onClick: _cache[6] || (_cache[6] = (...args) => ($options.apply && $options.apply(...args))),
            class: "btn btn-primary"
          }, toDisplayString($data.i18n('apply')), 9 /* TEXT, PROPS */, _hoisted_13$y)
        ]),
        _: 1 /* STABLE */
      }, 8 /* PROPS */, ["id"]))
    }

    var css_248z$C = "\ninput~.alert[data-v-3119c83e] {\n    display: none;\n}\ninput:invalid~.alert[data-v-3119c83e] {\n    display: block;\n}\n";
    styleInject(css_248z$C);

    script$2E.render = render$f;
    script$2E.__scopeId = "data-v-3119c83e";
    script$2E.__file = "http_src/vue/modal-filters.vue";

    /*
      (C) 2013-23 - ntop.org
     */

    /*
      Here a list of functions used to check, format data;
      e.g. functions that check if a string is null or empty
     */

    /* This function check if value is not set (null or empty).
     * Do not check for 0 as it may be a valid value. */
    const isEmptyOrNull = (value) => {
      return !!(value == null || value == "");
    };

    /* This function check if value is not set (null or empty).
     * Do not check for 0 as it may be a valid value. */
    const isEmptyString = (value) => {
      return !!(value == null || value === "");
    };

    /* This function check if value is null, or an empty array */
    const isEmptyArrayOrNull = (value) => {
      return !!(value == null || value.length === 0);
    };

    /* ******************************************************************** */

    const dataUtils = function () {
      return {
        isEmptyString,
        isEmptyOrNull,
        isEmptyArrayOrNull,
      };
    }();

    /*
    Tagify v4.35.6 - tags input component
    By: Yair Even-Or <vsync.design@gmail.com>
    https://github.com/yairEO/tagify

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.

    This Software may not be rebranded and sold as a library under any other name
    other than "Tagify" (by owner) or as part of another library.
    */

    var t="&#8203;";function e(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i<e;i++)n[i]=t[i];return n}function i$1(t){return function(t){if(Array.isArray(t))return e(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,i){if(!t)return;if("string"==typeof t)return e(t,i);var n=Object.prototype.toString.call(t).slice(8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Array.from(n);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return e(t,i)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}var n={isEnabled:function(){var t;return null===(t=window.TAGIFY_DEBUG)||void 0===t||t},log:function(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];var s;this.isEnabled()&&(s=console).log.apply(s,["[Tagify]:"].concat(i$1(e)));},warn:function(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];var s;this.isEnabled()&&(s=console).warn.apply(s,["[Tagify]:"].concat(i$1(e)));}},s=function(t,e,i,n){return t=""+t,e=""+e,n&&(t=t.trim(),e=e.trim()),i?t==e:t.toLowerCase()==e.toLowerCase()},a=function(t,e){return t&&Array.isArray(t)&&t.map((function(t){return o(t,e)}))};function o(t,e){var i,n={};for(i in t)e.indexOf(i)<0&&(n[i]=t[i]);return n}function r(t){return (new DOMParser).parseFromString(t.trim(),"text/html").body.firstElementChild}function l(t,e){for(e=e||"previous";t=t[e+"Sibling"];)if(3==t.nodeType)return t}function d$1(t){return "string"==typeof t?t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/`|'/g,"&#039;"):t}function c$1(t){var e=Object.prototype.toString.call(t).split(" ")[1].slice(0,-1);return t===Object(t)&&"Array"!=e&&"Function"!=e&&"RegExp"!=e&&"HTMLUnknownElement"!=e}function u$1(t,e,i){var n,s;function a(t,e){for(var i in e)if(e.hasOwnProperty(i)){if(c$1(e[i])){c$1(t[i])?a(t[i],e[i]):t[i]=Object.assign({},e[i]);continue}if(Array.isArray(e[i])){t[i]=Object.assign([],e[i]);continue}t[i]=e[i];}}return n=t,(null!=(s=Object)&&"undefined"!=typeof Symbol&&s[Symbol.hasInstance]?s[Symbol.hasInstance](n):n instanceof s)||(t={}),a(t,e),i&&a(t,i),t}function g(){var t=[],e={},i=!0,n=!1,s=void 0;try{for(var a,o=arguments[Symbol.iterator]();!(i=(a=o.next()).done);i=!0){var r=a.value,l=!0,d=!1,u=void 0;try{for(var g,h=r[Symbol.iterator]();!(l=(g=h.next()).done);l=!0){var p=g.value;c$1(p)?e[p.value]||(t.push(p),e[p.value]=1):t.includes(p)||t.push(p);}}catch(t){d=!0,u=t;}finally{try{l||null==h.return||h.return();}finally{if(d)throw u}}}}catch(t){n=!0,s=t;}finally{try{i||null==o.return||o.return();}finally{if(n)throw s}}return t}function h$1(t){return String.prototype.normalize?"string"==typeof t?t.normalize("NFD").replace(/[\u0300-\u036f]/g,""):void 0:t}var p$1=function(){return /(?=.*chrome)(?=.*android)/i.test(navigator.userAgent)};function f$1(){return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(function(t){return (t^crypto.getRandomValues(new Uint8Array(1))[0]&15>>t/4).toString(16)}))}function v(t){var e,i=b.call(this,t),n=null==t||null===(e=t.classList)||void 0===e?void 0:e.contains(this.settings.classNames.tag);return i&&n}function m$1(t){return b.call(this,t)&&(null==t?void 0:t.closest(this.settings.classNames.tagSelector))}function b(t){var e;return (null==t||null===(e=t.closest)||void 0===e?void 0:e.call(t,this.settings.classNames.namespaceSelector))===this.DOM.scope}function w(t,e){var i=window.getSelection();return e=e||i.getRangeAt(0),"string"==typeof t&&(t=document.createTextNode(t)),e&&(e.deleteContents(),e.insertNode(t)),t}function y(t,e,i){return t?(e&&(t.__tagifyTagData=i?e:u$1({},t.__tagifyTagData||{},e)),t.__tagifyTagData):(n.warn("tag element doesn't exist",{tagElm:t,data:e}),e)}function T(t){if(t&&t.parentNode){var e=t,i=window.getSelection(),n=i.getRangeAt(0);i.rangeCount&&(n.setStartAfter(e),n.collapse(!0),i.removeAllRanges(),i.addRange(n));}}function O(t,e){t.forEach((function(t){if(y(t.previousSibling)||!t.previousSibling){var i=document.createTextNode("​");t.before(i),e&&T(i);}}));}var D={delimiters:",",pattern:null,tagTextProp:"value",maxTags:1/0,callbacks:{},addTagOnBlur:!0,addTagOn:["blur","tab","enter"],onChangeAfterBlur:!0,duplicates:!1,whitelist:[],blacklist:[],enforceWhitelist:!1,userInput:!0,focusable:!0,focusInputOnRemove:!0,keepInvalidTags:!1,createInvalidTags:!0,mixTagsAllowedAfter:/,|\.|\:|\s/,mixTagsInterpolator:["[[","]]"],backspace:!0,skipInvalid:!1,pasteAsTags:!0,editTags:{clicks:2,keepInvalid:!0},transformTag:function(){},trim:!0,a11y:{focusableTags:!1},mixMode:{insertAfterTag:" "},autoComplete:{enabled:!0,rightKey:!1,tabKey:!1},classNames:{namespace:"tagify",mixMode:"tagify--mix",selectMode:"tagify--select",input:"tagify__input",focus:"tagify--focus",tagNoAnimation:"tagify--noAnim",tagInvalid:"tagify--invalid",tagNotAllowed:"tagify--notAllowed",scopeLoading:"tagify--loading",hasMaxTags:"tagify--hasMaxTags",hasNoTags:"tagify--noTags",empty:"tagify--empty",inputInvalid:"tagify__input--invalid",dropdown:"tagify__dropdown",dropdownWrapper:"tagify__dropdown__wrapper",dropdownHeader:"tagify__dropdown__header",dropdownFooter:"tagify__dropdown__footer",dropdownItem:"tagify__dropdown__item",dropdownItemActive:"tagify__dropdown__item--active",dropdownItemHidden:"tagify__dropdown__item--hidden",dropdownItemSelected:"tagify__dropdown__item--selected",dropdownInital:"tagify__dropdown--initial",tag:"tagify__tag",tagText:"tagify__tag-text",tagX:"tagify__tag__removeBtn",tagLoading:"tagify__tag--loading",tagEditing:"tagify__tag--editable",tagFlash:"tagify__tag--flash",tagHide:"tagify__tag--hide"},dropdown:{classname:"",enabled:2,maxItems:10,searchKeys:["value","searchBy"],fuzzySearch:!0,caseSensitive:!1,accentedSearch:!0,includeSelectedTags:!1,escapeHTML:!0,highlightFirst:!0,closeOnSelect:!0,clearOnSelect:!0,position:"all",appendTarget:null},hooks:{beforeRemoveTag:function(){return Promise.resolve()},beforePaste:function(){return Promise.resolve()},suggestionClick:function(){return Promise.resolve()},beforeKeyDown:function(){return Promise.resolve()}}};function x(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function S(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{},n=Object.keys(i);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(i).filter((function(t){return Object.getOwnPropertyDescriptor(i,t).enumerable})))),n.forEach((function(e){x(t,e,i[e]);}));}return t}function I(t,e){return e=null!=e?e:{},Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(e)):function(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,n);}return i}(Object(e)).forEach((function(i){Object.defineProperty(t,i,Object.getOwnPropertyDescriptor(e,i));})),t}function E(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i<e;i++)n[i]=t[i];return n}function M(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function N(t){return function(t){if(Array.isArray(t))return E(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(!t)return;if("string"==typeof t)return E(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);"Object"===i&&t.constructor&&(i=t.constructor.name);if("Map"===i||"Set"===i)return Array.from(i);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return E(t,e)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function A(){for(var t in this.dropdown={},this._dropdown)this.dropdown[t]="function"==typeof this._dropdown[t]?this._dropdown[t].bind(this):this._dropdown[t];this.dropdown.refs(),this.DOM.dropdown.__tagify=this;}var _,C,k=(_=function(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{},n=Object.keys(i);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(i).filter((function(t){return Object.getOwnPropertyDescriptor(i,t).enumerable})))),n.forEach((function(e){M(t,e,i[e]);}));}return t}({},{events:{binding:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],e=this.dropdown.events.callbacks,i=this.listeners.dropdown=this.listeners.dropdown||{position:this.dropdown.position.bind(this,null),onKeyDown:e.onKeyDown.bind(this),onMouseOver:e.onMouseOver.bind(this),onMouseLeave:e.onMouseLeave.bind(this),onClick:e.onClick.bind(this),onScroll:e.onScroll.bind(this)},n=t?"addEventListener":"removeEventListener";"manual"!=this.settings.dropdown.position&&(document[n]("scroll",i.position,!0),window[n]("resize",i.position),window[n]("keydown",i.onKeyDown)),this.DOM.dropdown[n]("mouseover",i.onMouseOver),this.DOM.dropdown[n]("mouseleave",i.onMouseLeave),this.DOM.dropdown[n]("mousedown",i.onClick),this.DOM.dropdown.content[n]("scroll",i.onScroll);},callbacks:{onKeyDown:function(t){var e=this;if(this.state.hasFocus&&!this.state.composing){var i=this.settings,s=i.dropdown.includeSelectedTags,a=this.DOM.dropdown.querySelector(i.classNames.dropdownItemActiveSelector),o=this.dropdown.getSuggestionDataByNode(a),r="mix"==i.mode,l="select"==i.mode;i.hooks.beforeKeyDown(t,{tagify:this}).then((function(d){switch(t.key){case"ArrowDown":case"ArrowUp":case"Down":case"Up":t.preventDefault();var c=e.dropdown.getAllSuggestionsRefs(),u="ArrowUp"==t.key||"Up"==t.key;a&&(a=e.dropdown.getNextOrPrevOption(a,!u)),a&&a.matches(i.classNames.dropdownItemSelector)||(a=c[u?c.length-1:0]),e.dropdown.highlightOption(a,!0);break;case"PageUp":case"PageDown":var g;t.preventDefault();var h=e.dropdown.getAllSuggestionsRefs(),p=Math.floor(e.DOM.dropdown.content.clientHeight/(null===(g=h[0])||void 0===g?void 0:g.offsetHeight))||1,f="PageUp"===t.key;if(a){var v=h.indexOf(a),m=f?Math.max(0,v-p):Math.min(h.length-1,v+p);a=h[m];}else a=h[0];e.dropdown.highlightOption(a,!0);break;case"Home":case"End":t.preventDefault();var b=e.dropdown.getAllSuggestionsRefs();a=b["Home"===t.key?0:b.length-1],e.dropdown.highlightOption(a,!0);break;case"Escape":case"Esc":e.dropdown.hide();break;case"ArrowRight":if(e.state.actions.ArrowLeft||i.autoComplete.rightKey)return;case"Tab":var w=!i.autoComplete.rightKey||!i.autoComplete.tabKey;if(!r&&!l&&a&&w&&!e.state.editing&&o){t.preventDefault();var y=e.dropdown.getMappedValue(o);return e.state.autoCompleteData=o,e.input.autocomplete.set.call(e,y),!1}return !0;case"Enter":t.preventDefault(),e.state.actions.selectOption=!0,setTimeout((function(){return e.state.actions.selectOption=!1}),100),i.hooks.suggestionClick(t,{tagify:e,tagData:o,suggestionElm:a}).then((function(){if(a){var i=s?a:e.dropdown.getNextOrPrevOption(a,!u);e.dropdown.selectOption(a,t,(function(){if(i){var t=i.getAttribute("value");i=e.dropdown.getSuggestionNodeByValue(t),e.dropdown.highlightOption(i);}}));}else e.dropdown.hide(),r||e.addTags(e.state.inputText.trim(),!0);})).catch((function(t){return n.warn(t)}));break;case"Backspace":if(r||e.state.editing.scope)return;var T=e.input.raw.call(e);""!=T&&8203!=T.charCodeAt(0)||(!0===i.backspace?e.removeTags():"edit"==i.backspace&&setTimeout(e.editTag.bind(e),0));}}));}},onMouseOver:function(t){var e=t.target.closest(this.settings.classNames.dropdownItemSelector);this.dropdown.highlightOption(e);},onMouseLeave:function(t){this.dropdown.highlightOption();},onClick:function(t){var e=this;if(0==t.button&&t.target!=this.DOM.dropdown&&t.target!=this.DOM.dropdown.content){var i=t.target.closest(this.settings.classNames.dropdownItemSelector),s=this.dropdown.getSuggestionDataByNode(i);this.state.actions.selectOption=!0,setTimeout((function(){return e.state.actions.selectOption=!1}),100),this.settings.hooks.suggestionClick(t,{tagify:this,tagData:s,suggestionElm:i}).then((function(){i?e.dropdown.selectOption(i,t):e.dropdown.hide();})).catch((function(t){return n.warn(t)}));}},onScroll:function(t){var e=t.target,i=e.scrollTop/(e.scrollHeight-e.parentNode.clientHeight)*100;this.trigger("dropdown:scroll",{percentage:Math.round(i)});}}},refilter:function(t){t=t||this.state.dropdown.query||"",this.suggestedListItems=this.dropdown.filterListItems(t),this.dropdown.fill(),this.suggestedListItems.length||this.dropdown.hide(),this.trigger("dropdown:updated",this.DOM.dropdown);},getSuggestionDataByNode:function(t){for(var e,i=t&&t.getAttribute("value"),n=this.suggestedListItems.length;n--;){if(c$1(e=this.suggestedListItems[n])&&e.value==i)return e;if(e==i)return {value:e}}},getSuggestionNodeByValue:function(t){return this.dropdown.getAllSuggestionsRefs().find((function(e){return e.getAttribute("value")===t}))},getNextOrPrevOption:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this.dropdown.getAllSuggestionsRefs(),n=i.findIndex((function(e){return e===t}));return e?i[n+1]:i[n-1]},highlightOption:function(t,e){var i,n=this.settings.classNames.dropdownItemActive;if(this.state.ddItemElm&&(this.state.ddItemElm.classList.remove(n),this.state.ddItemElm.removeAttribute("aria-selected")),!t)return this.state.ddItemData=null,this.state.ddItemElm=null,void this.input.autocomplete.suggest.call(this);i=this.dropdown.getSuggestionDataByNode(t),this.state.ddItemData=i,this.state.ddItemElm=t,t.classList.add(n),t.setAttribute("aria-selected",!0),e&&(t.parentNode.scrollTop=t.clientHeight+t.offsetTop-t.parentNode.clientHeight),this.settings.autoComplete&&(this.input.autocomplete.suggest.call(this,i),this.dropdown.position());},selectOption:function(t,e,i){var n=this,s=this.settings,a=s.dropdown.includeSelectedTags,o=s.dropdown,r=o.clearOnSelect,l=o.closeOnSelect;if(!t)return this.addTags(this.state.inputText,!0),void(l&&this.dropdown.hide());e=e||{};var d=t.getAttribute("value"),c="noMatch"==d,g="mix"==s.mode,h=this.suggestedListItems.find((function(t){var e;return (null!==(e=t.value)&&void 0!==e?e:t)==d}));if(this.trigger("dropdown:select",{data:h,elm:t,event:e}),h||c){if(this.state.editing){var p=this.normalizeTags([h])[0];h=s.transformTag.call(this,p)||p,this.onEditTagDone(null,u$1({__isValid:!0},h));}else this[g?"addMixTags":"addTags"]([h||this.input.raw.call(this)],r);(g||this.DOM.input.parentNode)&&(setTimeout((function(){n.DOM.input.focus(),n.toggleFocusClass(!0);})),l&&setTimeout(this.dropdown.hide.bind(this)),a?i&&i():(t.addEventListener("transitionend",(function(){n.dropdown.fillHeaderFooter(),setTimeout((function(){t.remove(),n.dropdown.refilter(),i&&i();}),100);}),{once:!0}),t.classList.add(this.settings.classNames.dropdownItemHidden)));}else l&&setTimeout(this.dropdown.hide.bind(this));},selectAll:function(t){this.suggestedListItems.length=0,this.dropdown.hide(),this.dropdown.filterListItems("");var e=this.dropdown.filterListItems("");return t||(e=this.state.dropdown.suggestions),this.addTags(e,!0),this},filterListItems:function(t,e){var i,n,s,a,o,r,l=function(){var t,l,d=void 0,u=void 0;t=v[T],n=(null!=(l=Object)&&"undefined"!=typeof Symbol&&l[Symbol.hasInstance]?l[Symbol.hasInstance](t):t instanceof l)?v[T]:{value:v[T]};var m,b=!Object.keys(n).some((function(t){return y.includes(t)}))?["value"]:y;g.fuzzySearch&&!e.exact?(a=b.reduce((function(t,e){return t+" "+(n[e]||"")}),"").toLowerCase().trim(),g.accentedSearch&&(a=h$1(a),r=h$1(r)),d=0==a.indexOf(r),u=a===r,m=a,s=r.toLowerCase().split(" ").every((function(t){return m.includes(t.toLowerCase())}))):(d=!0,s=b.some((function(t){var i=""+(n[t]||"");return g.accentedSearch&&(i=h$1(i),r=h$1(r)),g.caseSensitive||(i=i.toLowerCase()),u=i===r,e.exact?i===r:0==i.indexOf(r)}))),o=!g.includeSelectedTags&&i.isTagDuplicate(c$1(n)?n.value:n),s&&!o&&(u&&d?f.push(n):"startsWith"==g.sortby&&d?p.unshift(n):p.push(n));},d=this,u=this.settings,g=u.dropdown,p=(e=e||{},[]),f=[],v=u.whitelist,m=g.maxItems>=0?g.maxItems:1/0,b=g.includeSelectedTags,w="function"==typeof g.sortby,y=g.searchKeys,T=0;if(!(t="select"==u.mode&&this.value.length&&this.value[0][u.tagTextProp]==t?"":t)||!y.length){p=b?v:v.filter((function(t){return !d.isTagDuplicate(c$1(t)?t.value:t)}));var O=w?g.sortby(p,r):p.slice(0,m);return this.state.dropdown.suggestions=O,O}for(r=g.caseSensitive?""+t:(""+t).toLowerCase();T<v.length;T++)i=this,l();this.state.dropdown.suggestions=f.concat(p);O=w?g.sortby(f.concat(p),r):f.concat(p).slice(0,m);return this.state.dropdown.suggestions=O,O},getMappedValue:function(t){var e=this.settings.dropdown.mapValueTo;return e?"function"==typeof e?e(t):t[e]||t.value:t.value},createListHTML:function(t){var e=this;return u$1([],t).map((function(t,i){"string"!=typeof t&&"number"!=typeof t||(t={value:t});var n=e.dropdown.getMappedValue(t);return n="string"==typeof n&&e.settings.dropdown.escapeHTML?d$1(n):n,e.settings.templates.dropdownItem.apply(e,[I(S({},t),{mappedValue:n}),e])})).join("")}}),C=null!=(C={refs:function(){this.DOM.dropdown=this.parseTemplate("dropdown",[this.settings]),this.DOM.dropdown.content=this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-wrapper']");},getHeaderRef:function(){return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-header']")},getFooterRef:function(){return this.DOM.dropdown.querySelector("[data-selector='tagify-suggestions-footer']")},getAllSuggestionsRefs:function(){return N(this.DOM.dropdown.content.querySelectorAll(this.settings.classNames.dropdownItemSelector))},show:function(t){var e,i,n,a=this,o=this.settings,r="mix"==o.mode&&!o.enforceWhitelist,l=!o.whitelist||!o.whitelist.length,d="manual"==o.dropdown.position;if(t=void 0===t?this.state.inputText:t,!(l&&!r&&!o.templates.dropdownItemNoMatch||!1===o.dropdown.enabled||this.state.isLoading||this.settings.readonly)){if(clearTimeout(this.dropdownHide__bindEventsTimeout),this.suggestedListItems=this.dropdown.filterListItems(t),t&&!this.suggestedListItems.length&&(this.trigger("dropdown:noMatch",t),o.templates.dropdownItemNoMatch&&(n=o.templates.dropdownItemNoMatch.call(this,{value:t}))),!n){if(this.suggestedListItems.length)t&&r&&!this.state.editing.scope&&!s(this.suggestedListItems[0].value,t)&&this.suggestedListItems.unshift({value:t});else {if(!t||!r||this.state.editing.scope)return this.input.autocomplete.suggest.call(this),void this.dropdown.hide();this.suggestedListItems=[{value:t}];}i=""+(c$1(e=this.suggestedListItems[0])?e.value:e),o.autoComplete&&i&&0==i.indexOf(t)&&this.input.autocomplete.suggest.call(this,e);}this.dropdown.fill(n),o.dropdown.highlightFirst&&this.dropdown.highlightOption(this.DOM.dropdown.content.querySelector(o.classNames.dropdownItemSelector)),this.state.dropdown.visible||setTimeout(this.dropdown.events.binding.bind(this)),this.state.dropdown.visible=t||!0,this.state.dropdown.query=t,this.setStateSelection(),d||setTimeout((function(){a.dropdown.position(),a.dropdown.render();})),setTimeout((function(){a.trigger("dropdown:show",a.DOM.dropdown);}));}},hide:function(t){var e=this,i=this.DOM,n=i.scope,s=i.dropdown,a="manual"==this.settings.dropdown.position&&!t;if(s&&document.body.contains(s)&&!a)return window.removeEventListener("resize",this.dropdown.position),this.dropdown.events.binding.call(this,!1),n.setAttribute("aria-expanded",!1),s.parentNode.removeChild(s),setTimeout((function(){e.state.dropdown.visible=!1;}),100),this.state.dropdown.query=this.state.ddItemData=this.state.ddItemElm=this.state.selection=null,this.state.tag&&this.state.tag.value.length&&(this.state.flaggedTags[this.state.tag.baseOffset]=this.state.tag),this.trigger("dropdown:hide",s),this},toggle:function(t){this.dropdown[this.state.dropdown.visible&&!t?"hide":"show"]();},getAppendTarget:function(){var t=this.settings.dropdown;return "function"==typeof t.appendTarget?t.appendTarget():t.appendTarget},render:function(){var t,e,i,n=this,s=(t=this.DOM.dropdown,(i=t.cloneNode(!0)).style.cssText="position:fixed; top:-9999px; opacity:0",document.body.appendChild(i),e=i.clientHeight,i.parentNode.removeChild(i),e),a=this.settings,o=this.dropdown.getAppendTarget();return !1===a.dropdown.enabled||(this.DOM.scope.setAttribute("aria-expanded",!0),document.body.contains(this.DOM.dropdown)||(this.DOM.dropdown.classList.add(a.classNames.dropdownInital),this.dropdown.position(s),o.appendChild(this.DOM.dropdown),setTimeout((function(){return n.DOM.dropdown.classList.remove(a.classNames.dropdownInital)})))),this},fill:function(t){t="string"==typeof t?t:this.dropdown.createListHTML(t||this.suggestedListItems);var e,i=this.settings.templates.dropdownContent.call(this,t);this.DOM.dropdown.content.innerHTML=(e=i)?e.replace(/\>[\r\n ]+\</g,"><").split(/>\s+</).join("><").trim():"";},fillHeaderFooter:function(){var t=this.dropdown.filterListItems(this.state.dropdown.query),e=this.parseTemplate("dropdownHeader",[t]),i=this.parseTemplate("dropdownFooter",[t]),n=this.dropdown.getHeaderRef(),s=this.dropdown.getFooterRef();e&&(null==n||n.parentNode.replaceChild(e,n)),i&&(null==s||s.parentNode.replaceChild(i,s));},position:function(t){var e=this.settings.dropdown,i=this.dropdown.getAppendTarget();if("manual"!=e.position&&i){var n,s,a,o,r,l,d,c,u,g,h=this.DOM.dropdown,p=e.RTL,f=i===document.body,v=i===this.DOM.scope,m=f?window.pageYOffset:i.scrollTop,b=document.fullscreenElement||document.webkitFullscreenElement||document.documentElement,w=b.clientHeight,y=Math.max(b.clientWidth||0,window.innerWidth||0),T=y>480?e.position:"all",O=this.DOM["input"==T?"input":"scope"];if(t=t||h.clientHeight,this.state.dropdown.visible){if("text"==T?(a=(n=function(){var t=document.getSelection();if(t.rangeCount){var e,i,n=t.getRangeAt(0),s=n.startContainer,a=n.startOffset;if(a>0)return (i=document.createRange()).setStart(s,a-1),i.setEnd(s,a),{left:(e=i.getBoundingClientRect()).right,top:e.top,bottom:e.bottom};if(s.getBoundingClientRect)return s.getBoundingClientRect()}return {left:-9999,top:-9999}}()).bottom,s=n.top,o=n.left,r="auto"):(l=function(t){var e=0,i=0;for(t=t.parentNode;t&&t!=b;)e+=t.offsetTop||0,i+=t.offsetLeft||0,t=t.parentNode;return {top:e,left:i}}(i),n=O.getBoundingClientRect(),s=v?-1:n.top-l.top,a=(v?n.height:n.bottom-l.top)-1,o=v?-1:n.left-l.left,r=n.width+"px"),!f){var D=function(){for(var t=0,i=e.appendTarget.parentNode;i;)t+=i.scrollTop||0,i=i.parentNode;return t}();s+=D,a+=D;}var x;s=Math.floor(s),a=Math.ceil(a),c=y-o<120,u=((d=null!==(x=e.placeAbove)&&void 0!==x?x:w-n.bottom<t)?s:a)+m,g=o+(p&&n.width||0)+window.pageXOffset,g="text"==T&&c?"right: 0;":"left: ".concat(g,"px;"),h.style.cssText="".concat(g," top: ").concat(u,"px; min-width: ").concat(r,"; max-width: ").concat(r),h.setAttribute("placement",d?"top":"bottom"),h.setAttribute("position",T);}}}})?C:{},Object.getOwnPropertyDescriptors?Object.defineProperties(_,Object.getOwnPropertyDescriptors(C)):function(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,n);}return i}(Object(C)).forEach((function(t){Object.defineProperty(_,t,Object.getOwnPropertyDescriptor(C,t));})),_),L="@yaireo/tagify/",P={empty:"empty",exceed:"number of tags exceeded",pattern:"pattern mismatch",duplicate:"already exists",notAllowed:"not allowed"},j={wrapper:function(e,i){return '<tags class="'.concat(i.classNames.namespace," ").concat(i.mode?"".concat(i.classNames[i.mode+"Mode"]):""," ").concat(e.className,'"\n                    ').concat(i.readonly?"readonly":"","\n                    ").concat(i.disabled?"disabled":"","\n                    ").concat(i.required?"required":"","\n                    ").concat("select"===i.mode?"spellcheck='false'":"",'\n                    tabIndex="-1">\n                    ').concat(this.settings.templates.input.call(this),"\n                ").concat(t,"\n        </tags>")},input:function(){var e=this.settings,i=e.placeholder||t;return "<span ".concat(!e.readonly&&e.userInput?"contenteditable":"",' data-can-editable tabIndex="0" data-placeholder="').concat(i,'" aria-placeholder="').concat(e.placeholder||"",'"\n                    class="').concat(e.classNames.input,'"\n                    role="textbox"\n                    autocapitalize="false"\n                    autocorrect="off"\n                    aria-autocomplete="both"\n                    aria-multiline="').concat("mix"==e.mode,'"></span>')},tag:function(t,e){var i=e.settings;return '<tag title="'.concat(t.title||t.value,"\"\n                    contenteditable='false'\n                    tabIndex=\"").concat(i.a11y.focusableTags?0:-1,'"\n                    class="').concat(i.classNames.tag," ").concat(t.class||"",'"\n                    ').concat(this.getAttributes(t),">\n            <x title='' tabIndex=\"").concat(i.a11y.focusableTags?0:-1,'" class="').concat(i.classNames.tagX,"\" role='button' aria-label='remove tag'></x>\n            <div>\n                <span ").concat("select"===i.mode&&i.userInput?"contenteditable='true'":"",' autocapitalize="false" autocorrect="off" spellcheck=\'false\' class="').concat(i.classNames.tagText,'">').concat(t[i.tagTextProp]||t.value,"</span>\n            </div>\n        </tag>")},dropdown:function(t){var e=t.dropdown,i="manual"==e.position;return '<div class="'.concat(i?"":t.classNames.dropdown," ").concat(e.classname,'" role="listbox" aria-labelledby="dropdown" dir="').concat(e.RTL?"rtl":"","\">\n                    <div data-selector='tagify-suggestions-wrapper' class=\"").concat(t.classNames.dropdownWrapper,'"></div>\n                </div>')},dropdownContent:function(t){var e=this.settings.templates,i=this.state.dropdown.suggestions;return "\n            ".concat(e.dropdownHeader.call(this,i),"\n            ").concat(t,"\n            ").concat(e.dropdownFooter.call(this,i),"\n        ")},dropdownItem:function(t){return "<div ".concat(this.getAttributes(t),"\n                    class='").concat(this.settings.classNames.dropdownItem," ").concat(this.isTagDuplicate(t.value)?this.settings.classNames.dropdownItemSelected:""," ").concat(t.class||"",'\'\n                    tabindex="0"\n                    role="option">').concat(t.mappedValue||t.value,"</div>")},dropdownHeader:function(t){return "<header data-selector='tagify-suggestions-header' class=\"".concat(this.settings.classNames.dropdownHeader,'"></header>')},dropdownFooter:function(t){var e=t.length-this.settings.dropdown.maxItems;return e>0?"<footer data-selector='tagify-suggestions-footer' class=\"".concat(this.settings.classNames.dropdownFooter,'">\n                ').concat(e," more items. Refine your search.\n            </footer>"):""},dropdownItemNoMatch:null};function V(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i<e;i++)n[i]=t[i];return n}function R(t,e){return null!=e&&"undefined"!=typeof Symbol&&e[Symbol.hasInstance]?!!e[Symbol.hasInstance](t):t instanceof e}function F(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var i=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=i){var n,s,a=[],o=!0,r=!1;try{for(i=i.call(t);!(o=(n=i.next()).done)&&(a.push(n.value),!e||a.length!==e);o=!0);}catch(t){r=!0,s=t;}finally{try{o||null==i.return||i.return();}finally{if(r)throw s}}return a}}(t,e)||function(t,e){if(!t)return;if("string"==typeof t)return V(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);"Object"===i&&t.constructor&&(i=t.constructor.name);if("Map"===i||"Set"===i)return Array.from(i);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return V(t,e)}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function H(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i<e;i++)n[i]=t[i];return n}function B(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function W(t,e){return null!=e&&"undefined"!=typeof Symbol&&e[Symbol.hasInstance]?!!e[Symbol.hasInstance](t):t instanceof e}function q(t,e){return e=null!=e?e:{},Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(e)):function(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,n);}return i}(Object(e)).forEach((function(i){Object.defineProperty(t,i,Object.getOwnPropertyDescriptor(e,i));})),t}function U(t){return function(t){if(Array.isArray(t))return H(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(!t)return;if("string"==typeof t)return H(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);"Object"===i&&t.constructor&&(i=t.constructor.name);if("Map"===i||"Set"===i)return Array.from(i);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return H(t,e)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}var K={customBinding:function(){var t=this;this.customEventsList.forEach((function(e){t.on(e,t.settings.callbacks[e]);}));},binding:function(){var t,e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],i=this.settings,n=this.events.callbacks,s=e?"addEventListener":"removeEventListener";if(!(this.state.mainEvents&&e||i.disabled||i.readonly)){for(var a in this.state.mainEvents=e,e&&!this.listeners.main&&(this.events.bindGlobal.call(this),this.settings.isJQueryPlugin&&jQuery(this.DOM.originalInput).on("tagify.removeAllTags",this.removeAllTags.bind(this))),t=this.listeners.main=this.listeners.main||{keydown:["input",n.onKeydown.bind(this)],click:["scope",n.onClickScope.bind(this)],dblclick:"select"!=i.mode&&["scope",n.onDoubleClickScope.bind(this)],paste:["input",n.onPaste.bind(this)],drop:["input",n.onDrop.bind(this)],compositionstart:["input",n.onCompositionStart.bind(this)],compositionend:["input",n.onCompositionEnd.bind(this)]})t[a]&&this.DOM[t[a][0]][s](a,t[a][1]);var o=this.listeners.main.inputMutationObserver||new MutationObserver(n.onInputDOMChange.bind(this));o.disconnect(),"mix"==i.mode&&o.observe(this.DOM.input,{childList:!0}),this.events.bindOriginaInputListener.call(this),e&&(this.listeners.main=void 0);}},bindOriginaInputListener:function(t){var e=(t||0)+500;this.listeners.main&&(clearInterval(this.listeners.main.originalInputValueObserverInterval),this.listeners.main.originalInputValueObserverInterval=setInterval(this.events.callbacks.observeOriginalInputValue.bind(this),e));},bindGlobal:function(t){var e,i=this.events.callbacks,n=t?"removeEventListener":"addEventListener";if(this.listeners&&(t||!this.listeners.global)){this.listeners.global=this.listeners.global||[{type:this.isIE?"keydown":"input",target:this.DOM.input,cb:i[this.isIE?"onInputIE":"onInput"].bind(this)},{type:"keydown",target:window,cb:i.onWindowKeyDown.bind(this)},{type:"focusin",target:this.DOM.scope,cb:i.onFocusBlur.bind(this)},{type:"focusout",target:this.DOM.scope,cb:i.onFocusBlur.bind(this)},{type:"click",target:document,cb:i.onClickAnywhere.bind(this),useCapture:!0}];var s=!0,a=!1,o=void 0;try{for(var r,l=this.listeners.global[Symbol.iterator]();!(s=(r=l.next()).done);s=!0)(e=r.value).target[n](e.type,e.cb,!!e.useCapture);}catch(t){a=!0,o=t;}finally{try{s||null==l.return||l.return();}finally{if(a)throw o}}t&&(this.listeners.global=void 0);}},unbindGlobal:function(){this.events.bindGlobal.call(this,!0);},callbacks:{onFocusBlur:function(t){var e,i,n=this.settings,s=m$1.call(this,t.relatedTarget),a=v.call(this,t.target),o=t.target.classList.contains(n.classNames.tagX),r="focusin"==t.type,l="focusout"==t.type;o&&"mix"!=n.mode&&n.focusInputOnRemove&&this.DOM.input.focus(),s&&r&&!a&&!o&&this.toggleFocusClass(this.state.hasFocus=+new Date);var d=t.target?this.trim(this.DOM.input.textContent):"",c=null===(i=this.value)||void 0===i||null===(e=i[0])||void 0===e?void 0:e[n.tagTextProp],u=n.dropdown.enabled>=0,g={relatedTarget:t.relatedTarget},h=this.state.actions.selectOption&&(u||!n.dropdown.closeOnSelect),p=this.state.actions.addNew&&u;if(l){if(t.relatedTarget===this.DOM.scope)return this.dropdown.hide(),void this.DOM.input.focus();this.postUpdate(),n.onChangeAfterBlur&&this.triggerChangeEvent();}if(!(h||p||o))if(this.state.hasFocus=!(!r&&!s)&&+new Date,this.toggleFocusClass(this.state.hasFocus),"mix"!=n.mode){if(r){if(!n.focusable)return;var f=0===n.dropdown.enabled&&!this.state.dropdown.visible,b=this.DOM.scope.querySelector(this.settings.classNames.tagTextSelector);return this.trigger("focus",g),void(f&&!a&&(this.dropdown.show(this.value.length?"":void 0),"select"===n.mode&&this.setRangeAtStartEnd(!1,b)))}if(l){if(this.trigger("blur",g),this.loading(!1),"select"==n.mode){if(this.value.length){var w=this.getTagElms()[0];d=this.trim(w.textContent);}c===d&&(d="");}d&&!this.state.actions.selectOption&&n.addTagOnBlur&&n.addTagOn.includes("blur")&&this.addTags(d,!0);}s||(this.DOM.input.removeAttribute("style"),this.dropdown.hide());}else r?this.trigger("focus",g):l&&(this.trigger("blur",g),this.loading(!1),this.dropdown.hide(),this.state.dropdown.visible=void 0,this.setStateSelection());},onCompositionStart:function(t){this.state.composing=!0;},onCompositionEnd:function(t){this.state.composing=!1;},onWindowKeyDown:function(t){var e,i=this.settings,n=document.activeElement,s=m$1.call(this,n)&&this.DOM.scope.contains(n),a=n===this.DOM.input,o=s&&n.hasAttribute("readonly"),r=this.DOM.scope.querySelector(this.settings.classNames.tagTextSelector),l=this.state.dropdown.visible;if(("Tab"===t.key&&l||this.state.hasFocus||s&&!o)&&!a){e=n.nextElementSibling;var d=t.target.classList.contains(i.classNames.tagX);switch(t.key){case"Backspace":i.readonly||this.state.editing||(this.removeTags(n),(e||this.DOM.input).focus());break;case"Enter":if(d)return void this.removeTags(t.target.parentNode);i.a11y.focusableTags&&v.call(this,n)&&setTimeout(this.editTag.bind(this),0,n);break;case"ArrowDown":this.state.dropdown.visible||"mix"==i.mode||this.dropdown.show();break;case"Tab":null==r||r.focus();}}},onKeydown:function(t){var e=this,i=this.settings;if(!this.state.composing&&i.userInput){"select"==i.mode&&i.enforceWhitelist&&this.value.length&&"Tab"!=t.key&&t.preventDefault();var n=this.trim(t.target.textContent);this.trigger("keydown",{event:t}),i.hooks.beforeKeyDown(t,{tagify:this}).then((function(s){if("mix"==i.mode){switch(t.key){case"Left":case"ArrowLeft":e.state.actions.ArrowLeft=!0;break;case"Delete":case"Backspace":if(e.state.editing)return;var a=document.getSelection(),o="Delete"==t.key&&a.anchorOffset==(a.anchorNode.length||0),r=a.anchorNode.previousSibling,d=1==a.anchorNode.nodeType||!a.anchorOffset&&r&&1==r.nodeType&&a.anchorNode.previousSibling;!function(t){var e=document.createElement("div");t.replace(/\&#?[0-9a-z]+;/gi,(function(t){return e.innerHTML=t,e.innerText}));}(e.DOM.input.innerHTML);var c,u,g,h=e.getTagElms(),f=1===a.anchorNode.length&&a.anchorNode.nodeValue==String.fromCharCode(8203);if("edit"==i.backspace&&d)return c=1==a.anchorNode.nodeType?null:a.anchorNode.previousElementSibling,setTimeout(e.editTag.bind(e),0,c),void t.preventDefault();if(p$1()&&W(d,Element))return g=l(d),d.hasAttribute("readonly")||d.remove(),e.DOM.input.focus(),void setTimeout((function(){T(g),e.DOM.input.click();}));if("BR"==a.anchorNode.nodeName)return;if((o||d)&&1==a.anchorNode.nodeType?u=0==a.anchorOffset?o?h[0]:null:h[Math.min(h.length,a.anchorOffset)-1]:o?u=a.anchorNode.nextElementSibling:W(d,Element)&&(u=d),3==a.anchorNode.nodeType&&!a.anchorNode.nodeValue&&a.anchorNode.previousElementSibling&&t.preventDefault(),(d||o)&&!i.backspace)return void t.preventDefault();if("Range"!=a.type&&!a.anchorOffset&&a.anchorNode==e.DOM.input&&"Delete"!=t.key)return void t.preventDefault();if("Range"!=a.type&&u&&u.hasAttribute("readonly"))return void T(l(u));"Delete"==t.key&&f&&y(a.anchorNode.nextSibling)&&e.removeTags(a.anchorNode.nextSibling);break;case"Enter":if(t.preventDefault(),e.state.tag)return;var v=window.getSelection();v.getRangeAt(0).insertNode(document.createElement("br")),v.collapseToEnd();}return !0}var m="manual"==i.dropdown.position;switch(t.key){case"Backspace":"select"==i.mode&&i.enforceWhitelist&&e.value.length?e.removeTags():e.state.dropdown.visible&&"manual"!=i.dropdown.position||""!=t.target.textContent&&8203!=n.charCodeAt(0)||(!0===i.backspace?e.removeTags():"edit"==i.backspace&&setTimeout(e.editTag.bind(e),0));break;case"Esc":case"Escape":if(e.state.dropdown.visible)return;t.target.blur();break;case"Down":case"ArrowDown":e.state.dropdown.visible||e.dropdown.show();break;case"ArrowRight":var b=e.state.inputSuggestion||e.state.ddItemData;if(b&&i.autoComplete.rightKey)return void e.addTags([b],!0);break;case"Tab":if(!i.addTagOn.includes(t.key.toLowerCase()))break;case"Enter":if(e.state.dropdown.visible&&!m)return;var w=e.state.autoCompleteData||n;if(!w&&"Tab"===t.key)return !0;t.preventDefault(),setTimeout((function(){e.state.dropdown.visible&&!m||e.state.actions.selectOption||!i.addTagOn.includes(t.key.toLowerCase())||(e.addTags([w],!0),e.state.autoCompleteData=null);}));}})).catch((function(t){return t}));}},onInput:function(t){this.postUpdate();var e=this.settings;if("mix"==e.mode)return this.events.callbacks.onMixTagsInput.call(this,t);var i=this.input.normalize.call(this,void 0,{trim:!1}),n=i.length>=e.dropdown.enabled,s={value:i,inputElm:this.DOM.input},a=this.validateTag({value:i});"select"==e.mode&&this.toggleScopeValidation(a),s.isValid=a,this.state.inputText!=i&&(this.input.set.call(this,i,!1),-1!=i.search(e.delimiters)?this.addTags(i)&&this.input.set.call(this):e.dropdown.enabled>=0&&this.dropdown[n?"show":"hide"](i),this.trigger("input",s));},onMixTagsInput:function(t){var e,i,n,s,a,o,r,l,d=this,c=this.settings,g=this.value.length,h=this.getTagElms(),f=document.createDocumentFragment(),v=window.getSelection().getRangeAt(0),m=[].map.call(h,(function(t){return y(t).value}));if("deleteContentBackward"==t.inputType&&p$1()&&this.events.callbacks.onKeydown.call(this,{target:t.target,key:"Backspace"}),O(this.getTagElms()),this.value.slice().forEach((function(t){t.readonly&&!m.includes(t.value)&&f.appendChild(d.createTagElem(t));})),f.childNodes.length&&(v.insertNode(f),this.setRangeAtStartEnd(!1,f.lastChild)),h.length!=g)return this.value=[].map.call(this.getTagElms(),(function(t){return y(t)})),void this.update({withoutChangeEvent:!0});if(this.hasMaxTags())return !0;if(window.getSelection&&(o=window.getSelection()).rangeCount>0&&3==o.anchorNode.nodeType){if((v=o.getRangeAt(0).cloneRange()).collapse(!0),v.setStart(o.focusNode,0),n=(e=v.toString().slice(0,v.endOffset)).split(c.pattern).length-1,(i=e.match(c.pattern))&&(s=e.slice(e.lastIndexOf(i[i.length-1]))),s){if(this.state.actions.ArrowLeft=!1,this.state.tag={prefix:s.match(c.pattern)[0],value:s.replace(c.pattern,"")},this.state.tag.baseOffset=o.baseOffset-this.state.tag.value.length,l=this.state.tag.value.match(c.delimiters))return this.state.tag.value=this.state.tag.value.replace(c.delimiters,""),this.state.tag.delimiters=l[0],this.addTags(this.state.tag.value,c.dropdown.clearOnSelect),void this.dropdown.hide();a=this.state.tag.value.length>=c.dropdown.enabled;try{r=(r=this.state.flaggedTags[this.state.tag.baseOffset]).prefix==this.state.tag.prefix&&r.value[0]==this.state.tag.value[0],this.state.flaggedTags[this.state.tag.baseOffset]&&!this.state.tag.value&&delete this.state.flaggedTags[this.state.tag.baseOffset];}catch(t){}(r||n<this.state.mixMode.matchedPatternCount)&&(a=!1);}else this.state.flaggedTags={};this.state.mixMode.matchedPatternCount=n;}setTimeout((function(){d.update({withoutChangeEvent:!0}),d.trigger("input",u$1({},d.state.tag,{textContent:d.DOM.input.textContent})),d.state.tag&&d.dropdown[a?"show":"hide"](d.state.tag.value);}),10);},onInputIE:function(t){var e=this;setTimeout((function(){e.events.callbacks.onInput.call(e,t);}));},observeOriginalInputValue:function(){this.DOM.originalInput.parentNode||this.destroy(),this.DOM.originalInput.value!=this.DOM.originalInput.tagifyValue&&this.loadOriginalValues();},onClickAnywhere:function(t){if(t.target!=this.DOM.scope&&!this.DOM.scope.contains(t.target)){this.toggleFocusClass(!1),this.state.hasFocus=!1;var e=t.target.closest(this.settings.classNames.dropdownSelector);(null==e?void 0:e.__tagify)!=this&&this.dropdown.hide();}},onClickScope:function(t){var e=this.settings,i=t.target.closest("."+e.classNames.tag);t.target,this.DOM.scope;var n=+new Date-this.state.hasFocus;if(!t.target.classList.contains(e.classNames.tagX))return i&&!this.state.editing?(this.trigger("click",{tag:i,index:this.getNodeIndex(i),data:y(i),event:t}),void(1!==e.editTags&&1!==e.editTags.clicks&&"select"!=e.mode||this.events.callbacks.onDoubleClickScope.call(this,t))):void(t.target==this.DOM.input&&("mix"==e.mode&&this.fixFirefoxLastTagNoCaret(),n>500||!e.focusable)?this.state.dropdown.visible?this.dropdown.hide():0===e.dropdown.enabled&&"mix"!=e.mode&&this.dropdown.show(this.value.length?"":void 0):"select"!=e.mode||0!==e.dropdown.enabled||this.state.dropdown.visible||(this.events.callbacks.onDoubleClickScope.call(this,q(function(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{},n=Object.keys(i);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(i).filter((function(t){return Object.getOwnPropertyDescriptor(i,t).enumerable})))),n.forEach((function(e){B(t,e,i[e]);}));}return t}({},t),{target:this.getTagElms()[0]})),!e.userInput&&this.dropdown.show()));this.removeTags(t.target.parentNode);},onPaste:function(t){var e=this;t.preventDefault();var i,n,s,a=this.settings;if(!a.userInput)return !1;a.readonly||(n=t.clipboardData||window.clipboardData,s=n.getData("Text"),a.hooks.beforePaste(t,{tagify:this,pastedText:s,clipboardData:n}).then((function(a){void 0===a&&(a=s),a&&(e.injectAtCaret(a,window.getSelection().getRangeAt(0)),"mix"==e.settings.mode?e.events.callbacks.onMixTagsInput.call(e,t):e.settings.pasteAsTags?i=e.addTags(e.state.inputText+a,!0):(e.state.inputText=a,e.dropdown.show(a))),e.trigger("paste",{event:t,pastedText:s,clipboardData:n,tagsElems:i});})).catch((function(t){return t})));},onDrop:function(t){t.preventDefault();},onEditTagInput:function(t,e){var i,n=t.closest("."+this.settings.classNames.tag),s=this.getNodeIndex(n),a=y(n),o=this.input.normalize.call(this,t),r=(B(i={},this.settings.tagTextProp,o),B(i,"__tagId",a.__tagId),i),l=this.validateTag(r);this.editTagChangeDetected(u$1(a,r))||!0!==t.originalIsValid||(l=!0),n.classList.toggle(this.settings.classNames.tagInvalid,!0!==l),a.__isValid=l,n.title=!0===l?a.title||a.value:l,o.length>=this.settings.dropdown.enabled&&(this.state.editing&&(this.state.editing.value=o),this.dropdown.show(o)),this.trigger("edit:input",{tag:n,index:s,data:u$1({},this.value[s],{newValue:o}),event:e});},onEditTagPaste:function(t,e){var i=(e.clipboardData||window.clipboardData).getData("Text");e.preventDefault();var n=w(i);this.setRangeAtStartEnd(!1,n);},onEditTagClick:function(t,e){this.events.callbacks.onClickScope.call(this,e);},onEditTagFocus:function(t){this.state.editing={scope:t,input:t.querySelector("[contenteditable]")};},onEditTagBlur:function(t,e){var i=v.call(this,e.relatedTarget);if("select"==this.settings.mode&&i&&e.relatedTarget.contains(e.target))this.dropdown.hide();else if(this.state.editing&&(this.state.hasFocus||this.toggleFocusClass(),this.DOM.scope.contains(document.activeElement)||this.trigger("blur",{}),this.DOM.scope.contains(t))){var n,s,a,o=this.settings,r=t.closest("."+o.classNames.tag),l=y(r),d=this.input.normalize.call(this,t),c=(B(n={},o.tagTextProp,d),B(n,"__tagId",l.__tagId),n),g=l.__originalData,h=this.editTagChangeDetected(u$1(l,c)),p=this.validateTag(c);if(d)if(h){var f;if(s=this.hasMaxTags(),a=u$1({},g,(B(f={},o.tagTextProp,this.trim(d)),B(f,"__isValid",p),f)),o.transformTag.call(this,a,g),!0!==(p=(!s||!0===g.__isValid)&&this.validateTag(a))){if(this.trigger("invalid",{data:a,tag:r,message:p}),o.editTags.keepInvalid)return;o.keepInvalidTags?a.__isValid=p:a=g;}else o.keepInvalidTags&&(delete a.title,delete a["aria-invalid"],delete a.class);this.onEditTagDone(r,a);}else this.onEditTagDone(r,g);else this.onEditTagDone(r);}},onEditTagkeydown:function(t,e){if(!this.state.composing)switch(this.trigger("edit:keydown",{event:t}),t.key){case"Esc":case"Escape":this.state.editing=!1,!!e.__tagifyTagData.__originalData.value?e.parentNode.replaceChild(e.__tagifyTagData.__originalHTML,e):e.remove();break;case"Enter":case"Tab":t.preventDefault();setTimeout((function(){return t.target.blur()}),0);}},onDoubleClickScope:function(t){var e=t.target.closest("."+this.settings.classNames.tag);if(e){var i,n,s=y(e),a=this.settings;!1!==(null==s?void 0:s.editable)&&(i=e.classList.contains(this.settings.classNames.tagEditing),n=e.hasAttribute("readonly"),a.readonly||i||n||!this.settings.editTags||!a.userInput||(this.events.callbacks.onEditTagFocus.call(this,e),this.editTag(e)),this.toggleFocusClass(!0),"select"!=a.mode&&this.trigger("dblclick",{tag:e,index:this.getNodeIndex(e),data:y(e)}));}},onInputDOMChange:function(t){var e=this;t.forEach((function(t){t.addedNodes.forEach((function(t){if("<div><br></div>"==t.outerHTML)t.replaceWith(document.createElement("br"));else if(1==t.nodeType&&t.querySelector(e.settings.classNames.tagSelector)){var i,n=document.createTextNode("");3==t.childNodes[0].nodeType&&"BR"!=t.previousSibling.nodeName&&(n=document.createTextNode("\n")),(i=t).replaceWith.apply(i,U([n].concat(U(U(t.childNodes).slice(0,-1))))),T(n);}else if(v.call(e,t)){var s;if(3!=(null===(s=t.previousSibling)||void 0===s?void 0:s.nodeType)||t.previousSibling.textContent||t.previousSibling.remove(),t.previousSibling&&"BR"==t.previousSibling.nodeName){t.previousSibling.replaceWith("\n​");for(var a=t.nextSibling,o="";a;)o+=a.textContent,a=a.nextSibling;o.trim()&&T(t.previousSibling);}else t.previousSibling&&!y(t.previousSibling)||t.before("​");}})),t.removedNodes.forEach((function(t){t&&"BR"==t.nodeName&&v.call(e,i)&&(e.removeTags(i),e.fixFirefoxLastTagNoCaret());}));}));var i=this.DOM.input.lastChild;i&&""==i.nodeValue&&i.remove(),i&&"BR"==i.nodeName||this.DOM.input.appendChild(document.createElement("br"));}}};function z(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i<e;i++)n[i]=t[i];return n}function X(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function J(t,e){return null!=e&&"undefined"!=typeof Symbol&&e[Symbol.hasInstance]?!!e[Symbol.hasInstance](t):t instanceof e}function G(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{},n=Object.keys(i);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(i).filter((function(t){return Object.getOwnPropertyDescriptor(i,t).enumerable})))),n.forEach((function(e){X(t,e,i[e]);}));}return t}function $$1(t){return function(t){if(Array.isArray(t))return z(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(!t)return;if("string"==typeof t)return z(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);"Object"===i&&t.constructor&&(i=t.constructor.name);if("Map"===i||"Set"===i)return Array.from(i);if("Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i))return z(t,e)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Q(t,e){if(!t){n.warn("input element not found",t);var i=new Proxy(this,{get:function(){return function(){return i}}});return i}if(t.__tagify)return n.warn("input element is already Tagified - Same instance is returned.",t),t.__tagify;var s;u$1(this,function(t){var e=document.createTextNode(""),i={};function s(t,i,n){n&&i.split(/\s+/g).forEach((function(i){return e[t+"EventListener"].call(e,i,n)}));}return {removeAllCustomListeners:function(){Object.entries(i).forEach((function(t){var e=F(t,2),i=e[0];e[1].forEach((function(t){return s("remove",i,t)}));})),i={};},off:function(t,e){return t&&(e?s("remove",t,e):t.split(/\s+/g).forEach((function(t){var e;null===(e=i[t])||void 0===e||e.forEach((function(e){return s("remove",t,e)})),delete i[t];}))),this},on:function(t,e){return e&&"function"==typeof e&&(t.split(/\s+/g).forEach((function(t){Array.isArray(i[t])?i[t].push(e):i[t]=[e];})),s("add",t,e)),this},trigger:function(i,s,a){var o;if(a=a||{cloneData:!0},i)if(t.settings.isJQueryPlugin)"remove"==i&&(i="removeTag"),jQuery(t.DOM.originalInput).triggerHandler(i,[s]);else {try{var r="object"==typeof s?s:{value:s};if((r=a.cloneData?u$1({},r):r).tagify=this,s.event&&(r.event=this.cloneEvent(s.event)),R(s,Object))for(var l in s)R(s[l],HTMLElement)&&(r[l]=s[l]);o=new CustomEvent(i,{detail:r});}catch(t){n.warn(t);}e.dispatchEvent(o);}}}}(this)),this.isFirefox=/firefox|fxios/i.test(navigator.userAgent)&&!/seamonkey/i.test(navigator.userAgent),this.isIE=window.document.documentMode,e=e||{},this.getPersistedData=(s=e.id,function(t){var e;if(s){var i,n="/"+t;if(1===(null===(e=localStorage)||void 0===e?void 0:e.getItem(L+s+"/v")))try{i=JSON.parse(localStorage[L+s+n]);}catch(t){}return i}}),this.setPersistedData=function(t){var e;return t?(null===(e=localStorage)||void 0===e||e.setItem(L+t+"/v",1),function(e,i){var n,s="/"+i,a=JSON.stringify(e);e&&i&&(null===(n=localStorage)||void 0===n||n.setItem(L+t+s,a),dispatchEvent(new Event("storage")));}):function(){}}(e.id),this.clearPersistedData=function(t){return function(e){var i=L+"/"+t+"/";if(e)localStorage.removeItem(i+e);else for(var n in localStorage)n.includes(i)&&localStorage.removeItem(n);}}(e.id),this.applySettings(t,e),this.state={inputText:"",editing:!1,composing:!1,actions:{},mixMode:{},dropdown:{},flaggedTags:{}},this.value=[],this.listeners={},this.DOM={},this.build(t),A.call(this),this.getCSSVars(),this.loadOriginalValues(),this.events.customBinding.call(this),this.events.binding.call(this),t.autofocus&&this.DOM.input.focus(),t.__tagify=this;}Q.prototype={_dropdown:k,placeCaretAfterNode:T,getSetTagData:y,helpers:{sameStr:s,removeCollectionProp:a,omit:o,isObject:c$1,parseHTML:r,escapeHTML:d$1,extend:u$1,concatWithoutDups:g,getUID:f$1,isNodeTag:v},customEventsList:["change","add","remove","invalid","input","paste","click","keydown","focus","blur","edit:input","edit:beforeUpdate","edit:updated","edit:start","edit:keydown","dropdown:show","dropdown:hide","dropdown:select","dropdown:updated","dropdown:noMatch","dropdown:scroll"],dataProps:["__isValid","__removed","__originalData","__originalHTML","__tagId"],trim:function(t){return this.settings.trim&&t&&"string"==typeof t?t.trim():t},parseHTML:r,templates:j,parseTemplate:function(t,e){return r((t=this.settings.templates[t]||t).apply(this,e))},set whitelist(t){var e=t&&Array.isArray(t);this.settings.whitelist=e?t:[],this.setPersistedData(e?t:[],"whitelist");},get whitelist(){return this.settings.whitelist},set userInput(t){this.settings.userInput=!!t,this.setContentEditable(!!t);},get userInput(){return this.settings.userInput},generateClassSelectors:function(t){var e=function(e){var i=e;Object.defineProperty(t,i+"Selector",{get:function(){return "."+this[i].split(" ")[0]}});};for(var i in t)e(i);},applySettings:function(t,e){var i,n;D.templates=this.templates;var s=u$1({},D,"mix"==e.mode?{dropdown:{position:"text"}}:{}),a=this.settings=u$1({},s,e);if(a.disabled=t.hasAttribute("disabled"),a.readonly=a.readonly||t.hasAttribute("readonly"),a.placeholder=d$1(t.getAttribute("placeholder")||a.placeholder||""),a.required=t.hasAttribute("required"),this.generateClassSelectors(a.classNames),this.isIE&&(a.autoComplete=!1),["whitelist","blacklist"].forEach((function(e){var i=t.getAttribute("data-"+e);i&&J(i=i.split(a.delimiters),Array)&&(a[e]=i);})),"autoComplete"in e&&!c$1(e.autoComplete)&&(a.autoComplete=D.autoComplete,a.autoComplete.enabled=e.autoComplete),"mix"==a.mode&&(a.pattern=a.pattern||/@/,a.autoComplete.rightKey=!0,a.delimiters=e.delimiters||null,a.tagTextProp&&!a.dropdown.searchKeys.includes(a.tagTextProp)&&a.dropdown.searchKeys.push(a.tagTextProp)),t.pattern)try{a.pattern=new RegExp(t.pattern);}catch(t){}if(a.delimiters){a._delimiters=a.delimiters;try{a.delimiters=new RegExp(this.settings.delimiters,"g");}catch(t){}}(a.disabled||a.readonly)&&(a.userInput=!1),this.TEXTS=G({},P,a.texts||{}),"select"==a.mode&&(a.dropdown.includeSelectedTags=!0),("select"!=a.mode||(null===(i=e.dropdown)||void 0===i?void 0:i.enabled))&&a.userInput||(a.dropdown.enabled=0),a.disabled&&(a.dropdown.enabled=!1),a.dropdown.appendTarget=(null===(n=e.dropdown)||void 0===n?void 0:n.appendTarget)||document.body,void 0===a.dropdown.includeSelectedTags&&(a.dropdown.includeSelectedTags=a.duplicates);var o=this.getPersistedData("whitelist");Array.isArray(o)&&(this.whitelist=Array.isArray(a.whitelist)?g(a.whitelist,o):o);},getAttributes:function(t){var e,i=this.getCustomAttributes(t),n="";for(e in i)n+=" "+e+(void 0!==t[e]?'="'.concat(i[e],'"'):"");return n},getCustomAttributes:function(t){if(!c$1(t))return "";var e,i={};for(e in t)"__"!=e.slice(0,2)&&"class"!=e&&t.hasOwnProperty(e)&&void 0!==t[e]&&(i[e]=d$1(t[e]));return i},setStateSelection:function(){var t=window.getSelection(),e={anchorOffset:t.anchorOffset,anchorNode:t.anchorNode,range:t.getRangeAt&&t.rangeCount&&t.getRangeAt(0)};return this.state.selection=e,e},getCSSVars:function(){var t,e,i,n=getComputedStyle(this.DOM.scope,null);this.CSSVars={tagHideTransition:(t=function(t){if(!t)return {};var e=(t=t.trim().split(" ")[0]).split(/\d+/g).filter((function(t){return t})).pop().trim();return {value:+t.split(e).filter((function(t){return t}))[0].trim(),unit:e}}((i="tag-hide-transition",n.getPropertyValue("--"+i))),e=t.value,"s"==t.unit?1e3*e:e)};},build:function(t){var e=this.DOM,i=t.closest("label");this.settings.mixMode.integrated?(e.originalInput=null,e.scope=t,e.input=t):(e.originalInput=t,e.originalInput_tabIndex=t.tabIndex,e.scope=this.parseTemplate("wrapper",[t,this.settings]),e.input=e.scope.querySelector(this.settings.classNames.inputSelector),t.parentNode.insertBefore(e.scope,t),t.tabIndex=-1),i&&i.setAttribute("for","");},destroy:function(){var t,e,i;this.events.unbindGlobal.call(this),null===(t=this.DOM.scope.parentNode)||void 0===t||t.removeChild(this.DOM.scope),this.DOM.originalInput.tabIndex=this.DOM.originalInput_tabIndex,delete this.DOM.originalInput.__tagify,this.dropdown.hide(!0),this.removeAllCustomListeners(),clearTimeout(this.dropdownHide__bindEventsTimeout),clearInterval(null===(i=this.listeners)||void 0===i||null===(e=i.main)||void 0===e?void 0:e.originalInputValueObserverInterval);},loadOriginalValues:function(t){var e,i=this.settings;if(this.state.blockChangeEvent=!0,void 0===t){var n=this.getPersistedData("value");t=n&&!this.DOM.originalInput.value?n:i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value;}if(this.removeAllTags(),t)if("mix"==i.mode)this.parseMixTags(t),(e=this.DOM.input.lastChild)&&"BR"==e.tagName||this.DOM.input.insertAdjacentHTML("beforeend","<br>");else {try{J(JSON.parse(t),Array)&&(t=JSON.parse(t));}catch(t){}this.addTags(t,!0).forEach((function(t){return t&&t.classList.add(i.classNames.tagNoAnimation)}));}else this.postUpdate();this.state.lastOriginalValueReported=i.mixMode.integrated?"":this.DOM.originalInput.value;},cloneEvent:function(t){var e={};for(var i in t)"path"!=i&&(e[i]=t[i]);return e},loading:function(t){return this.state.isLoading=t,this.DOM.scope.classList[t?"add":"remove"](this.settings.classNames.scopeLoading),this},tagLoading:function(t,e){return t&&t.classList[e?"add":"remove"](this.settings.classNames.tagLoading),this},toggleClass:function(t,e){"string"==typeof t&&this.DOM.scope.classList.toggle(t,e);},toggleScopeValidation:function(t){var e=!0===t||void 0===t;!this.settings.required&&t&&t===this.TEXTS.empty&&(e=!0),this.toggleClass(this.settings.classNames.tagInvalid,!e),this.DOM.scope.title=e?"":t;},toggleFocusClass:function(t){this.toggleClass(this.settings.classNames.focus,!!t);},setPlaceholder:function(t){var e=this;["data","aria"].forEach((function(i){return e.DOM.input.setAttribute("".concat(i,"-placeholder"),t)}));},triggerChangeEvent:function(){if(!this.settings.mixMode.integrated){var t=this.DOM.originalInput,e=this.state.lastOriginalValueReported!==t.value,i=new CustomEvent("change",{bubbles:!0});e&&(this.state.lastOriginalValueReported=t.value,i.simulated=!0,t._valueTracker&&t._valueTracker.setValue(Math.random()),t.dispatchEvent(i),this.trigger("change",this.state.lastOriginalValueReported),t.value=this.state.lastOriginalValueReported);}},events:K,fixFirefoxLastTagNoCaret:function(){},setRangeAtStartEnd:function(t,e){if(e){t="number"==typeof t?t:!!t,e=e.lastChild||e;var i=document.getSelection();if(J(i.focusNode,Element)&&!this.DOM.input.contains(i.focusNode))return !0;try{i.rangeCount>=1&&["Start","End"].forEach((function(n){return i.getRangeAt(0)["set"+n](e,t||e.length)}));}catch(t){console.warn(t);}}},insertAfterTag:function(t,e){if(e=e||this.settings.mixMode.insertAfterTag,t&&t.parentNode&&e)return e="string"==typeof e?document.createTextNode(e):e,t.parentNode.insertBefore(e,t.nextSibling),e},editTagChangeDetected:function(t){var e=t.__originalData;for(var i in e)if(!this.dataProps.includes(i)&&t[i]!=e[i])return !0;return !1},getTagTextNode:function(t){return t.querySelector(this.settings.classNames.tagTextSelector)},setTagTextNode:function(t,e){this.getTagTextNode(t).innerHTML=d$1(e);},editTag:function(t,e){var i=this;t=t||this.getLastTag(),e=e||{};var s=this.settings,a=this.getTagTextNode(t),o=this.getNodeIndex(t),r=y(t),l=this.events.callbacks,d=!0,c="select"==s.mode;if(!c&&this.dropdown.hide(),a){if(!J(r,Object)||!("editable"in r)||r.editable)return r=y(t,{__originalData:u$1({},r),__originalHTML:t.cloneNode(!0)}),y(r.__originalHTML,r.__originalData),a.setAttribute("contenteditable",!0),t.classList.add(s.classNames.tagEditing),this.events.callbacks.onEditTagFocus.call(this,t),a.addEventListener("click",l.onEditTagClick.bind(this,t)),a.addEventListener("blur",l.onEditTagBlur.bind(this,this.getTagTextNode(t))),a.addEventListener("input",l.onEditTagInput.bind(this,a)),a.addEventListener("paste",l.onEditTagPaste.bind(this,a)),a.addEventListener("keydown",(function(e){return l.onEditTagkeydown.call(i,e,t)})),a.addEventListener("compositionstart",l.onCompositionStart.bind(this)),a.addEventListener("compositionend",l.onCompositionEnd.bind(this)),e.skipValidation||(d=this.editTagToggleValidity(t)),a.originalIsValid=d,this.trigger("edit:start",{tag:t,index:o,data:r,isValid:d}),a.focus(),!c&&this.setRangeAtStartEnd(!1,a),0===s.dropdown.enabled&&!c&&this.dropdown.show(),this.state.hasFocus=!0,this}else n.warn("Cannot find element in Tag template: .",s.classNames.tagTextSelector);},editTagToggleValidity:function(t,e){var i;if(e=e||y(t))return (i=!("__isValid"in e)||!0===e.__isValid)||this.removeTagsFromValue(t),this.update(),t.classList.toggle(this.settings.classNames.tagNotAllowed,!i),e.__isValid=i,e.__isValid;n.warn("tag has no data: ",t,e);},onEditTagDone:function(t,e){t=t||this.state.editing.scope,e=e||{};var i,n,s=this.settings,a={tag:t,index:this.getNodeIndex(t),previousData:y(t),data:e};this.trigger("edit:beforeUpdate",a,{cloneData:!1}),this.state.editing=!1,delete e.__originalData,delete e.__originalHTML,t&&t.parentNode&&((void 0!==(n=e[s.tagTextProp])?null===(i=(n+="").trim)||void 0===i?void 0:i.call(n):s.tagTextProp in e?void 0:e.value)?(t=this.replaceTag(t,e),this.editTagToggleValidity(t,e),s.a11y.focusableTags?t.focus():"select"!=s.mode&&T(t)):this.removeTags(t)),this.trigger("edit:updated",a),s.dropdown.closeOnSelect&&this.dropdown.hide(),this.settings.keepInvalidTags&&this.reCheckInvalidTags();},replaceTag:function(t,e){e&&""!==e.value&&void 0!==e.value||(e=t.__tagifyTagData),e.__isValid&&1!=e.__isValid&&u$1(e,this.getInvalidTagAttrs(e,e.__isValid));var i=this.createTagElem(e);return t.parentNode.replaceChild(i,t),this.updateValueByDOMTags(),i},updateValueByDOMTags:function(){var t=this;this.value.length=0;var e=this.settings.classNames,i=[e.tagNotAllowed.split(" ")[0],e.tagHide];[].forEach.call(this.getTagElms(),(function(e){$$1(e.classList).some((function(t){return i.includes(t)}))||t.value.push(y(e));})),this.update(),this.dropdown.refilter();},injectAtCaret:function(t,e){var i;if(e=e||(null===(i=this.state.selection)||void 0===i?void 0:i.range),"string"==typeof t&&(t=document.createTextNode(t)),!t)return this;var n=11===t.nodeType?Array.prototype.slice.call(t.childNodes):[t];if(!n.length)return this;if(!e)return this.appendMixTags(t),this;if(!this.DOM.scope.contains(null==e?void 0:e.startContainer))return this;w(t,e);var s=n[n.length-1]||t;return (null==s?void 0:s.parentNode)&&T(s),this.setStateSelection(),this.updateValueByDOMTags(),this.update(),this},input:{set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this.settings,n=i.dropdown.closeOnSelect;this.state.inputText=t,e&&(this.DOM.input.innerHTML=d$1(""+t),t&&this.toggleClass(i.classNames.empty,!this.DOM.input.innerHTML)),!t&&n&&this.dropdown.hide.bind(this),this.input.autocomplete.suggest.call(this),this.input.validate.call(this);},raw:function(){return this.DOM.input.textContent},validate:function(){var t=!this.state.inputText||!0===this.validateTag({value:this.state.inputText});return this.DOM.input.classList.toggle(this.settings.classNames.inputInvalid,!t),t},normalize:function(t,e){var i=t||this.DOM.input,n=[];i.childNodes.forEach((function(t){return 3==t.nodeType&&n.push(t.nodeValue)})),n=n.join("\n");try{n=n.replace(/(?:\r\n|\r|\n)/g,this.settings.delimiters.source.charAt(0));}catch(t){}return n=n.replace(/\s/g," "),(null==e?void 0:e.trim)?this.trim(n):n},autocomplete:{suggest:function(t){if(this.settings.autoComplete.enabled){"object"!=typeof(t=t||{value:""})&&(t={value:t});var e=this.dropdown.getMappedValue(t);if("number"!=typeof e){var i=this.state.inputText.toLowerCase(),n=e.substr(0,this.state.inputText.length).toLowerCase(),s=e.substring(this.state.inputText.length);e&&this.state.inputText&&n==i?(this.DOM.input.setAttribute("data-suggest",s),this.state.inputSuggestion=t):(this.DOM.input.removeAttribute("data-suggest"),delete this.state.inputSuggestion);}}},set:function(t){var e=this.DOM.input.getAttribute("data-suggest"),i=t||(e?this.state.inputText+e:null);return !!i&&("mix"==this.settings.mode?this.replaceTextWithNode(document.createTextNode(this.state.tag.prefix+i)):(this.input.set.call(this,i),this.setRangeAtStartEnd(!1,this.DOM.input)),this.input.autocomplete.suggest.call(this),this.dropdown.hide(),!0)}}},getTagIdx:function(t){return this.value.findIndex((function(e){return e.__tagId==(t||{}).__tagId}))},getNodeIndex:function(t){var e=0;if(t)for(;t=t.previousElementSibling;)e++;return e},getTagElms:function(){for(var t=arguments.length,e=new Array(t),i=0;i<t;i++)e[i]=arguments[i];var n="."+$$1(this.settings.classNames.tag.split(" ")).concat($$1(e)).join(".");return [].slice.call(this.DOM.scope.querySelectorAll(n))},getLastTag:function(){var t=this.settings.classNames,e=this.DOM.scope.querySelectorAll("".concat(t.tagSelector,":not(.").concat(t.tagHide,"):not([readonly])"));return e[e.length-1]},isTagDuplicate:function(t,e,i){var n=0,a=!0,o=!1,r=void 0;try{for(var l,d=this.value[Symbol.iterator]();!(a=(l=d.next()).done);a=!0){var c=l.value;s(this.trim(""+t),c.value,e)&&i!=c.__tagId&&n++;}}catch(t){o=!0,r=t;}finally{try{a||null==d.return||d.return();}finally{if(o)throw r}}return n},getTagIndexByValue:function(t){var e=this,i=[],n=this.settings.dropdown.caseSensitive;return this.getTagElms().forEach((function(a,o){a.__tagifyTagData&&s(e.trim(a.__tagifyTagData.value),t,n)&&i.push(o);})),i},getTagElmByValue:function(t){var e=this.getTagIndexByValue(t)[0];return this.getTagElms()[e]},flashTag:function(t){var e=this;t&&(t.classList.add(this.settings.classNames.tagFlash),setTimeout((function(){t.classList.remove(e.settings.classNames.tagFlash);}),100));},isTagBlacklisted:function(t){return t=this.trim(t.toLowerCase()),this.settings.blacklist.filter((function(e){return (""+e).toLowerCase()==t})).length},isTagWhitelisted:function(t){return !!this.getWhitelistItem(t)},getWhitelistItem:function(t,e,i){e=e||"value";var n,a=this.settings;return (i=i||a.whitelist).some((function(i){var o="object"==typeof i?i[e]||i.value:i;if(s(o,t,a.dropdown.caseSensitive,a.trim))return n="object"==typeof i?i:{value:i},!0})),n||"value"!=e||"value"==a.tagTextProp||(n=this.getWhitelistItem(t,a.tagTextProp,i)),n},validateTag:function(t){var e=this.settings,i="value"in t?"value":e.tagTextProp,n=this.trim(t[i]+"");return (t[i]+"").trim()?"mix"!=e.mode&&e.pattern&&J(e.pattern,RegExp)&&!e.pattern.test(n)?this.TEXTS.pattern:!e.duplicates&&this.isTagDuplicate(n,e.dropdown.caseSensitive,t.__tagId)?this.TEXTS.duplicate:this.isTagBlacklisted(n)||e.enforceWhitelist&&!this.isTagWhitelisted(n)?this.TEXTS.notAllowed:!e.validate||e.validate(t):this.TEXTS.empty},getInvalidTagAttrs:function(t,e){return {"aria-invalid":!0,class:"".concat(t.class||""," ").concat(this.settings.classNames.tagNotAllowed).trim(),title:e}},hasMaxTags:function(){return this.value.length>=this.settings.maxTags&&this.TEXTS.exceed},setReadonly:function(t,e){var i=this.settings;this.DOM.scope.contains(document.activeElement)&&document.activeElement.blur(),i[e||"readonly"]=t,this.DOM.scope[(t?"set":"remove")+"Attribute"](e||"readonly",!0),this.settings.userInput=!0,this.setContentEditable(!t),t||(this.events.binding.call(this,!0),this.events.binding.call(this));},setContentEditable:function(t){this.DOM.scope.querySelectorAll("[data-can-editable]").forEach((function(e){e.contentEditable=t,e.tabIndex=t?0:-1;}));},setDisabled:function(t){this.setReadonly(t,"disabled");},normalizeTags:function(t){var e=this,i=this.settings,n=i.whitelist,s=i.delimiters,a=i.mode,o=i.tagTextProp,r=[],l=!!n&&J(n[0],Object),d=Array.isArray(t),g=d&&t[0].value,h=function(t){return (t+"").split(s).reduce((function(t,i){var n,s=e.trim(i);return s&&t.push((X(n={},o,s),X(n,"value",s),n)),t}),[])};if("number"==typeof t&&(t=t.toString()),"string"==typeof t){if(!t.trim())return [];t=h(t);}else d&&(t=t.reduce((function(t,i){if(c$1(i)){var n=u$1({},i);o in n||(o="value"),n[o]=e.trim(n[o]),(n[o]||0===n[o])&&t.push(n);}else if(null!=i&&""!==i&&void 0!==i){var s;(s=t).push.apply(s,$$1(h(i)));}return t}),[]));return l&&!g&&(t.forEach((function(t){var i=r.map((function(t){return t.value})),n=e.dropdown.filterListItems.call(e,t[o],{exact:!0});e.settings.duplicates||(n=n.filter((function(t){return !i.includes(t.value)})));var s=n.length>1?e.getWhitelistItem(t[o],o,n):n[0];s&&J(s,Object)?r.push(s):"mix"!=a&&(null==t.value&&(t.value=t[o]),r.push(t));})),r.length&&(t=r)),t},parseMixTags:function(t){var e=this,i=this.settings,n=i.mixTagsInterpolator,s=i.duplicates,a=i.transformTag,o=i.enforceWhitelist,r=i.maxTags,l=i.tagTextProp,d=[];t=t.split(n[0]).map((function(t,i){var c,u,g,h=t.split(n[1]),p=h[0],f=d.length==r;try{if(p==+p)throw Error;u=JSON.parse(p);}catch(t){u=e.normalizeTags(p)[0]||{value:p};}if(a.call(e,u),f||!(h.length>1)||o&&!e.isTagWhitelisted(u.value)||!s&&e.isTagDuplicate(u.value)){if(t)return i?n[0]+t:t}else u[c=u[l]?l:"value"]=e.trim(u[c]),g=e.createTagElem(u),d.push(u),g.classList.add(e.settings.classNames.tagNoAnimation),h[0]=g.outerHTML,e.value.push(u);return h.join("")})).join(""),this.DOM.input.innerHTML=t,this.DOM.input.appendChild(document.createTextNode("")),this.DOM.input.normalize();var c=this.getTagElms();return c.forEach((function(t,e){return y(t,d[e])})),this.update({withoutChangeEvent:!0}),O(c,this.state.hasFocus),t},replaceTextWithNode:function(t,e){if(this.state.tag||e){e=e||this.state.tag.prefix+this.state.tag.value;var i,n,s=this.state.selection||window.getSelection(),a=s.anchorNode,o=this.state.tag.delimiters?this.state.tag.delimiters.length:0;return a.splitText(s.anchorOffset-o),-1==(i=a.nodeValue.lastIndexOf(e))?!0:(n=a.splitText(i),t&&a.parentNode.replaceChild(t,n),!0)}},prepareNewTagNode:function(t,e){e=e||{};var i=this.settings,n=[],s={},a=Object.assign({},t,{value:t.value+""});if(t=Object.assign({},a),i.transformTag.call(this,t),t.__isValid=this.hasMaxTags()||this.validateTag(t),!0!==t.__isValid){if(e.skipInvalid)return;if(u$1(s,this.getInvalidTagAttrs(t,t.__isValid),{__preInvalidData:a}),t.__isValid==this.TEXTS.duplicate&&this.flashTag(this.getTagElmByValue(t.value)),!i.createInvalidTags)return void n.push(t.value)}return "readonly"in t&&(t.readonly?s["aria-readonly"]=!0:delete t.readonly),{tagElm:this.createTagElem(t,s),tagData:t,aggregatedInvalidInput:n}},postProcessNewTagNode:function(t,e){var i=this,n=this.settings,s=e.__isValid;s&&!0===s?this.value.push(e):(this.trigger("invalid",{data:e,index:this.value.length,tag:t,message:s}),n.keepInvalidTags||setTimeout((function(){return i.removeTags(t,!0)}),1e3)),this.dropdown.position();},selectTag:function(t,e){var i=this;if(!this.settings.enforceWhitelist||this.isTagWhitelisted(e.value)){this.state.actions.selectOption&&setTimeout((function(){return i.setRangeAtStartEnd(!1,i.DOM.input)}));var n=this.getLastTag();return n?this.replaceTag(n,e):this.appendTag(t),this.value[0]=e,this.update(),this.trigger("add",{tag:t,data:e}),[t]}},addEmptyTag:function(t){var e=u$1({value:""},t||{}),i=this.createTagElem(e);y(i,e),this.appendTag(i),this.editTag(i,{skipValidation:!0}),this.toggleFocusClass(!0);},addTags:function(t,e,i){var n=this,s=[],a=this.settings,o=[],r=document.createDocumentFragment(),l=[];if(!t||0==t.length)return s;switch(t=this.normalizeTags(t),a.mode){case"mix":return this.addMixTags(t);case"select":e=!1,this.removeAllTags();}return this.DOM.input.removeAttribute("style"),t.forEach((function(t){var e=n.prepareNewTagNode(t,{skipInvalid:i||a.skipInvalid});if(e){var d=e.tagElm;if(t=e.tagData,o=e.aggregatedInvalidInput,s.push(d),"select"==a.mode)return n.selectTag(d,t);r.appendChild(d),n.postProcessNewTagNode(d,t),l.push({tagElm:d,tagData:t});}})),this.appendTag(r),l.forEach((function(t){var e=t.tagElm,i=t.tagData;return n.trigger("add",{tag:e,index:n.getTagIdx(i),data:i})})),this.update(),t.length&&e&&(this.input.set.call(this,a.createInvalidTags?"":o.join(a._delimiters)),this.setRangeAtStartEnd(!1,this.DOM.input)),this.dropdown.refilter(),s},addMixTags:function(t){var e=this;if((t=this.normalizeTags(t))[0].prefix||this.state.tag)return this.prefixedTextToTag(t[0]);var i=document.createDocumentFragment(),n=[];return t.forEach((function(t){var s=e.prepareNewTagNode(t);i.appendChild(s.tagElm),e.insertAfterTag(s.tagElm),e.postProcessNewTagNode(s.tagElm,s.tagData),n.push({tagElm:s.tagElm,tagData:s.tagData});})),this.appendMixTags(i,n),i.children},appendMixTags:function(t,e){var i,n=null===(i=this.state.selection)||void 0===i?void 0:i.range,s=!!n&&this.DOM.scope.contains(n.startContainer),a=t?11===t.nodeType?Array.prototype.slice.call(t.childNodes):[t]:[];if(s)this.injectAtCaret(t);else {this.DOM.input.focus();var o=this.setStateSelection();(null==o?void 0:o.range)&&(o.range.setStart(this.DOM.input,o.range.endOffset),o.range.setEnd(this.DOM.input,o.range.endOffset)),this.DOM.input.appendChild(t),this.updateValueByDOMTags(),this.update();var r=a[a.length-1];(null==r?void 0:r.parentNode)&&T(r),this.setStateSelection();}var l=(null==e?void 0:e.length)?e:a.filter((function(t){return 1===t.nodeType}));(null==l?void 0:l.length)&&this.trigger("add",{tags:l});},prefixedTextToTag:function(t){var e,i,n,s=this,a=this.settings,o=null===(e=this.state.tag)||void 0===e?void 0:e.delimiters;if(t.prefix=t.prefix||this.state.tag?this.state.tag.prefix:(a.pattern.source||a.pattern)[0],n=this.prepareNewTagNode(t),i=n.tagElm,this.replaceTextWithNode(i)||this.DOM.input.appendChild(i),setTimeout((function(){return i.classList.add(s.settings.classNames.tagNoAnimation)}),300),this.update(),!o){var r=this.insertAfterTag(i)||i;setTimeout(T,0,r);}return this.state.tag=null,this.postProcessNewTagNode(i,n.tagData),this.trigger("add",{tag:n,data:t}),i},appendTag:function(t){var e=this.DOM,i=e.input;e.scope.insertBefore(t,i);},createTagElem:function(t,e){t.__tagId=f$1();var i,n=u$1({},t,G({value:d$1(t.value+"")},e));return function(t){for(var e,i=document.createNodeIterator(t,NodeFilter.SHOW_TEXT,null,!1);e=i.nextNode();)e.textContent.trim()||e.parentNode.removeChild(e);}(i=this.parseTemplate("tag",[n,this])),y(i,t),i},reCheckInvalidTags:function(){var t=this,e=this.settings;this.getTagElms(e.classNames.tagNotAllowed).forEach((function(i,n){var s=y(i),a=t.hasMaxTags(),o=t.validateTag(s),r=!0===o&&!a;if("select"==e.mode&&t.toggleScopeValidation(o),r)return s=s.__preInvalidData?s.__preInvalidData:{value:s.value},t.replaceTag(i,s);i.title=a||o;}));},removeTags:function(t,e,i){var n,s=this,a=this.settings;if(t=t&&J(t,HTMLElement)?[t]:J(t,Array)?t:t?[t]:[this.getLastTag()].filter((function(t){return t})),n=t.reduce((function(t,e){e&&"string"==typeof e&&(e=s.getTagElmByValue(e));var i=y(e);return e&&i&&!i.readonly&&t.push({node:e,idx:s.getTagIdx(i),data:y(e,{__removed:!0})}),t}),[]),i="number"==typeof i?i:this.CSSVars.tagHideTransition,"select"==a.mode&&(i=0,this.input.set.call(this)),1==n.length&&"select"!=a.mode&&n[0].node.classList.contains(a.classNames.tagNotAllowed)&&(e=!0),n.length)return a.hooks.beforeRemoveTag(n,{tagify:this}).then((function(){var t=function(t){t.node.parentNode&&(t.node.parentNode.removeChild(t.node),e?a.keepInvalidTags&&this.trigger("remove",{tag:t.node,index:t.idx}):(this.dropdown.refilter(),this.dropdown.position(),this.DOM.input.normalize(),a.keepInvalidTags&&this.reCheckInvalidTags(),this.trigger("remove",{tag:t.node,index:t.idx,data:t.data})));};e||(s.removeTagsFromValue(n.map((function(t){return t.node}))),s.update(),"select"==a.mode&&a.userInput&&s.setContentEditable(!0)),i&&i>10&&1==n.length?function(e){e.node.style.width=parseFloat(window.getComputedStyle(e.node).width)+"px",document.body.clientTop,e.node.classList.add(a.classNames.tagHide),setTimeout(t.bind(this),i,e);}.call(s,n[0]):n.forEach(t.bind(s));})).catch((function(t){}))},removeTagsFromDOM:function(){this.getTagElms().forEach((function(t){return t.remove()}));},removeTagsFromValue:function(t){var e=this;(t=Array.isArray(t)?t:[t]).forEach((function(t){var i=y(t),n=e.getTagIdx(i);n>-1&&e.value.splice(n,1);}));},removeAllTags:function(t){var e=this;t=t||{},this.value=[],"mix"==this.settings.mode?this.DOM.input.innerHTML="":this.removeTagsFromDOM(),this.dropdown.refilter(),this.dropdown.position(),this.state.dropdown.visible&&setTimeout((function(){e.DOM.input.focus();})),"select"==this.settings.mode&&(this.input.set.call(this),this.settings.userInput&&this.setContentEditable(!0));var i=this.state.blockChangeEvent?void 0:function(){!e.state.blockChangeEvent&&e.trigger("remove",{});};this.update(t,i);},postUpdate:function(){this.state.blockChangeEvent=!1;var t,e,i=this.settings,n=i.classNames,s="mix"==i.mode?i.mixMode.integrated?this.DOM.input.textContent:this.DOM.originalInput.value.trim():this.value.length+this.input.raw.call(this).length;(this.toggleClass(n.hasMaxTags,this.value.length>=i.maxTags),this.toggleClass(n.hasNoTags,!this.value.length),this.toggleClass(n.empty,!s),"select"==i.mode)&&this.toggleScopeValidation(null===(e=this.value)||void 0===e||null===(t=e[0])||void 0===t?void 0:t.__isValid);},setOriginalInputValue:function(t){var e=this.DOM.originalInput;this.settings.mixMode.integrated||(e.value=t,e.tagifyValue=e.value);},update:function(t,e){clearTimeout(this.debouncedUpdateTimeout),this.debouncedUpdateTimeout=setTimeout(function(){this.setPersistedData(i,"value"),this.settings.onChangeAfterBlur&&(t||{}).withoutChangeEvent||this.state.blockChangeEvent||this.triggerChangeEvent();this.postUpdate(),null==e||e();}.bind(this),100),this.events.bindOriginaInputListener.call(this,100);var i=this.getInputValue();this.setOriginalInputValue(i);},getInputValue:function(){var t=this.getCleanValue();return "mix"==this.settings.mode?this.getMixedTagsAsString(t):t.length?this.settings.originalInputValueFormat?this.settings.originalInputValueFormat(t):JSON.stringify(t):""},getCleanValue:function(t){return a(t||this.value,this.dataProps)},getMixedTagsAsString:function(){var t="",e=this,i=this.settings,n=i.originalInputValueFormat||JSON.stringify,s=i.mixTagsInterpolator;return function i(a){a.childNodes.forEach((function(a){if(1==a.nodeType){var r=y(a);if("BR"==a.tagName&&(t+="\r\n"),r&&v.call(e,a)){if(r.__removed)return;t+=s[0]+n(o(r,e.dataProps))+s[1];}else a.getAttribute("style")||["B","I","U"].includes(a.tagName)?t+=a.textContent:"DIV"!=a.tagName&&"P"!=a.tagName||(t+="\r\n",i(a));}else t+=a.textContent;}));}(this.DOM.input),t}},Q.prototype.removeTag=Q.prototype.removeTags;

    function get_page(alert_stats_page) {
        let page = ntopng_url_manager.get_url_entry("page");
        if (page == null) {
            if (alert_stats_page) {
                page = "all";
            } else {
                page = "overview";
            }
        }
        return page;
    }

    async function get_filter_const(is_alert_stats_url, page) {
        let url_request;
        let query_preset = ntopng_url_manager.get_url_entry("query_preset");
        if (query_preset == null) { query_preset = ""; }
        if (is_alert_stats_url) {
            url_request = `${http_prefix}/lua/rest/v2/get/alert/filter/consts.lua?page=${page}&query_preset=${query_preset}`;
        } else {
            let aggregated = ntopng_url_manager.get_url_entry("aggregated") || "";
            url_request = `${http_prefix}/lua/pro/rest/v2/get/db/filter/consts.lua?page=${page}&query_preset=${query_preset}&aggregated=${aggregated}`;
        }
        let filter_consts = await ntopng_utility.http_request(url_request);
        return filter_consts;
    }

    let FILTERS_CONST$1 = [];
    let TAG_OPERATORS$1;
    let DEFINED_TAGS$1;
    /* Initial Tags */
    let initialTags;
    //let pageHandle = {};
    let TAGIFY;
    let IS_ALERT_STATS_URL = window.location.toString().match(/alert_stats.lua/) != null;
    let STATUS_VIEW = ntopng_url_manager.get_url_entry("status");
    if (STATUS_VIEW == null || STATUS_VIEW == "") {
        STATUS_VIEW = "historical";
    }

    let PAGE = get_page(IS_ALERT_STATS_URL);

    const create_tag_from_filter = function (filter) {
        let f_const = FILTERS_CONST$1.find((f) => f.id == filter.id);
        if (f_const == null) { console.error("create_tag_from_filter: filter const not found;"); }

        let value_label = filter.value;
        if (f_const.options != null) {
            let opt = f_const.options.find((o) => o.value == filter.value);
            if (opt != null) {
                value_label = opt.label;
            }
        }
        const tag = {
            label: f_const.label,
            key: f_const.id,
            value: value_label,
            realValue: filter.value,
            title: `${f_const.label}${filter.operator}${value_label}`,
            selectedOperator: filter.operator,
        };
        if (tag.value == "") { tag.value = "''"; }
        if (tag.realValue == null || tag.selectedOperator == null || tag.selectedOperator == "") {
            return null;
        }
        return tag;
    };

    const load_filters_data = async function () {
        FILTERS_CONST$1 = await get_filter_const(IS_ALERT_STATS_URL, PAGE);
        FILTERS_CONST$1.filter((x) => x.label == null).forEach((x) => { console.error(`label not defined for filter ${JSON.stringify(x)}`); x.label = ""; });
        FILTERS_CONST$1.sort((a, b) => a.label.localeCompare(b.label));
        i18n_ext.tags = {};
        TAG_OPERATORS$1 = {};
        DEFINED_TAGS$1 = {};
        FILTERS_CONST$1.forEach((f_def) => {
            i18n_ext.tags[f_def.id] = f_def.label;
            f_def.operators.forEach((op) => TAG_OPERATORS$1[op.id] = op.label);
            DEFINED_TAGS$1[f_def.id] = f_def.operators.map((op) => op.id);
        });
        let entries = ntopng_url_manager.get_url_entries();
        let filters = [];
        for (const [key, value] of entries) {
            let filter_def = FILTERS_CONST$1.find((fc) => fc.id == key);
            if (filter_def != null) {
                let options_string = value.split(",");
                options_string.forEach((opt_string) => {
                    let [value, operator] = opt_string.split(";");
                    let value_label = value;
                    if (filter_def.value_type == "array") {
                        value_label = filter_def?.options?.find((opt) => opt.value == value)?.label;
                    }
                    filters.push({ id: filter_def.id, operator: operator, value: value, label: filter_def.label, value_label });
                });
            }
        }
        return filters;
        // "l7proto=XXX;eq"
    };

    var script$2D = {
        props: {
            id: String,
            min_time_interval_id: String,
            round_time: Boolean,
        },
        components: {
            'date-time-range-picker': script$2G,
            'modal-filters': script$2E,
        },
        /**
         * First method called when the component is created.
         */
        created() {
        },
        async mounted() {
            let dt_range_picker_mounted = ntopng_sync.on_ready(this.id_data_time_range_picker);
            ntopng_sync.on_ready(this.id_modal_filters);
            await dt_range_picker_mounted;

            if (this.page != 'all') {
                let filters = await load_filters_data();

                TAGIFY = create_tagify(this);
                ntopng_events_manager.emit_event(ntopng_events.FILTERS_CHANGE, { filters });
                ntopng_events_manager.on_event_change(this.$props["id"], ntopng_events.FILTERS_CHANGE, (status) => this.reload_status(status), true);
            }
            this.modal_data = FILTERS_CONST$1;

            //await modal_filters_mounted;
            ntopng_sync.ready(this.$props["id"]);
        },
        data() {
            return {
                i18n: i18n,
                id_modal_filters: `${this.$props.id}_modal_filters`,
                id_data_time_range_picker: `${this.$props.id}_date-time-range-picker`,
                show_filters: false,
                edit_tag: null,
                is_alert_stats_url: IS_ALERT_STATS_URL,
                status_view: STATUS_VIEW,
                page: PAGE,
                modal_data: [],
                last_filters: [],
            };
        },
        methods: {
            is_filter_defined: function (filter) {
                return DEFINED_TAGS$1[filter.id] != null;
            },
            update_status_view: function (status) {
                ntopng_url_manager.set_key_to_url("status", status);
                ntopng_url_manager.reload_url();
            },
            show_modal_filters: function () {
                this.$refs["modal_filters"].show();
            },
            remove_filters: function () {
                let filters = [];
                ntopng_events_manager.emit_event(ntopng_events.FILTERS_CHANGE, { filters });
            },
            reload_status: function (status) {
                let filters = status.filters;
                if (filters == null) { return; }
                // delete all previous filter
                ntopng_url_manager.delete_params(FILTERS_CONST$1.map((f) => f.id));
                TAGIFY.tagify.removeAllTags();
                const filters_object = filtersManager.get_filters_object(filters);
                let filters_to_add = {};
                for (const key in filters_object) {
                    filters_object[key];
                    filters_to_add[key] = filters_object[key];
                }
                ntopng_url_manager.add_obj_to_url(filters_to_add);
                filters.forEach((f) => {
                    //if (!dataUtils.isEmptyOrNull(f.value)) {
                        let tag = create_tag_from_filter(f);
                        if (tag == null) { return; }
                        TAGIFY.addFilterTag(tag);
                    //}
                });
                this.last_filters = filters;
            },
            apply_modal: function (params) {
                let status = ntopng_status_manager.get_status();
                let filters = status.filters;
                if (filters == null) { filters = []; }
                if (this.edit_tag != null) {
                    filters = filters.filter((f) => f.id != this.edit_tag.key || f.value != this.edit_tag.realValue);
                    this.edit_tag = null;
                }
                filters.push(params);
                // trigger event and then call reload_status
                ntopng_events_manager.emit_event(ntopng_events.FILTERS_CHANGE, { filters });
            },
        },
    };

    function create_tagify(range_picker_vue) {
        // create tagify
        const tagify = new Q(range_picker_vue.$refs["tagify"], {
            duplicates: true,
            delimiters: null,
            dropdown: {
                enabled: 1, // suggest tags after a single character input
                classname: 'extra-properties' // custom class for the suggestions dropdown
            },
            autoComplete: { enabled: false },
            templates: {
                tag: function (tagData) {
                    try {
                        return `<tag title='${tagData.value}' contenteditable='false' spellcheck="false" class='tagify__tag'>
                        <x title='remove tag' class='tagify__tag__removeBtn'></x>
                        <div>
                           <b>${tagData.label ? tagData.label : tagData.key}</b>&nbsp;
                           <b class='operator'>${tagData.selectedOperator ? TAG_OPERATORS$1[tagData.selectedOperator] : '='}</b>&nbsp;
                            <span class='tagify__tag-text'>${tagData.value == "&#039;&#039;" ? '' : tagData.value}</span>
                        </div>
                    </tag>`
                    }
                    catch (err) {
                        console.error(`An error occured when creating a new tag: ${err}`);
                    }
                },
            },
            validate: function (tagData) {
                return (typeof tagData.key !== 'undefined' &&
                    typeof tagData.selectedOperator !== 'undefined' &&
                    typeof tagData.value !== 'undefined');
            }
        });

        $(document).ready(function () {
            // add existing tags
            tagify.addTags(initialTags);
        }); /* $(document).ready() */

        const addFilterTag = async function (tag) {
            /* Convert values to string (this avoids issues e.g. with 0) */
            if (typeof tag.realValue === 'number') { tag.realValue = '' + tag.realValue; }
            if (typeof tag.value === 'number') { tag.value = '' + tag.value; }

            const existingTagElms = tagify.getTagElms();

            /* Lookup by key, value and operator (do not add the same key and value multiple times) */
            let existingTagElement = existingTagElms.find(htmlTag =>
                htmlTag.getAttribute('key') === tag.key
                && htmlTag.getAttribute('realValue') === tag.realValue
            );
            if (existingTagElement && tagify.getSetTagData(existingTagElement) !== undefined) {
                return;
            }

            // has the tag an operator object?
            if (DEFINED_TAGS$1[tag.key] && !Array.isArray(DEFINED_TAGS$1[tag.key])) {
                tag.operators = DEFINED_TAGS$1[tag.key].operators;
            }

            if (!tag.selectedOperator) {
                tag.selectedOperator = 'eq';
            }
            // add filter!
            tagify.addTags([tag]);
        };

        // when an user remove the tag
        tagify.on('remove', async function (e) {
            const detail = e.detail;
            if (detail.data === undefined) { return; }
            const tag = detail.data;
            const key = tag?.key;
            const value = tag?.realValue;
            const status = ntopng_status_manager.get_status();

            if (key === undefined) { return; }
            if (status.filters == null) { return; }

            const filters = status.filters.filter((f) => (f.id != key || (f.id == key && f.value != value)));
            ntopng_events_manager.emit_event(ntopng_events.FILTERS_CHANGE, { filters });
        });

        tagify.on('add', async function (e) {
            const detail = e.detail;
            if (detail.data === undefined) { return; }
            const tag = detail.data;
            // let's check if the tag has a key field
            if (!tag.key) {
                tagify.removeTags([e.detail.tag]);
                e.preventDefault();
                e.stopPropagation();
                return;
            }
        });

        // Tag 'click' event handler to open the 'Edit' modal. Note: this prevents
        // inline editing of the tag ('edit:updated' is never called as a consequence)
        tagify.on('click', async function (e) {
            const detail = e.detail;
            if (detail.data === undefined) { return; }
            if (detail.data.key === undefined) { return; }
            const tag = detail.data;
            // remember that this tag already exixts
            range_picker_vue.edit_tag = tag;
            // show modal-filters
            ntopng_events_manager.emit_custom_event(ntopng_custom_events.SHOW_MODAL_FILTERS, { id: tag.key, operator: tag.selectedOperator, value: tag.realValue });
        });

        return {
            tagify,
            addFilterTag,
        };
    }

    const _hoisted_1$2m = { style: {"width":"100%"} };
    const _hoisted_2$29 = { class: "mb-1" };
    const _hoisted_3$1X = {
      key: 0,
      style: {"margin-right":"0.1rem"},
      class: "d-flex align-items-center me-2"
    };
    const _hoisted_4$1S = {
      class: "btn-group",
      id: "statusSwitch",
      role: "group"
    };
    const _hoisted_5$1A = {
      key: 0,
      class: "d-flex mt-1",
      style: {"width":"100%"}
    };
    const _hoisted_6$1l = ["placeholder"];
    const _hoisted_7$1e = ["title"];

    function render$e(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_modal_filters = resolveComponent("modal-filters");
      const _component_date_time_range_picker = resolveComponent("date-time-range-picker");

      return (openBlock(), createElementBlock("div", _hoisted_1$2m, [
        createBaseVNode("div", _hoisted_2$29, [
          createVNode(_component_modal_filters, {
            filters_options: $data.modal_data,
            onApply: $options.apply_modal,
            ref: "modal_filters",
            id: $data.id_modal_filters
          }, null, 8 /* PROPS */, ["filters_options", "onApply", "id"]),
          createVNode(_component_date_time_range_picker, {
            id: $data.id_data_time_range_picker,
            min_time_interval_id: $props.min_time_interval_id,
            round_time: $props.round_time
          }, {
            begin: withCtx(() => [
              ($data.is_alert_stats_url)
                ? (openBlock(), createElementBlock("div", _hoisted_3$1X, [
                    createBaseVNode("div", _hoisted_4$1S, [
                      ($data.page != 'flow')
                        ? (openBlock(), createElementBlock("a", {
                            key: 0,
                            href: "#",
                            onClick: _cache[0] || (_cache[0] = $event => ($options.update_status_view('engaged'))),
                            class: normalizeClass(["btn btn-sm", { 'active': $data.status_view == 'engaged', 'btn-seconday': $data.status_view != 'engaged', 'btn-primary': $data.status_view == 'engaged' }])
                          }, [...(_cache[5] || (_cache[5] = [
                            createBaseVNode("i", {
                              class: "fa-solid fa-fire",
                              title: "Engaged"
                            }, null, -1 /* CACHED */)
                          ]))], 2 /* CLASS */))
                        : createCommentVNode("v-if", true),
                      createBaseVNode("a", {
                        href: "#",
                        onClick: _cache[1] || (_cache[1] = $event => ($options.update_status_view('historical'))),
                        class: normalizeClass(["btn btn-sm", { 'active': $data.status_view == 'historical' || ($data.page == 'flow' && $data.status_view == 'engaged'), 'btn-seconday': $data.status_view != 'historical', 'btn-primary': $data.status_view == 'historical' || ($data.page == 'flow' && $data.status_view == 'engaged') }])
                      }, [...(_cache[6] || (_cache[6] = [
                        createBaseVNode("i", {
                          class: "fa-regular fa-eye",
                          title: "Require Attention"
                        }, null, -1 /* CACHED */)
                      ]))], 2 /* CLASS */),
                      createCommentVNode(" <a href=\"#\" @click=\"update_status_view('acknowledged')\" class=\"btn btn-sm\"\n                                :class=\"{ 'active': status_view == 'acknowledged', 'btn-seconday': status_view != 'acknowledged', 'btn-primary': status_view == 'acknowledged' }\"><i class=\"fa-solid fa-check-double\" title=\"Acknowledged\"></i></a>"),
                      createBaseVNode("a", {
                        href: "#",
                        onClick: _cache[2] || (_cache[2] = $event => ($options.update_status_view('any'))),
                        class: normalizeClass(["btn btn-sm", { 'active': $data.status_view == 'any', 'btn-seconday': $data.status_view != 'any', 'btn-primary': $data.status_view == 'any' }])
                      }, [...(_cache[7] || (_cache[7] = [
                        createBaseVNode("i", {
                          class: "fa-solid fa-inbox",
                          title: "All"
                        }, null, -1 /* CACHED */)
                      ]))], 2 /* CLASS */)
                    ])
                  ]))
                : createCommentVNode("v-if", true),
              renderSlot(_ctx.$slots, "begin")
            ]),
            extra_buttons: withCtx(() => [
              renderSlot(_ctx.$slots, "extra_range_buttons")
            ]),
            _: 3 /* FORWARDED */
          }, 8 /* PROPS */, ["id", "min_time_interval_id", "round_time"])
        ]),
        createCommentVNode(" tagify "),
        ($data.page != 'all')
          ? (openBlock(), createElementBlock("div", _hoisted_5$1A, [
              createBaseVNode("input", {
                class: "w-100 form-control h-auto",
                name: "tags",
                ref: "tagify",
                placeholder: $data.i18n('show_alerts.filters')
              }, null, 8 /* PROPS */, _hoisted_6$1l),
              withDirectives(createBaseVNode("button", {
                class: "btn btn-link",
                "aria-controls": "flow-alerts-table",
                type: "button",
                id: "btn-add-alert-filter",
                onClick: _cache[3] || (_cache[3] = (...args) => ($options.show_modal_filters && $options.show_modal_filters(...args)))
              }, [...(_cache[8] || (_cache[8] = [
                createBaseVNode("span", null, [
                  createBaseVNode("i", {
                    class: "fas fa-plus",
                    "data-original-title": "",
                    title: "Add Filter"
                  })
                ], -1 /* CACHED */)
              ]))], 512 /* NEED_PATCH */), [
                [vShow, $data.modal_data && $data.modal_data.length > 0]
              ]),
              withDirectives(createBaseVNode("button", {
                "data-bs-toggle": "tooltip",
                "data-placement": "bottom",
                title: $data.i18n('show_alerts.remove_filters'),
                onClick: _cache[4] || (_cache[4] = (...args) => ($options.remove_filters && $options.remove_filters(...args))),
                class: "btn ms-1 my-auto btn-sm btn-remove-tags"
              }, [...(_cache[9] || (_cache[9] = [
                createBaseVNode("i", { class: "fas fa-times" }, null, -1 /* CACHED */)
              ]))], 8 /* PROPS */, _hoisted_7$1e), [
                [vShow, $data.modal_data && $data.modal_data.length > 0]
              ])
            ]))
          : createCommentVNode("v-if", true),
        createCommentVNode(" end tagify ")
      ]))
    }

    var css_248z$B = "\n.tagify__input[data-v-aaee59ea] {\n    min-width: 175px;\n}\n.tagify__tag[data-v-aaee59ea] {\n    white-space: nowrap;\n    margin: 3px 0px 5px 5px;\n}\n.tagify__tag select.operator[data-v-aaee59ea] {\n    margin: 0px 4px;\n    border: 1px solid #c4c4c4;\n    border-radius: 4px;\n}\n.tagify__tag b.operator[data-v-aaee59ea] {\n    margin: 0px 4px;\n    background-color: white;\n    border: 1px solid #c4c4c4;\n    border-radius: 4px;\n    padding: 0.05em 0.2em;\n}\n.tagify__tag>div[data-v-aaee59ea] {\n    display: flex;\n    align-items: center;\n}\n";
    styleInject(css_248z$B);

    script$2D.render = render$e;
    script$2D.__scopeId = "data-v-aaee59ea";
    script$2D.__file = "http_src/vue/range-picker.vue";

    const render_component = function(component, { props, children, element, app } = {}) {
        let el = element;
        let vNode = createVNode(component, props, children);
        if (app && app._context) vNode.appContext = app._context;
        if (el) render$l(vNode, el);
        else if (typeof document !== 'undefined' ) render$l(vNode, el = document.createElement('div'));
        
        const destroy = () => {
    	if (el) render$l(null, el);
    	el = null;
    	vNode = null;
        };
        
        return { vNode, destroy, el }
    };

    var script$2C = {
        props: ['content'],
        watch: {
    	"content": function(val, oldVal) {
    	    // this.$forceUpdate();
    	}
        },
        render() {
    	return h$2(this.$props.content);
        }
    };

    script$2C.__file = "http_src/vue/vue_node.vue";

    const _hoisted_1$2l = ["id"];
    const _hoisted_2$28 = ["aria-labelledby"];
    const _hoisted_3$1W = { class: "dropdown-item" };


    var script$2B = {
      __name: 'dropdown',
      props: {
        id: String,
        auto_load: Boolean,
        button_class: String,
        button_style: String,
        f_on_open: Function,
        f_on_close: Function,
    },
      emits: [],
      setup(__props, { expose: __expose, emit: __emit }) {
    const slots = useSlots();

    const options = ref([]);
    ref([]);
    const menu_options = ref([]);
    const dropdown = ref(null);
    const dropdown_button = ref(null);

    const props = __props;

    let default_overflow = null;

    onBeforeUnmount(() => {
        /* Just to be sure, before unmounting the element reset correctly the css */
        $(dropdown.value).parent().closest('div')?.css('overflow', default_overflow);
        $(dropdown.value).parent().closest('div')[0]?.style.setProperty('overflow', default_overflow, 'important');
        $(dropdown.value).parent().closest('td')[0]?.style.setProperty('z-index', 'auto', 'important');
        if (props.f_on_close != null) {
            props.f_on_close(el);
        }
    });
    onMounted(() => {
        default_overflow = $(dropdown.value).parent().closest('div').css('overflow');
        if (props.auto_load == true) {
            load_menu();
        }
        let el = { dropdown: dropdown.value, dropdown_button: dropdown_button.value };

        $(dropdown.value).on('show.bs.dropdown', function () {
            $(dropdown.value).parent().closest('div')?.css('overflow', "visible");
            $(dropdown.value).parent().closest('div')?.css('z-index', 'auto');
            $(dropdown.value).parent().closest('div')[0]?.style.setProperty('overflow', 'visible', 'important');
            $(dropdown.value).parent().closest('div')[0]?.style.setProperty('z-index', 'auto', 'important');
            $(dropdown.value).parent().closest('td')[0]?.style.setProperty('z-index', '10', 'important');
            $(this).find('.dropdown-menu').css('z-index', '1050');

            if (props.f_on_open != null) {
                props.f_on_open(el);
            }
        });

        $(dropdown.value).on('hide.bs.dropdown', function () {
            $(dropdown.value).parent().closest('div')?.css('overflow', default_overflow);
            $(dropdown.value).parent().closest('div')[0]?.style.setProperty('overflow', default_overflow, 'important');
            $(dropdown.value).parent().closest('td')[0]?.style.setProperty('z-index', 'auto', 'important');
            if (props.f_on_close != null) {
                props.f_on_close(el);
            }
        });
    });

    const button_class_2 = computed$1(() => {
        if (props.button_class != null) { return props.button_class; }
        return "btn-link";
    });

    async function load_menu() {
        options.value = [];
        if (slots == null || slots.menu == null) { return; }
        let m_options = slots.menu();
        if (m_options == null || m_options.length == 0) { return; }
        if (typeof m_options[0].type === 'symbol') {
            m_options = m_options[0].children;
        }
        menu_options.value = [];
        m_options.forEach((opt_slot) => {
            let node = opt_slot;
            menu_options.value.push(node);
            // let element = $("<div></div>")[0];
            // const { vNode, el } = render_component(node, { app:  instance?.appContext?.app, element });
            // options.value.push(el);
        });
        await nextTick();
        // nextTick(() => {
        // 	options.value.forEach((opt, i) => {
        // 	    let html_element = menu.value[i];
        // 	    $(html_element).append(opt);
        // 	});
        // });
    }

    __expose({ load_menu });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", {
        class: "dropdown",
        ref_key: "dropdown",
        ref: dropdown,
        style: {"display":"inline-block"}
      }, [
        createBaseVNode("button", {
          class: normalizeClass(["btn dropdown-toggle", button_class_2.value]),
          style: normalizeStyle(__props.button_style),
          type: "button",
          id: __props.id,
          ref_key: "dropdown_button",
          ref: dropdown_button,
          "aria-expanded": "false",
          "data-bs-toggle": "dropdown"
        }, [
          renderSlot(_ctx.$slots, "title")
        ], 14 /* CLASS, STYLE, PROPS */, _hoisted_1$2l),
        createBaseVNode("ul", {
          class: "dropdown-menu",
          "aria-labelledby": __props.id,
          style: {"max-height":"25rem","overflow":"auto"}
        }, [
          createCommentVNode(" <slot name=\"menu\"></slot> "),
          createCommentVNode(" <li class=\"dropdown-item\" v-for=\"(opt, i) in options\" :ref=\"el => { menu[i] = el }\"> "),
          createCommentVNode("   asd "),
          createCommentVNode("   </li> "),
          (openBlock(true), createElementBlock(Fragment, null, renderList(menu_options.value, (opt, i) => {
            return (openBlock(), createElementBlock("li", _hoisted_3$1W, [
              createVNode(script$2C, { content: opt }, null, 8 /* PROPS */, ["content"])
            ]))
          }), 256 /* UNKEYED_FRAGMENT */))
        ], 8 /* PROPS */, _hoisted_2$28)
      ], 512 /* NEED_PATCH */))
    }
    }

    };

    script$2B.__file = "http_src/vue/dropdown.vue";

    const _hoisted_1$2k = {
      class: "row",
      style: {"margin-left":"-2px","margin-right":"-2px","margin-top":"1rem","margin-bottom":"-1rem"}
    };
    const _hoisted_2$27 = { class: "col-sm-12 col-md-5" };
    const _hoisted_3$1V = {
      class: "dataTables_info",
      role: "status",
      "aria-live": "polite"
    };
    const _hoisted_4$1R = { class: "col-sm-12 col-md-7" };
    const _hoisted_5$1z = {
      class: "dataTables_paginate paging_simple_numbers",
      style: {"display":"flex","justify-content":"flex-end"}
    };
    const _hoisted_6$1k = { class: "pagination" };
    const _hoisted_7$1d = { class: "paginate_button page-item previous" };
    const _hoisted_8$13 = ["onClick"];
    const _hoisted_9$U = {
      href: "javascript:void(0);",
      "aria-controls": "default-datatable",
      "data-dt-idx": "1",
      tabindex: "0",
      class: "page-link"
    };
    const _hoisted_10$O = { class: "paginate_button page-item previous" };
    const max_page_buttons = 6;

    const text_template = "Showing page %active_page of %total_pages: total %total_rows rows";

    var script$2A = {
      __name: 'select_table_page',
      props: {
        total_rows: Number,
        per_page: Number,
    },
      emits: ['change_active_page'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const props = __props;

    const emit = __emit;

    const start_page_button = ref(0);
    const num_page_buttons = ref(0);

    const total_pages = ref(0);
    const active_page = ref(0);

    const text = ref("");

    onMounted(() => {
        calculate_pages();
    });

    watch(() => [props.total_rows, props.per_page], (cur_value, old_value) => {
        calculate_pages();
    }, { flush: 'pre', immediate: true });

    function calculate_pages() {
        if (props.total_rows == null) { return; }
        let per_page = props.per_page;
        total_pages.value = Number.parseInt((props.total_rows + per_page - 1) / per_page);
        num_page_buttons.value = max_page_buttons;
        if (total_pages.value < num_page_buttons.value) {
    	    num_page_buttons.value = total_pages.value;
        }
        if (active_page.value >= total_pages.value && total_pages.value > 0) {
          //	total_pages.value = total_pages.value + 1;
          /* In case the current active page is higher than the max pages, restart from page 1 */
          active_page.value = total_pages.value - 1;
          start_page_button.value = total_pages.value - num_page_buttons.value;
          /* Redundant call in order to correctly load pages */
          change_active_page(active_page.value);
        }

        set_text();
    }

    const enable_first_page = computed$1(() => {
        if (total_pages.value < max_page_buttons) {
    	return false;
        }
        return active_page.value >= num_page_buttons.value - 1;
    });

    const enable_last_page = computed$1(() => {
        if (total_pages.value < max_page_buttons) {
    	return false;
        }
        return active_page.value < total_pages.value - num_page_buttons.value + 1;
    });


    function next_page() {
        change_active_page(active_page.value + 1);
    }

    function back_page() {
        change_active_page(active_page.value - 1);
    }

    /*  This function is used to set the current active page, if no params is passed
        then it's going to keep the same page.
        This function handles the case where the active page > last page,
        setting the active page as the last page.
     */
    function change_active_page(new_active_page, new_start_page_button) {
      /* In case a new active page is requested, jump to that page */
      if (new_active_page != null) {
        active_page.value = new_active_page;
      } 

      /* Change the table footer button */
      if (new_start_page_button != null) {
        start_page_button.value = new_start_page_button;
      }

      /* Set up the correct start and end page of the table footer */
      if (active_page.value == start_page_button.value && start_page_button.value > 0) {
        start_page_button.value -= 1;
      }
      const end_page_button = start_page_button.value + num_page_buttons.value - 1;
      if (active_page.value == end_page_button && total_pages.value - 1 > end_page_button) {
        start_page_button.value += 1;	
      }
      
      /* Check that the active_page is not greater then the last page */
      /* otherwise set to the last page */
      if(active_page.value > total_pages.value - 1 && total_pages.value != 0) {
        active_page.value = total_pages.value - 1;
        start_page_button.value = active_page.value;
      }

      /* Set the text on the table footer, num_pages, total_rows, ecc. */
      set_text();

      /* Emit the change_active_page event */
      emit('change_active_page', active_page.value);
    }

    function set_text() {
        text.value = text_template.replace("%active_page", format_number(`${active_page.value + 1}`))
    	.replace("%total_pages", format_number(`${total_pages.value}`))
    	.replace("%total_rows", format_number(`${props.total_rows}`))
    	.replace("%per_page", format_number(`${props.per_page}`));
    }

    function format_number(s) {
        return s.replace(/(.)(?=(\d{3})+$)/g,'$1,');
    }

    __expose({ change_active_page });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createCommentVNode(" PerPage "),
        createBaseVNode("div", _hoisted_1$2k, [
          createCommentVNode(" div col-md-5 "),
          createBaseVNode("div", _hoisted_2$27, [
            createBaseVNode("div", _hoisted_3$1V, toDisplayString(text.value), 1 /* TEXT */)
          ]),
          createCommentVNode(" div col-md-5 "),
          createCommentVNode(" start div col-sm-12 "),
          withDirectives(createBaseVNode("div", _hoisted_4$1R, [
            createBaseVNode("div", _hoisted_5$1z, [
              createBaseVNode("ul", _hoisted_6$1k, [
                withDirectives(createBaseVNode("li", _hoisted_7$1d, [
                  createBaseVNode("a", {
                    href: "javascript:void(0);",
                    onClick: _cache[0] || (_cache[0] = $event => (change_active_page(0, 0))),
                    "aria-controls": "default-datatable",
                    "data-dt-idx": "0",
                    tabindex: "0",
                    class: "page-link"
                  }, " « ")
                ], 512 /* NEED_PATCH */), [
                  [vShow, enable_first_page.value]
                ]),
                createBaseVNode("li", {
                  class: normalizeClass(["paginate_button page-item previous", { 'disabled': active_page.value == 0}])
                }, [
                  createBaseVNode("a", {
                    href: "javascript:void(0);",
                    onClick: _cache[1] || (_cache[1] = $event => (back_page())),
                    "aria-controls": "default-datatable",
                    "data-dt-idx": "0",
                    tabindex: "0",
                    class: "page-link"
                  }, " < ")
                ], 2 /* CLASS */),
                (openBlock(true), createElementBlock(Fragment, null, renderList(num_page_buttons.value, (n) => {
                  return (openBlock(), createElementBlock("li", {
                    onClick: $event => (change_active_page(start_page_button.value + n - 1)),
                    class: normalizeClass([{'active': active_page.value == start_page_button.value + n - 1 }, "paginate_button page-item"])
                  }, [
                    createBaseVNode("a", _hoisted_9$U, toDisplayString(start_page_button.value + n), 1 /* TEXT */),
                    createCommentVNode("  :key=\"total_rows\"")
                  ], 10 /* CLASS, PROPS */, _hoisted_8$13))
                }), 256 /* UNKEYED_FRAGMENT */)),
                createBaseVNode("li", {
                  class: normalizeClass(["paginate_button page-item next", { 'disabled': active_page.value == total_pages.value - 1}]),
                  id: "default-datatable_next"
                }, [
                  createBaseVNode("a", {
                    href: "javascript:void(0);",
                    onClick: _cache[2] || (_cache[2] = $event => (next_page())),
                    "aria-controls": "default-datatable",
                    "data-dt-idx": "7",
                    tabindex: "0",
                    class: "page-link"
                  }, " > ")
                ], 2 /* CLASS */),
                withDirectives(createBaseVNode("li", _hoisted_10$O, [
                  createBaseVNode("a", {
                    href: "javascript:void(0);",
                    onClick: _cache[3] || (_cache[3] = $event => (change_active_page(total_pages.value - 1, total_pages.value - num_page_buttons.value))),
                    "aria-controls": "default-datatable",
                    "data-dt-idx": "0",
                    tabindex: "0",
                    class: "page-link"
                  }, " » ")
                ], 512 /* NEED_PATCH */), [
                  [vShow, enable_last_page.value]
                ])
              ])
            ])
          ], 512 /* NEED_PATCH */), [
            [vShow, total_pages.value > 0]
          ]),
          createCommentVNode(" end div col-md-7 ")
        ]),
        createCommentVNode(" PerPage ")
      ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
    }
    }

    };

    script$2A.__file = "http_src/vue/select_table_page.vue";

    const _hoisted_1$2j = {
      class: "form-check form-switch ms-1",
      style: {"white-space":"nowrap"}
    };
    const _hoisted_2$26 = ["checked", "title"];
    const _hoisted_3$1U = ["innerHTML"];


    var script$2z = {
      __name: 'switch',
      props: {
        value: Boolean,
        title: String,
        label: String,
    },
      emits: ['update:value', 'change_value'],
      setup(__props, { emit: __emit }) {

    const emit = __emit;

    const props = __props;

    const value_2 = ref(false);

    onMounted(() => {
        value_2.value = props.value;
    });

    watch(() => props.value, (cur_value, old_value) => {
        value_2.value = props.value;
    }, { flush: 'pre'});

    function change_value() {
        emit('update:value', !value_2.value);
        emit('change_value', !value_2.value);
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$2j, [
        createBaseVNode("input", {
          class: "form-check-input",
          style: {"cursor":"pointer"},
          checked: value_2.value == true,
          onClick: _cache[0] || (_cache[0] = $event => (change_value())),
          type: "checkbox",
          id: "toggle-Begin",
          title: __props.title
        }, null, 8 /* PROPS */, _hoisted_2$26),
        createBaseVNode("label", {
          class: "form-check-label",
          for: "toggle-Begin",
          innerHTML: __props.label
        }, null, 8 /* PROPS */, _hoisted_3$1U)
      ]))
    }
    }

    };

    script$2z.__file = "http_src/vue/switch.vue";

    const _hoisted_1$2i = { class: "loading-content vertical" };
    const _hoisted_2$25 = { class: "loading-text" };

    /* *************************************** */


    var script$2y = {
      __name: 'loading',
      props: {
        styles: String,
        isLoading: Boolean,
    },
      setup(__props) {

    const loading = i18n('loading');
    const overlay = ref(null);
    const props = __props;

    function adjustLoadingPosition() {
        if (!overlay.value || !overlay.value.parentElement) {
            return
        }

        const parentHeight = overlay.value.parentElement.getBoundingClientRect();
        const viewportHeight = window.innerHeight;

        /* Height higher then viewpoint, add the fixed one */
        if (parentHeight.height > viewportHeight * 0.7) {
            overlay.value?.classList.add("fixed");
        } else {
            overlay.value?.classList.remove("fixed");
        }
    }

    /* *************************************** */

    /* By default already show the loading */
    onMounted(() => {
        if (props.isLoading) {
            adjustLoadingPosition();
            showLoading();
            window.addEventListener("resize", () => adjustLoadingPosition());
        }
    });

    /* *************************************** */

    watch(() => [props.isLoading], (cur_value, old_value) => {
        if (props.isLoading) {
            showLoading(); /* isLoading is true, show the loading css */
        } else {
            hideLoading(); /* isLoading is false, hide the loading css */
        }
    }, { flush: 'pre' });

    /* *************************************** */

    /* Show the loading */
    function showLoading() {
        overlay.value?.classList.add("show");
    }

    /* *************************************** */

    /* Hide the loading */
    function hideLoading() {
        overlay.value?.classList.remove("show");
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", {
        ref_key: "overlay",
        ref: overlay,
        class: "loading-overlay"
      }, [
        createBaseVNode("div", _hoisted_1$2i, [
          createBaseVNode("div", {
            class: "loading-spinner",
            style: normalizeStyle(__props.styles)
          }, null, 4 /* STYLE */),
          createBaseVNode("div", _hoisted_2$25, toDisplayString(unref(loading)), 1 /* TEXT */)
        ])
      ], 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$A = "\n.loading-overlay[data-v-0f1b3f42] {\n    position: absolute;\n    inset: 0;\n    /* top: 0; left: 0; bottom: 0; right: 0; */\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    z-index: 999;\n    /* Bootstrap max overlay is 999 */\n    opacity: 0;\n    pointer-events: none;\n    transition: opacity 1s ease;\n}\n.loading-overlay[data-v-0f1b3f42] {\n    background-color: rgba(15, 23, 42, 0.9);\n}\n.loading-overlay.show[data-v-0f1b3f42] {\n    opacity: 1;\n    pointer-events: all;\n}\n.loading-spinner[data-v-0f1b3f42] {\n    width: 40px;\n    height: 40px;\n    border: 3px solid #334155;\n    border-radius: 50%;\n    border-top-color: var(--ntop-orange);\n    /* Spinning part ntop orange */\n    animation: spin-0f1b3f42 1s ease-in-out infinite;\n    margin-bottom: 12px;\n}\n.loading-text[data-v-0f1b3f42] {\n    color: #e2e8f0;\n    font-size: 14px;\n    letter-spacing: 1px;\n}\n.loading-content[data-v-0f1b3f42] {\n  display: flex;\n  align-items: center;\n}\n.loading-content.vertical[data-v-0f1b3f42] {\n  display: flex;\n  flex-direction: column; \n  align-items: center;\n}\n\n/* Variante fixed visibile ovunque */\n.loading-overlay.fixed[data-v-0f1b3f42] {\n    padding-top: min(20%, 100px);\n    align-items: normal !important;\n}\n\n/* Light theme */\n:root[data-theme=\"light\"] .loading-overlay[data-v-0f1b3f42],\n[data-theme=\"light\"] .loading-overlay[data-v-0f1b3f42] {\n    background-color: rgba(243, 244, 246, 0.9);\n}\n:root[data-theme=\"light\"] .loading-spinner[data-v-0f1b3f42],\n[data-theme=\"light\"] .loading-spinner[data-v-0f1b3f42] {\n    border: 3px solid #6b7280;\n    border-top-color: var(--ntop-orange);\n}\n:root[data-theme=\"light\"] .loading-text[data-v-0f1b3f42],\n[data-theme=\"light\"] .loading-text[data-v-0f1b3f42] {\n    color: #374151;\n}\n@keyframes spin-0f1b3f42 {\nto {\n        transform: rotate(360deg);\n}\n}\n";
    styleInject(css_248z$A);

    script$2y.__scopeId = "data-v-0f1b3f42";
    script$2y.__file = "http_src/vue/loading.vue";

    const _hoisted_1$2h = ["id"];
    const _hoisted_2$24 = { class: "button-group mb-2 d-flex align-items-center" };
    const _hoisted_3$1T = {
      class: "form-group d-flex align-items-end",
      style: {"flex-wrap":"wrap"}
    };
    const _hoisted_4$1Q = {
      style: {"text-align":"right"},
      class: "form-group d-flex align-items-center ms-auto"
    };
    const _hoisted_5$1y = { class: "d-flex align-items-center" };
    const _hoisted_6$1j = { class: "me-2" };
    const _hoisted_7$1c = ["value"];
    const _hoisted_8$12 = ["title"];
    const _hoisted_9$T = ["title"];
    const _hoisted_10$N = {
      key: 0,
      class: "d-inline-block"
    };
    const _hoisted_11$I = ["title"];
    const _hoisted_12$D = { class: "form-check form-switch ms-1" };
    const _hoisted_13$x = ["checked", "onClick", "id"];
    const _hoisted_14$t = ["for", "innerHTML"];
    const _hoisted_15$q = {
      key: 1,
      class: "d-inline me-2 ms-auto"
    };
    const _hoisted_16$q = {
      key: 0,
      class: "centered-message"
    };
    const _hoisted_17$n = ["innerHTML"];
    const _hoisted_18$k = ["data-resizable-columns-id"];
    const _hoisted_19$j = ["onClick", "data-resizable-column-id"];
    const _hoisted_20$j = { style: {"display":"flex"} };
    const _hoisted_21$j = ["innerHTML"];
    const _hoisted_22$j = { class: "fa fa-fw fa-sort-up" };
    const _hoisted_23$j = { class: "fa fa-fw fa-sort-down" };
    const _hoisted_24$g = ["innerHTML"];
    const _hoisted_25$f = {
      key: 0,
      style: {},
      class: "",
      scope: "col"
    };
    const _hoisted_26$d = {
      key: 0,
      class: "mt-2"
    };
    const _hoisted_27$d = { class: "text-end" };
    const _hoisted_28$b = {
      style: {},
      class: "query text-end"
    };
    const _hoisted_29$b = { class: "records" };
    const _hoisted_30$b = { class: "text-start" };
    const _hoisted_31$9 = {
      id: "historical_flows_table-query-time",
      style: {},
      class: "query"
    };
    const _hoisted_32$8 = { class: "seconds" };
    const _hoisted_33$8 = ["title"];

    /* rows_loaded, is emitted every time the rows are loaded,
     * loaded,      is emitted when the table is loaded (mounted)
     */

    var script$2x = {
      __name: 'table',
      props: {
        id: String,                     // table id   
        columns: Array,                 // table columns
        get_rows: Function,             // async (currentPage: number, rowsPerPage: number, processedColumns: any[], search_map: string, isFirstDataLoad: boolean) => { totalRowCount: number, rows: any[], query_info: { query_duration_msec: number, num_records_processed: string, query: string } }
        get_column_id: Function,        // function to 
        print_column_name: Function,    // Function to render column header text/HTML
        print_html_row: Function,       // Function to render cell HTML content
        print_vue_node_row: Function,   // Function to render Vue component in cell
        f_is_column_sortable: Function, // Function to determine if column is sortable
        f_column_min_width: Function,   // Function to determine column minimum width
        f_column_max_width: Function,   // Function to determine column maximum width
        f_sort_rows: Function,          // Function for custom row sorting
        f_get_column_classes: Function, // Function to get CSS classes for column
        f_get_column_style: Function,   // Function to get inline styles for column
        enable_search: Boolean,         // Enable search functionality
        display_empty_rows: Boolean,    // Show empty rows to maintain table height  
        show_autorefresh: Number,       // autorefresh seconds, if null or 0 autorefresh switch will not showed
        default_sort: Object,           // { column_id: string, sort: number (0, 1, 2) }
        csrf: String,                   // CSRF token for API calls
        paging: Boolean,                // Enable server-side pagination
        display_message: Boolean,       // Display a message instead of the table
        message_to_display: String,     // Message to display    
        showLoading: Boolean,
    },
      emits: ['custom_event', 'loaded', 'rows_loaded'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const emit = __emit;
    const vue_obj = {
        emit,
        h: h$2,
        nextTick,
    };

    const props = __props;

    // Get number of rows shown per page from local storage
    const get_num_pages = function () {
        return Number(localStorage.getItem("ntopng.tables.rowPerPage")) || 10;
    };

    const _i18n = (t) => i18n(t);

    const tableContainerRef = ref(null);                   // Reference to the table container
    const tableRef = ref(null);                             // Reference to the table element
    const dropdownRef = ref(null);                          // Reference to the column visibility dropdown
    ref([]);                   // References to row HTML elements
    const showLoading = ref(props.showLoading);
    let currentPage = 0;                                 // Current active page
    let allRows = [];                                       // All fetched rows data
    let lastRes = [];                                       // Last fetched response
    const processedColumns = ref([]);                        // Wrapped column definitions with extra properties
    const displayedRows = ref([]);                         // Rows displayed in the current page
    const totalRowCount = ref(0);                           // Total number of rows (for pagination)
    const rowsPerPageOptions = [10, 20, 40, 50, 80, 100];  // Available rows per page options
    const rowsPerPage = ref(get_num_pages());               // Current rows per page setting
    const columnWidthStore = window.store;                          // Store for column width persistence
    const searchString = ref("");                          // Search term
    const isLoading = ref(showLoading.value ? showLoading.value : false);

    const paginationRef = ref(null);                 // Reference to pagination component
    const query_info = ref(null);                        // Query execution info (time, records, SQL)
    const sqlButtonRef = ref(null);             // Reference to SQL button for copy to clipboard
    const isChangingColumnVisibility = ref(false);       // Flag for column visibility changes
    const isChangingRows = ref(false);                    // Flag for row data changes
    const isAutoRefreshEnabled = ref(false);               // Auto-refresh state

    onMounted(async () => {
        if (props.columns != null) {
            load_table();
        }
    });

    // refresh bootstrap tooltip
    onUpdated(() => {
        NtopUtils$1.reloadBSTooltips();
    });

    // autorefresh tooltip text
    const autorefresh_title = computed$1(() => {
        if (props.show_autorefresh == null || props.show_autorefresh <= 0) {
            return "";
        }
        let text = _i18n("table.autorefresh");
        return text.replace("%time", props.show_autorefresh);
    });

    watch(() => [props.id, props.columns], (cur_value, old_value) => {
        load_table();
    }, { flush: 'pre' });

    // get column id as defined in the json of the table definition
    function get_col_id(col) {
        if (col != null && col.id != null) {
            return col.id;
        } else {
            return "toggle-Begin";
        }
    }

    function set_dropdown_listener() {
        document.querySelectorAll('.dropdown').forEach(dropdown => {
            dropdown.addEventListener('show.bs.dropdown', function () {
                this.closest('td')?.classList.add('dropdown-active');
            });

            dropdown.addEventListener('hide.bs.dropdown', function () {
                this.closest('td')?.classList.remove('dropdown-active');
            });
        });
    }

    // init table with columns and rows
    async function load_table() {
        await set_columns_wrap();       // Prepare column definitions
        await set_rows();               // Fetch initial rows
        set_columns_resizable();        // Initialize resizable columns
        dropdownRef.value.load_menu();     // Initialize dropdown menu
        set_dropdown_listener();
        emit("loaded");                 // Emit loaded event
    }

    // autorefresh interval and enabling
    let refreshInterval;
    function update_autorefresh() {
        if (isAutoRefreshEnabled.value == false) {
            clearInterval(refreshInterval);
            return;
        }
        refreshInterval = setInterval(() => {
            change_active_page();
        }, props.show_autorefresh * 1000);
    }

    async function change_columns_visibility(col) {
        isChangingColumnVisibility.value = true;
        col.visible = !col.visible;

        // if server side pagination, get rows
        if (props.paging) {
            await set_rows();
        }

        // redraw with new visibility
        // redraw_table();
        await redraw_table_resizable();
        await set_columns_visibility();
        // set_columns_resizable();
        isChangingColumnVisibility.value = false;
    }

    async function redraw_table_resizable() {
        await redraw_table();
        set_columns_resizable();
    }

    // increase table key to force vuejs to rerender table
    const table_key = ref(0);
    async function redraw_table() {
        table_key.value += 1;
        await nextTick();
    }

    function set_columns_resizable() {
        let options = {
            store: columnWidthStore, // persist column width
            minWidth: 70,
        };
        $(tableRef.value).resizableColumns(options);
    }

    // get table configuration
    async function get_columns_visibility_dict() {
        if (props.csrf == null) { return {}; }

        // build request parameters
        // fetches tableId (in httpdocs/tables_config/<tableId>.json where the table scheme is defined)
        const params = { table_id: props.id };
        const url_params = ntopng_url_manager$1.obj_to_url_params(params);
        const url = `${http_prefix}/lua/rest/v2/get/tables/user_columns_config.lua?${url_params}`;
        let columns_visible = await ntopng_utility$1.http_request(url);
        let columns_visible_dict = {};

        // convert to dictionary
        columns_visible.forEach((c) => {
            columns_visible_dict[c.id] = c;
        });

        return columns_visible_dict;
    }

    // save columns visibility in backend
    async function set_columns_visibility() {
        if (props.csrf == null) { return; }

        // prepare request parameters with column configuration
        let params = { table_id: props.id, visible_columns_ids: [], csrf: props.csrf };
        params.visible_columns_ids = processedColumns.value.map((c, i) => {
            return {
                id: c.id,
                visible: c.visible,
                order: c.order,
                sort: c.sort,
            };
        });

        // post configuration to server
        const url = `${http_prefix}/lua/rest/v2/add/tables/user_columns_config.lua`;
        await ntopng_utility$1.http_post_request(url, params);
    }

    // preapre column definitions with visibility and sort 
    async function set_columns_wrap() {

        // get configuration
        let cols_visibility_dict = await get_columns_visibility_dict();

        // check if table has sorting enabled
        let is_table_not_sorted = true;
        for (let id in cols_visibility_dict) {
            is_table_not_sorted &= (cols_visibility_dict[id]?.sort);
        }

        // process column definitions
        processedColumns.value = props.columns.map((c, i) => {
            let classes = [];
            let style = "";

            // get column CSS class from json config
            if (props.f_get_column_classes != null) {
                classes = props.f_get_column_classes(c);
            }
            // get column CSS style from json config
            if (props.f_get_column_style != null) {
                style = props.f_get_column_style(c);
            }

            // get column id and condiguration
            let id = props.get_column_id(c);
            let col_opt = cols_visibility_dict[id];

            // determine sort state, 0 = no sort, 1 = asc, 2 = desc
            let sort = col_opt?.sort;
            if (is_table_not_sorted == true && sort == null && props.default_sort != null && id == props.default_sort.column_id) {
                sort = props.default_sort.sort;
            } else if (col_opt?.sort) {
                sort = col_opt?.sort;
            } else {
                sort = 0;
            }
            return {
                id,
                visible: col_opt?.visible == null || col_opt?.visible == true, // defaults column to visible
                sort: sort,
                sortable: is_column_sortable(c),
                min_width: column_min_width(c),
                max_width: column_max_width(c),
                order: col_opt?.order || i,
                sticky: c?.sticky,
                classes,
                style,
                data: c,
            };
        });

        // save column config to server
        await set_columns_visibility();
    }

    // reset all column widths to default
    async function reset_column_size() {
        props.columns.forEach((c) => {
            let id = `${props.id}-${props.get_column_id(c)}`;
            columnWidthStore.remove(id); // remove saved width from local storage
        });
        await redraw_table_resizable();
    }

    // handle row number count per page
    function change_per_page() {
        save_num_pages();
        redraw_select_pages();
        change_active_page(0);
    }

    // save rows count per page to local storage
    const save_num_pages = function () {
        localStorage.setItem("ntopng.tables.rowPerPage", rowsPerPage.value);
    };

    // force pagination to redraw
    const searchDelay = ref(0);
    function redraw_select_pages() {
        searchDelay.value += 1;
    }


    // force table content to redraw
    const table_content_id = ref(0);
    function refresh_table_content() {
        table_content_id.value += 1;
    }


    // change pagination active page and update displayed rows
    async function change_active_page(new_active_page) {
        if (new_active_page != null) {
            currentPage = new_active_page;
        }
        if (currentPage == null) {
            currentPage = 0;
        }

        // fetch new rows if paging is enabled
        if (props.paging == true || shouldForceRefresh) {
            await set_rows();
        } else {
            set_active_rows();
        }
        refresh_table_content();
        set_dropdown_listener();
    }

    // handle column sort
    async function change_column_sort(col, col_index) {
        if (!col.sortable) {
            return;
        }

        // cycle states: no sort -> asc desc -> none
        col.sort = (col.sort + 1) % 3;

        // reset sort on all other columns
        processedColumns.value.filter((c, i) => i != col_index).forEach((c) => c.sort = 0);

        // no sort, stop
        if (col.sort == 0) { return; }

        if (props.paging) {
            await set_rows(); // server side sorting
        } else {
            set_active_rows(); // client side sorting
        }
        await set_columns_visibility();
        set_dropdown_listener();
    }


    // get sort function for available functions
    function get_sort_function() {
        if (props.f_sort_rows != null) {
            return props.f_sort_rows;
        }

        // default sort strings
        return (col, r0, r1) => {
            let r0_col = props.print_html_row(col.data, r0);
            let r1_col = props.print_html_row(col.data, r1);
            if (col.sort == 1) {
                return r0_col.localeCompare(r1_col); // ascending
            }
            return r1_col.localeCompare(r0_col);     // descending
        };
    }

    let shouldForceRefresh = false;

    /* ********************************************* */

    /*  Refresh table contents
        - If disable_loading is true, no loading indicator will be shown
        - If disable_loading is true, current page will be maintained
    */

    async function refresh_table(disable_loading) {
        /* NOTE: first refresh_table is called then set_rows */
        shouldForceRefresh = true;
        const shouldDisableLoading = disable_loading || false;

        if (shouldDisableLoading) {
            disableLoading();
            /* In case of disabled loading, reload the same page */
            paginationRef.value.change_active_page();
        } else {
            /* Otherwise reload from page 1 */
            paginationRef.value.change_active_page(0, 0);
        }
        await nextTick();

        /* Reset the refresh/loading params */
        shouldForceRefresh = false;

        if (shouldDisableLoading) {
            enableLoading();
        }
    }

    /* ********************************************* */

    let isFirstDataLoad = true;

    // get and update rows data
    async function set_rows(do_not_reload) {
        if (showLoading.value) {
            isLoading.value = true;
        }
        let res = null;
        if (props.paging !== true && do_not_reload === true) {
            // If no reload is requested and the paging is false,
            // simply use the old rows
            res = JSON.parse(JSON.stringify(lastRes)); // Create a deep copy of the request
        } else {
            // get rows from backend
            res = await props.get_rows(
                currentPage,                // current page
                rowsPerPage.value,          // rows per page
                processedColumns.value,     // columns definition
                searchString.value,         // search term
                isFirstDataLoad             // first load
            );
            // store fetched rows and update displayed rows 
            lastRes = res;
        }

        // update query info if available
        query_info.value = null;
        if (res.query_info != null) {
            query_info.value = res.query_info;
        }

        isFirstDataLoad = false;

        // If we're not using server-side paging, we need to filter the rows client-side
        if (props.paging !== true && searchString.value.trim() !== '') {
            res.rows = filterRows(res.rows, searchString.value);
        }

        // update total rows count
        totalRowCount.value = res.rows.length;
        if (props.paging == true) {
            totalRowCount.value = res.total_rows; // use number of rows provided by server
        }

        allRows = res.rows;
        set_active_rows();

        // wait for dom to update and emit event
        await nextTick();
        emit('rows_loaded', res);
        if (showLoading.value) {
            isLoading.value = false;
        }
    }

    // New function to filter rows based on search string
    function filterRows(rows, searchTerm) {
        if (!searchTerm || searchTerm.trim() === '') {
            return rows;
        }

        searchTerm = searchTerm.toLowerCase();

        return rows.filter(row => {
            // Check each visible column for the search term
            return processedColumns.value.some(col => {
                if (!col.visible) return false;

                let cellContent = '';

                // Get cell content based on how it's rendered
                if (props.print_html_row && props.print_html_row(col.data, row, true) != null) {
                    // For HTML content, get the rendered text and strip HTML tags
                    const htmlContent = props.print_html_row(col.data, row);
                    cellContent = htmlContent ? stripHtmlTags(htmlContent).toLowerCase() : '';
                } else if (props.print_vue_node_row && props.print_vue_node_row(col.data, row, vue_obj, true) != null) {
                    // For Vue nodes, we might need to extract text differently
                    // This is a simplification and might need adjustment based on your Vue node structure
                    const vueContent = props.print_vue_node_row(col.data, row, vue_obj);
                    cellContent = typeof vueContent === 'string' ? vueContent.toLowerCase() : '';
                } else {
                    // Fallback: try to access the raw value from the row using column data
                    const rawValue = row[col.data];
                    cellContent = rawValue ? String(rawValue).toLowerCase() : '';
                }

                return cellContent.includes(searchTerm);
            });
        });
    }

    function stripHtmlTags(html) {
        if (!html) return '';
        html = html.toString();
        return html.replace(/<\/?[^>]+(>|$)/g, " ").replace(/\s+/g, " ").trim();
    }

    // determine if column is sortable
    function is_column_sortable(col) {
        if (props.f_is_column_sortable != null) {
            return props.f_is_column_sortable(col); // use custom function to sort
        }
        return true;
    }

    // determine column minimum width
    function column_min_width(col) {
        if (props.f_column_min_width != null) {
            return props.f_column_min_width(col); // use custom function for min width
        }
        return true;
    }

    // determine column minimum width
    function column_max_width(col) {
        if (props.f_column_max_width != null) {
            return props.f_column_max_width(col); // use custom function for min width
        }
        return true;
    }

    // set displayed columns for current page
    function set_active_rows() {
        let start_row_index = 0;

        // start index for client side pagination
        if (props.paging == false) {
            start_row_index = currentPage * rowsPerPage.value;
        }

        // sort rows client side if enabled
        if (props.paging == false) {
            let f_sort = get_sort_function();
            let col_to_sort = get_column_to_sort();

            // sort rows if enabled

            if (col_to_sort) {
                allRows = allRows.sort((r0, r1) => {
                    return f_sort(col_to_sort, r0, r1);
                });
            }
        }

        // update displayed rows for current page
        displayedRows.value = allRows.slice(start_row_index, start_row_index + rowsPerPage.value);
    }

    // get columns with active sort
    function get_column_to_sort() {
        let col_to_sort = processedColumns.value.find((c) => c.sort != 0);
        if (col_to_sort) {
            localStorage.setItem(`tablesort_${props.id}`, col_to_sort.id);
            localStorage.setItem(`tablesort_${props.id}_sort`, col_to_sort.sort);
        } else {
            col_to_sort = localStorage.getItem(`tablesort_${props.id}`);
            const sort = localStorage.getItem(`tablesort_${props.id}_sort`);
            if (col_to_sort) {
                col_to_sort = processedColumns.value.find((c) => c.id === col_to_sort);
                if (!col_to_sort) {
                    col_to_sort = processedColumns.value[0];
                }
                col_to_sort.sort = sort;
            }
        }
        return col_to_sort;
    }

    // add timeout to search to prevent request flooding
    let map_search_change_timeout;
    async function on_change_map_search() {
        let timeout = 300; // Reduced timeout for better responsiveness

        // clear existing timeouts
        if (map_search_change_timeout != null) {
            clearTimeout(map_search_change_timeout);
        }

        map_search_change_timeout = setTimeout(async () => {
            if (props.paging) {
                // For server-side pagination, reset to first page when searching
                currentPage = 0;
                paginationRef.value.change_active_page(0, 0);
                await set_rows(false /* reload rows */); // get filtered rows
            }

            // Update pagination after search
            if (!props.paging) {
                await set_rows(true /* Skip reloading */); // get filtered rows
                redraw_select_pages();
            }

            map_search_change_timeout = null;
        }, timeout);
    }

    // set search value
    function search_value(value) {
        searchString.value = value; /* Add the new value */
        on_change_map_search(); // trigger search
    }

    // copy executed SQL query to clipboard
    function copy_query_into_clipboard($event) {
        NtopUtils$1.copyToClipboard(query_info.value.query, sqlButtonRef.value);
    }

    // get current column definition
    function get_columns_defs() {
        return processedColumns.value;
    }

    // get total number of rows
    function get_rows_num() {
        return totalRowCount.value;
    }

    const disableLoading = () => {
        showLoading.value = false;
    };

    const enableLoading = () => {
        showLoading.value = true;
    };

    // expose methods for parent components
    __expose({ load_table, refresh_table, get_columns_defs, get_rows_num, search_value, disableLoading, enableLoading });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        renderSlot(_ctx.$slots, "custom_header2"),
        createBaseVNode("div", {
          ref_key: "tableContainerRef",
          ref: tableContainerRef,
          id: __props.id
        }, [
          createVNode(script$2y, { isLoading: isLoading.value }, null, 8 /* PROPS */, ["isLoading"]),
          createBaseVNode("div", _hoisted_2$24, [
            createCommentVNode(" TableHeader "),
            createBaseVNode("div", _hoisted_3$1T, [
              createCommentVNode(" Slot for custom header"),
              renderSlot(_ctx.$slots, "custom_header")
            ]),
            createBaseVNode("div", _hoisted_4$1Q, [
              createBaseVNode("div", _hoisted_5$1y, [
                createCommentVNode(" Rows per page selector"),
                createBaseVNode("div", _hoisted_6$1j, [
                  createBaseVNode("label", null, [
                    withDirectives(createBaseVNode("select", {
                      "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((rowsPerPage).value = $event)),
                      onChange: change_per_page
                    }, [
                      (openBlock(), createElementBlock(Fragment, null, renderList(rowsPerPageOptions, (rowsPerPage) => {
                        return createBaseVNode("option", { value: rowsPerPage }, toDisplayString(rowsPerPage), 9 /* TEXT, PROPS */, _hoisted_7$1c)
                      }), 64 /* STABLE_FRAGMENT */))
                    ], 544 /* NEED_HYDRATION, NEED_PATCH */), [
                      [vModelSelect, rowsPerPage.value]
                    ])
                  ])
                ]),
                createCommentVNode(" Custom buttons slot "),
                renderSlot(_ctx.$slots, "custom_buttons"),
                createCommentVNode(" Reset columns size"),
                createBaseVNode("button", {
                  class: "btn btn-link",
                  type: "button",
                  onClick: reset_column_size
                }, [
                  createBaseVNode("i", {
                    class: "fas fa-columns",
                    "data-bs-toggle": "tooltip",
                    "data-bs-placement": "top",
                    title: _i18n('reset_column')
                  }, null, 8 /* PROPS */, _hoisted_8$12)
                ]),
                createCommentVNode(" Refresh table "),
                createBaseVNode("button", {
                  class: "btn btn-link",
                  type: "button",
                  onClick: _cache[1] || (_cache[1] = $event => (refresh_table()))
                }, [
                  createBaseVNode("i", {
                    class: "fas fa-refresh",
                    "data-bs-toggle": "tooltip",
                    "data-bs-placement": "top",
                    title: _i18n('refresh')
                  }, null, 8 /* PROPS */, _hoisted_9$T)
                ]),
                createCommentVNode(" Autorefresh toggle "),
                (__props.show_autorefresh > 0)
                  ? (openBlock(), createElementBlock("div", _hoisted_10$N, [
                      createVNode(script$2z, {
                        value: isAutoRefreshEnabled.value,
                        "onUpdate:value": _cache[2] || (_cache[2] = $event => ((isAutoRefreshEnabled).value = $event)),
                        class: "me-2 mt-1",
                        title: autorefresh_title.value,
                        style: {},
                        onChange_value: update_autorefresh
                      }, null, 8 /* PROPS */, ["value", "title"])
                    ]))
                  : createCommentVNode("v-if", true),
                createVNode(script$2B, {
                  id: __props.id + '_dropdown',
                  ref_key: "dropdownRef",
                  ref: dropdownRef
                }, {
                  title: withCtx(() => [
                    createBaseVNode("i", {
                      class: "fas fa-eye",
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      title: _i18n('visible_columns')
                    }, null, 8 /* PROPS */, _hoisted_11$I)
                  ]),
                  menu: withCtx(() => [
                    (openBlock(true), createElementBlock(Fragment, null, renderList(processedColumns.value, (col) => {
                      return (openBlock(), createElementBlock("div", _hoisted_12$D, [
                        createBaseVNode("input", {
                          class: "form-check-input",
                          style: {"cursor":"pointer"},
                          checked: col.visible == true,
                          onClick: $event => (change_columns_visibility(col)),
                          type: "checkbox",
                          id: get_col_id(col)
                        }, null, 8 /* PROPS */, _hoisted_13$x),
                        createBaseVNode("label", {
                          class: "form-check-label",
                          for: get_col_id(col),
                          innerHTML: __props.print_column_name(col.data)
                        }, null, 8 /* PROPS */, _hoisted_14$t)
                      ]))
                    }), 256 /* UNKEYED_FRAGMENT */))
                  ]),
                  _: 1 /* STABLE */
                }, 8 /* PROPS */, ["id"]),
                createCommentVNode(" Dropdown columns "),
                createCommentVNode(" Columns search if enabled in table json definition "),
                (__props.enable_search)
                  ? (openBlock(), createElementBlock("div", _hoisted_15$q, [
                      createBaseVNode("label", null, [
                        createTextVNode(toDisplayString(_i18n('search')) + ": ", 1 /* TEXT */),
                        withDirectives(createBaseVNode("input", {
                          type: "search",
                          "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((searchString).value = $event)),
                          onInput: on_change_map_search,
                          class: ""
                        }, null, 544 /* NEED_HYDRATION, NEED_PATCH */), [
                          [vModelText, searchString.value]
                        ])
                      ])
                    ]))
                  : createCommentVNode("v-if", true)
              ])
            ])
          ]),
          createCommentVNode(" TableHeader "),
          createVNode(Transition, {
            name: "list",
            mode: "out-in"
          }, {
            default: withCtx(() => [
              (openBlock(), createElementBlock("div", {
                key: table_key.value,
                style: {"overflow":"auto","width":"100%"}
              }, [
                createCommentVNode(" Table "),
                createCommentVNode(" Message display "),
                (__props.display_message == true)
                  ? (openBlock(), createElementBlock("div", _hoisted_16$q, [
                      createBaseVNode("span", { innerHTML: __props.message_to_display }, null, 8 /* PROPS */, _hoisted_17$n)
                    ]))
                  : createCommentVNode("v-if", true),
                createBaseVNode("table", {
                  ref_key: "tableRef",
                  ref: tableRef,
                  class: "table table-striped table-bordered ml-0 mr-0 mb-0 ntopng-table",
                  "data-resizable": "true",
                  "data-resizable-columns-id": __props.id
                }, [
                  createCommentVNode(" Table "),
                  createBaseVNode("thead", null, [
                    createBaseVNode("tr", null, [
                      createCommentVNode(" Column Headers "),
                      (openBlock(true), createElementBlock(Fragment, null, renderList(processedColumns.value, (col, col_index) => {
                        return (openBlock(), createElementBlock(Fragment, null, [
                          (col.visible)
                            ? (openBlock(), createElementBlock("th", {
                                key: 0,
                                scope: "col",
                                class: normalizeClass([
                                        { 'pointer': col.sortable, 'unset': !col.sortable, },
                                        { 'sticky-column-th': col.sticky }
                                    ]),
                                style: normalizeStyle([{"white-space":"nowrap"}, [
                                        (col.min_width ? 'min-width: ' + col.min_width + ';' : ''),
                                        (col.max_width ? 'max-width: ' + col.max_width + ';' : ''),
                                    ]]),
                                onClick: $event => (change_column_sort(col, col_index)),
                                "data-resizable-column-id": __props.get_column_id(col.data)
                              }, [
                                createBaseVNode("div", _hoisted_20$j, [
                                  createCommentVNode(" Print column name "),
                                  createBaseVNode("span", {
                                    innerHTML: __props.print_column_name(col.data),
                                    class: "wrap-column"
                                  }, null, 8 /* PROPS */, _hoisted_21$j),
                                  createCommentVNode(" Sort indicators, 0 double arrow, else up or down"),
                                  createCommentVNode(" <i v-show=\"col.sort == 0\" class=\"fa fa-fw fa-sort\"></i> "),
                                  withDirectives(createBaseVNode("i", _hoisted_22$j, null, 512 /* NEED_PATCH */), [
                                    [vShow, col.sort == 1 && col.sortable]
                                  ]),
                                  withDirectives(createBaseVNode("i", _hoisted_23$j, null, 512 /* NEED_PATCH */), [
                                    [vShow, col.sort == 2 && col.sortable]
                                  ])
                                ])
                              ], 14 /* CLASS, STYLE, PROPS */, _hoisted_19$j))
                            : createCommentVNode("v-if", true)
                        ], 64 /* STABLE_FRAGMENT */))
                      }), 256 /* UNKEYED_FRAGMENT */))
                    ])
                  ]),
                  createBaseVNode("tbody", null, [
                    createCommentVNode(" Data rows "),
                    (!isChangingColumnVisibility.value && !isChangingRows.value)
                      ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(displayedRows.value, (row) => {
                          return (openBlock(), createElementBlock("tr", null, [
                            (openBlock(true), createElementBlock(Fragment, null, renderList(processedColumns.value, (col, col_index) => {
                              return (openBlock(), createElementBlock(Fragment, null, [
                                (col.visible)
                                  ? (openBlock(), createElementBlock("td", {
                                      key: 0,
                                      scope: "col",
                                      class: normalizeClass([
                                        { 'sticky-column-td': col.sticky }
                                    ])
                                    }, [
                                      createCommentVNode(" HTML content if provided "),
                                      (__props.print_html_row != null && __props.print_html_row(col.data, row, true) != null)
                                        ? (openBlock(), createElementBlock("div", {
                                            key: 0,
                                            class: normalizeClass([col.classes, "wrap-column"]),
                                            style: normalizeStyle(col.style),
                                            innerHTML: __props.print_html_row(col.data, row)
                                          }, null, 14 /* CLASS, STYLE, PROPS */, _hoisted_24$g))
                                        : createCommentVNode("v-if", true),
                                      createBaseVNode("div", {
                                        style: normalizeStyle([col.style, {}]),
                                        class: normalizeClass(["wrap-column margin-sm", col.classes])
                                      }, [
                                        createCommentVNode(" Vue node if provided "),
                                        (__props.print_vue_node_row != null && __props.print_vue_node_row(col.data, row, vue_obj, true) != null)
                                          ? (openBlock(), createBlock(script$2C, {
                                              key: row,
                                              content: __props.print_vue_node_row(col.data, row, vue_obj)
                                            }, null, 8 /* PROPS */, ["content"]))
                                          : createCommentVNode("v-if", true)
                                      ], 6 /* CLASS, STYLE */)
                                    ], 2 /* CLASS */))
                                  : createCommentVNode("v-if", true)
                              ], 64 /* STABLE_FRAGMENT */))
                            }), 256 /* UNKEYED_FRAGMENT */))
                          ]))
                        }), 256 /* UNKEYED_FRAGMENT */))
                      : createCommentVNode("v-if", true),
                    createCommentVNode(" Show empty rows if present "),
                    (__props.display_empty_rows && displayedRows.value.length < rowsPerPage.value)
                      ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList((rowsPerPage.value - displayedRows.value.length), (index) => {
                          return (openBlock(), createElementBlock("tr", null, [
                            (openBlock(true), createElementBlock(Fragment, null, renderList(processedColumns.value, (col, col_index) => {
                              return (openBlock(), createElementBlock(Fragment, null, [
                                (col.visible)
                                  ? (openBlock(), createElementBlock("td", _hoisted_25$f, [...(_cache[4] || (_cache[4] = [
                                      createBaseVNode("div", { class: "wrap-column" }, null, -1 /* CACHED */)
                                    ]))]))
                                  : createCommentVNode("v-if", true)
                              ], 64 /* STABLE_FRAGMENT */))
                            }), 256 /* UNKEYED_FRAGMENT */))
                          ]))
                        }), 256 /* UNKEYED_FRAGMENT */))
                      : createCommentVNode("v-if", true)
                  ])
                ], 8 /* PROPS */, _hoisted_18$k),
                createCommentVNode(" Table ")
              ]))
            ]),
            _: 1 /* STABLE */
          }),
          createBaseVNode("div", null, [
            createCommentVNode(" Pagination component, bottom right "),
            (openBlock(), createBlock(script$2A, {
              ref_key: "paginationRef",
              ref: paginationRef,
              key: searchDelay.value,
              total_rows: totalRowCount.value,
              per_page: rowsPerPage.value,
              onChange_active_page: change_active_page
            }, null, 8 /* PROPS */, ["total_rows", "per_page"]))
          ]),
          createCommentVNode(" SQL Query info footer, if present "),
          (query_info.value != null)
            ? (openBlock(), createElementBlock("div", _hoisted_26$d, [
                createBaseVNode("div", _hoisted_27$d, [
                  createBaseVNode("small", _hoisted_28$b, [
                    createBaseVNode("span", _hoisted_29$b, toDisplayString(query_info.value.num_records_processed), 1 /* TEXT */),
                    _cache[5] || (_cache[5] = createTextVNode(".", -1 /* CACHED */))
                  ])
                ]),
                createBaseVNode("div", _hoisted_30$b, [
                  createBaseVNode("small", _hoisted_31$9, [
                    _cache[6] || (_cache[6] = createTextVNode("Query performed in ", -1 /* CACHED */)),
                    createBaseVNode("span", _hoisted_32$8, toDisplayString((query_info.value.query_duration_msec / 1000).toFixed(3)), 1 /* TEXT */),
                    _cache[7] || (_cache[7] = createTextVNode(" seconds. ", -1 /* CACHED */)),
                    createBaseVNode("span", {
                      id: "historical_flows_table-query",
                      style: {"cursor":"pointer"},
                      class: "badge bg-secondary",
                      title: query_info.value.query,
                      onClick: copy_query_into_clipboard,
                      ref_key: "sqlButtonRef",
                      ref: sqlButtonRef
                    }, "SQL", 8 /* PROPS */, _hoisted_33$8)
                  ])
                ])
              ]))
            : createCommentVNode("v-if", true)
        ], 8 /* PROPS */, _hoisted_1$2h)
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    var css_248z$z = "\n/* Used just for the action column in case of dropdowns, otherwise the\n * visualization stack is incorrect, need to increment the z-index of the\n * selected td\n */\ntd.sticky-column-td.dropdown-active[data-v-61956e9e] {\n    z-index: 3 !important;\n}\n.list-move[data-v-61956e9e],\n\n.list-enter-active[data-v-61956e9e],\n.list-leave-active[data-v-61956e9e] {\n    transition: all 0.35s ease;\n}\n.list-enter-from[data-v-61956e9e] {\n    opacity: 0;\n    transform: translateX(-60px);\n    /* entra da sinistra */\n}\n.list-leave-to[data-v-61956e9e] {\n    opacity: 0;\n    transform: translateX(0);\n    /* esce verso destra */\n}\n\n/* ensure leaving items are taken out of layout flow so that moving\n   animations can be calculated correctly. */\n.list-leave-active[data-v-61956e9e] {\n    position: absolute;\n}\n";
    styleInject(css_248z$z);

    script$2x.__scopeId = "data-v-61956e9e";
    script$2x.__file = "http_src/vue/table.vue";

    /**
    	(C) 2022 - ntop.org
    */

    const _i18n = (t) => i18n(t);

    async function build_table(http_prefix, table_id, f_map_columns, f_get_extra_params_obj, f_on_get_rows) {
    	let epoch_end = Number.parseInt(Date.now() / 1000);
    	let table_def_url = `${http_prefix}/tables_config/${table_id}.json?epoch_end=${epoch_end}`;
    	let table_def = await ntopng_utility$1.http_request(table_def_url, null, null, true);
    	if (table_def == null) {
    		return {
    			id: table_id,
    			columns: [],
    		}
    	}
    	if (table_def.columns) {
    		table_def.columns.forEach((item, x) => {
    			if (item.button_def_array) {
    				item.button_def_array.forEach((button, y) => {
    					if (!button.class) {
    						button.class = [];
    					}
    				});
    			}
    			if (!item.class) {
    				item.class = [];
    			}
    		}); 
    	}
    	if (f_map_columns != null) {
    		table_def.columns = await f_map_columns(table_def.columns);
    	}
    	const table_config = {
    		id: table_id,
    		columns: table_def.columns,
    		get_rows: get_rows_func(table_def, f_get_extra_params_obj, f_on_get_rows),
    		get_column_id: get_column_id_func(table_def),
    		print_column_name: get_f_print_column_name(),
    		print_html_row: get_f_print_html_row(),
    		print_vue_node_row: get_f_print_vue_node_row(),
    		f_is_column_sortable: get_f_is_column_sortable(),
    		f_get_column_classes: get_f_get_column_classes(),
    		f_column_min_width: get_f_column_min_width(),
    		f_column_max_width: get_f_column_max_width(),
    		f_get_column_style: get_f_get_column_style(),
    		display_empty_rows: table_def.display_empty_rows,
    		enable_search: table_def.enable_search,
    		default_sort: table_def.default_sort,
    		show_autorefresh: table_def.show_autorefresh,
    		paging: table_def.paging,
    	};
    	return table_config;
    }

    function get_f_is_column_sortable(table_def) {
    	return (col) => {
    		return col.sortable;
    	};
    }

    function get_f_column_min_width(table_def) {
    	return (col) => {
    		return col["min-width"];
    	};
    }

    function get_f_column_max_width(table_def) {
    	return (col) => {
    		return col["max-width"];
    	};
    }

    function get_f_get_column_classes(table_def) {
    	return (col) => {
    		let classes = col?.class;
    		if (classes != null) { return classes; }
    		return [];
    	};
    }

    function get_f_get_column_style(table_def) {
    	return (col) => {
    		let style = col?.style;
    		if (style != null) { return style; }
    		return "";
    	};
    }

    function get_f_print_vue_node_row(table_def) {
    	const available_types = {
    		"button_list": get_f_print_v_node_buttons(true),
    		"button_array": get_f_print_v_node_buttons(false),
    	};
    	return (col, row, vue_obj, return_true_if_def) => {
    		if (col.render_v_node_type == null && col.render_v_func == null) { return null; }
    		if (return_true_if_def == true) { return true; }
    		if (col.render_v_func != null) {
    			return col.render_v_func(col, row, vue_obj, return_true_if_def);
    		}
    		let print_v_node_buttons = available_types[col.render_v_node_type];
    		return print_v_node_buttons(col, row, vue_obj);
    	};
    }

    function get_f_print_html_row(table_def) {
    	return (col, row, return_true_if_def) => {
    		if (col.render_v_func != null) { return null; }
    		if (return_true_if_def == true) { return true; }

    		let data;
    		if (col.data_field != null) {
    			data = row[col.data_field];
    		}
    		if (col.zero_is_null != null && data != null && data == 0) {
    			data = "";
    		}
    		if (col.render_generic != null) {
    			let render = DataTableRenders$1.getFormatGenericField(col.render_generic, col.zero_is_null);
    			return render(data, 'display', row);
    		}
    		if (col.render_func != null) {
    			return col.render_func(data, row);
    		}
    		if (col.render_type != null) {
    			if (formatterUtils.types[col.render_type] != null) {
    				col.render_func = formatterUtils.getFormatter(col.render_type);
    				return col.render_func(data);
    			} else if (DataTableRenders$1[col.render_type] != null) {
    				return DataTableRenders$1[col.render_type](data, 'display', row, col.zero_is_null);
    			} else {
    				throw `In column ${col.data_field} render_type: ${col.render_type} not found`;
    			}
    		}
    		return data;
    	};
    }

    /* This function apparently create the action buttons */
    function get_f_print_v_node_buttons(list_or_array) {
    	return (col, row, vue_obj) => {
    		const on_click = (id) => {
    			return (e) => {
    				e.stopPropagation();
    				let event = { event_id: id, row, col };
    				vue_obj.emit('custom_event', event);
    			}
    		};
    		let v_nodes = col.button_def_array.map((b_def) => {
    			let attributes = {};
    			if (b_def.attributes != null) {
    				attributes = b_def.attributes;
    			}
    			let class_array = ntopng_utility$1.clone(b_def.class || []);
    			if (b_def.f_map_class != null) {
    				class_array = b_def.f_map_class(class_array, row);
    			}
    			let _class = "";
    			if (class_array != null && class_array.length > 0) {
    				_class = class_array.join(" ");
    			}
    			if (list_or_array == false) {
    				return vue_obj.h("button", { class: `btn btn-sm btn-secondary ${_class}`, style: "margin-right:0.2rem;", onClick: on_click(b_def.event_id), ...attributes }, [vue_obj.h("span", { class: b_def.icon, style: "", title: _i18n(b_def.title_i18n) }),]);
    			}
    			return vue_obj.h("a", { class: `${_class} btn dropdown-item p-0`, onClick: on_click(b_def.event_id), style: "display:block;", ...attributes }, [vue_obj.h("span", { class: b_def.icon, style: "margin-right:0.2rem;cursor:pointer;" }), _i18n(b_def.title_i18n)]);
    		});
    		if (list_or_array == true) {
    			let v_title = vue_obj.h("span", { class: "fas fa-sm fa-align-justify" });
    			let dropdown = vue_obj.h(script$2B, { auto_load: true, button_style: "", button_class: "btn-secondary btn-sm" }, {
    				title: () => v_title,
    				menu: () => v_nodes,
    			});
    			return dropdown;
    		}
    		return vue_obj.h("div", { class: "button-group" }, v_nodes);
    	};
    }

    function get_rows_func(table_def, f_get_extra_params_obj, f_on_get_rows) {
    	let f_get_column_id = get_column_id_func(table_def);
    	return async (active_page, per_page, columns_wrap, map_search, first_get_rows) => {
    		let sort_column = columns_wrap.find((c) => c.sort != 0);
    		let visible_columns = columns_wrap.filter((c) => c.visible == true)
    			.map((c) => f_get_column_id(c)).join(",");
    		let params = {
    			start: (active_page * per_page),
    			length: per_page,
    			map_search,
    			visible_columns,
    		};
    		if (sort_column != null) {
    			params.sort = f_get_column_id(sort_column.data);
    			params.order = sort_column.sort == 1 ? "asc" : "desc";
    		}
    		if (f_get_extra_params_obj != null) {
    			let extra_params = f_get_extra_params_obj();
    			params = { ...params, ...extra_params, };
    		}
    		const url_params = ntopng_url_manager$1.obj_to_url_params(params);
    		const url = `${http_prefix}/${table_def.data_url}?${url_params}`;
    		let res = await ntopng_utility$1.http_request(url, null, null, true);
    		if (f_on_get_rows != null) {
    			f_on_get_rows(params);
    		}
    		let rows = res.rsp;
    		if (table_def.rsp_records_field != null) {
    			rows = res.rsp[table_def.rsp_records_field];
    		}
    		let query_info = res.query_info;
    		if (table_def.rsp_query_info_field != null) {
    			query_info = res.rsp[table_def.rsp_query_info_field];
    		}
    		// fix server rest bug
    		if (res.recordsFiltered > res.recordsTotal) {
    			res.recordsTotal = res.recordsFiltered;
    		}
    		return { total_rows: res.recordsTotal, rows, query_info };
    		// return { total_rows: 1, rows: [rows[0]], query_info };
    	}
    }

    function get_f_print_column_name(table_def) {
    	return (col) => {
    		if (col.title_i18n != null) {
    			return _i18n(col.title_i18n);
    		}
    		if (col.title != null) {
    			return col.title;
    		}
    		return "";
    	};
    }

    function get_column_id_func(table_def) {
    	return (col) => {
    		if (col.id != null) { return col.id; }
    		if (col.data_field != null) { return col.data_field; }
    		return table_def.columns.findIndex((c) => c == col);
    	};
    }

    /**
       Get columns in new table format from old datatable format.
    */
    function get_columns_from_datatable_columns(datatable_columns_title, datatable_columns_js) {
    	let table_columns_config = datatable_columns_js.map((c, index) => {
    		({
    			data_field: c.data,
    			title: datatable_columns_title[index],
    			sortable: c.orderable == null || c.orderable == true,
    		});
    		if (c.className != null) {
    			c.className.split(" ");
    		}
    		if (c.render != null) {
    			c.render_func = (data, row) => c.render(data, 'display', row);
    		}
    	});
    	return table_columns_config;

    }

    const table_utils = {
    	get_columns_from_datatable_columns,
    	build_table,
    };

    var script$2w = {
      __name: 'table-with-config',
      props: {
        table_config_id: String, // name of configuration file in httpdocs/tables_config
        table_id: String, // id of table, same table_config_id can have different table_id and then different columuns visible settins
        csrf: String,
        f_map_config: Function,
        f_map_columns: Function,
        f_sort_rows: Function,
        get_extra_params_obj: Function,
        display_message: Boolean,
        message_to_display: String,
        showLoading: Boolean,
    },
      emits: ['custom_event', 'loaded', 'rows_loaded'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const emit = __emit;
    const props = __props;

    const table_config = ref({});
    const table = ref(null);
    const mount_table = ref(false);

    onMounted(async () => {
        if (props.table_id != null || props.table_config_id != null) {
            load_table();
        }
    });

    watch(() => [props.table_id, props.table_config_id], (cur_value, old_value) => {
        load_table();
    }, { flush: 'pre' });

    const table_id_2 = computed$1(() => {
        if (props.table_id != null) { return props.table_id; }
        return props.table_config_id;
    });

    async function load_table() {
        mount_table.value = false;
        await nextTick();
        let table_config_id_2 = props.table_config_id;
        if (table_config_id_2 == null) {
            table_config_id_2 = props.table_id;
        }
        table_config.value = await table_utils.build_table(http_prefix, table_config_id_2, props.f_map_columns, props.get_extra_params_obj);
        if (props.f_map_config != null) {
            table_config.value = props.f_map_config(table_config.value);
        }
        mount_table.value = true;
        await nextTick();
    }

    function on_loaded() {
        emit('loaded');
    }

    function on_custom_event(event) {
        emit('custom_event', event);
    }

    function rows_loaded(res) {
        emit('rows_loaded', res);
    }

    const refresh_table = (disable_loading) => {
        if (table.value) {
            table.value.refresh_table(disable_loading);
        }
    };

    const get_columns_defs = () => {
        if (table.value == null) { return []; }
        return table.value.get_columns_defs();
    };

    const get_rows_num = () => {
        return table.value.get_rows_num();
    };

    const search_value = (value) => {
        table.value.search_value(value);
    };

    const disableLoading = () => {
        table.value.disableLoading();
    };

    const enableLoading = () => {
        table.value.enableLoading();
    };

    __expose({ refresh_table, get_columns_defs, get_rows_num, search_value, disableLoading, enableLoading });


    return (_ctx, _cache) => {
      return (mount_table.value)
        ? (openBlock(), createBlock(script$2x, {
            key: 0,
            ref_key: "table",
            ref: table,
            id: table_id_2.value,
            columns: table_config.value.columns,
            get_rows: table_config.value.get_rows,
            get_column_id: table_config.value.get_column_id,
            print_column_name: table_config.value.print_column_name,
            print_html_row: table_config.value.print_html_row,
            print_vue_node_row: table_config.value.print_vue_node_row,
            f_is_column_sortable: table_config.value.f_is_column_sortable,
            f_column_min_width: table_config.value.f_column_min_width,
            f_column_max_width: table_config.value.f_column_max_width,
            f_get_column_classes: table_config.value.f_get_column_classes,
            f_get_column_style: table_config.value.f_get_column_style,
            display_empty_rows: table_config.value.display_empty_rows,
            f_sort_rows: __props.f_sort_rows,
            enable_search: table_config.value.enable_search,
            default_sort: table_config.value.default_sort,
            show_autorefresh: table_config.value.show_autorefresh,
            paging: table_config.value.paging,
            csrf: __props.csrf,
            showLoading: __props.showLoading,
            display_message: __props.display_message,
            message_to_display: __props.message_to_display,
            onLoaded: on_loaded,
            onRows_loaded: rows_loaded,
            onCustom_event: on_custom_event
          }, {
            custom_header: withCtx(() => [
              renderSlot(_ctx.$slots, "custom_header")
            ]),
            custom_buttons: withCtx(() => [
              renderSlot(_ctx.$slots, "custom_buttons")
            ]),
            _: 3 /* FORWARDED */
          }, 8 /* PROPS */, ["id", "columns", "get_rows", "get_column_id", "print_column_name", "print_html_row", "print_vue_node_row", "f_is_column_sortable", "f_column_min_width", "f_column_max_width", "f_get_column_classes", "f_get_column_style", "display_empty_rows", "f_sort_rows", "enable_search", "default_sort", "show_autorefresh", "paging", "csrf", "showLoading", "display_message", "message_to_display"]))
        : createCommentVNode("v-if", true)
    }
    }

    };

    script$2w.__file = "http_src/vue/table-with-config.vue";

    var script$2v = {
      __name: 'spinner',
      props: {
        size: String,
        show: Boolean,
    },
      setup(__props) {

    onMounted(async () => {
    });


    return (_ctx, _cache) => {
      return (__props.show)
        ? (openBlock(), createElementBlock("div", {
            key: 0,
            class: "spinner-border text-primary",
            style: normalizeStyle({'width': __props.size, 'height': __props.size}),
            role: "status"
          }, null, 4 /* STYLE */))
        : createCommentVNode("v-if", true)
    }
    }

    };

    script$2v.__file = "http_src/vue/spinner.vue";

    const _hoisted_1$2g = {
      id: "script-description",
      class: "notes bg-light border",
      role: "alert",
      style: {"overflow-wrap":"anywhere !important"}
    };
    const _hoisted_2$23 = ["innerHTML"];
    const _hoisted_3$1S = { key: 0 };
    const _hoisted_4$1P = ["innerHTML"];


    var script$2u = {
      __name: 'note-list',
      props: {
      note_list: Array,
      sub_note_list: Array,
      add_sub_notes: Boolean,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$2g, [
        createBaseVNode("b", null, toDisplayString(_i18n('notes')), 1 /* TEXT */),
        createBaseVNode("ul", null, [
          (openBlock(true), createElementBlock(Fragment, null, renderList(props.note_list, (note) => {
            return (openBlock(), createElementBlock("li", { innerHTML: note }, null, 8 /* PROPS */, _hoisted_2$23))
          }), 256 /* UNKEYED_FRAGMENT */)),
          (props.add_sub_notes)
            ? (openBlock(), createElementBlock("ul", _hoisted_3$1S, [
                (openBlock(true), createElementBlock(Fragment, null, renderList(props.sub_note_list, (note) => {
                  return (openBlock(), createElementBlock("li", { innerHTML: note }, null, 8 /* PROPS */, _hoisted_4$1P))
                }), 256 /* UNKEYED_FRAGMENT */))
              ]))
            : createCommentVNode("v-if", true)
        ])
      ]))
    }
    }

    };

    script$2u.__file = "http_src/vue/note-list.vue";

    var script$2t = defineComponent({
        components: {
    	'modal': script$2F,
        },
        props: {
    	id: String,
        },
        updated() {
        },
        data() {
    	return {
    	    description: "",
    	    invalid_bpf: false,
    	    bpf_filter: "",
    	    extract_now: true,
    	    show_menu: true,
    	    data_available: 0, // 0 == loading, 1 == available, 2 == no data
    	    i18n: (t) => i18n(t),
    	    epoch_interval: null,
    	    id_modal: `${this.$props.id}_modal`,
    	};
        },
        emits: ["apply"],
        created() {
        },
        /** This method is the first method called after html template creation. */
        mounted() {
        },
        methods: {
    	pad2_number: function(number) {
    	    return String(number).padStart(2, '0');
    	},
    	format_date: function(d) {
    	    // let day = this.pad2_number(d.getDate());
    	    // let month = this.pad2_number(d.getMonth());
    	    // let hours = this.pad2_number(d.getHours());
    	    // let minutes = this.pad2_number(d.getMinutes());
    	    // let s = `${day}/${month}/${d.getFullYear()} ${hours}:${minutes}`;
    	    let d_ms = d.valueOf();
    	    return ntopng_utility.from_utc_to_server_date_format(d_ms);
    	},
    	apply: async function() {
    	    if (this.bpf_filter != null && this.bpf_filter != "") {
                let url_request = `${http_prefix}/lua/pro/rest/v2/check/filter.lua?query=${this.bpf_filter}`;
                let res = await ntopng_utility.http_request(url_request, null, false, true);
                this.invalid_bpf = !res.rsp.response;
                if (this.invalid_bpf == true) {
                    return;
                }		
    	    }
    	    let url_request_obj = {
                ifid: ntopng_url_manager.get_url_entry("ifid"),
                epoch_begin: this.epoch_interval.epoch_begin,
                epoch_end: this.epoch_interval.epoch_end,
                bpf_filter: this.bpf_filter,
    	    };
    	    let url_request_params = ntopng_url_manager.obj_to_url_params(url_request_obj);
    	    if (this.extract_now == true) {
                let url_request = `${http_prefix}/lua/rest/v2/get/pcap/live_extraction.lua?${url_request_params}`;
                window.open(url_request, '_self', false);
    	    } else {
                let url_request = `${http_prefix}/lua/rest/v2/create/pcap/extraction/task.lua?${url_request_params}`;
                let job_info = await ntopng_utility.http_request(url_request);
                let job_id = job_info.id;
                let alert_text_html = i18n('traffic_recording.extraction_scheduled');
                let page_name = i18n('traffic_recording.traffic_extraction_jobs');
                let ifid = ntopng_url_manager.get_url_entry("ifid");
                let href = `<a href="/lua/if_stats.lua?ifid=${ifid}&page=traffic_recording&tab=jobs&job_id=${job_id}">${page_name}</a>`; 
                alert_text_html = alert_text_html.replace('%{page}', href);
                alert_text_html = `${alert_text_html} ${job_id}`;
                ntopng_events_manager.emit_custom_event(ntopng_custom_events.SHOW_GLOBAL_ALERT_INFO, { text_html: alert_text_html, type: "alert-success" });
    	    }
    	    this.close();
    	},
    	close: function() {
    	    this.$refs["modal"].close();
    	    setTimeout(() => {
    		this.data_available = 0;
    	    }, 1000);
    	},
    	show: async function(bpf_filter, epoch_interval) {
    	    if (epoch_interval == null) {
                let epoch_begin = Number(ntopng_url_manager.get_url_entry("epoch_begin"));
                let epoch_end = Number(ntopng_url_manager.get_url_entry("epoch_end"));
                let status = ntopng_status_manager.get_status();
                if (status.epoch_begin != null && status.epoch_end != null) {
                    epoch_begin = status.epoch_begin;
                    epoch_end = status.epoch_end;
                }
                if (epoch_begin == null || epoch_end == null) {
                    console.error("modal-traffic-extraction: epoch_begin and epoch_end undefined");
                    return;
                }
                epoch_interval = { epoch_begin: epoch_begin, epoch_end: epoch_end };
    	    }
    	    this.epoch_interval = epoch_interval;
    	    let url_params = ntopng_url_manager.obj_to_url_params(epoch_interval);
    	    let url_request = `${http_prefix}/lua/check_recording_data.lua?${url_params}`;
    	    let res = await ntopng_utility.http_request(url_request, null, null, true);
    	    if (res.available == false) {
                this.data_available = 2;
                this.description = i18n('traffic_recording.no_recorded_data');
                this.$refs["modal"].show();
                return;
    	    }
    	    this.data_available = 1;
    	    let extra_info = "";
    	    if (res.info != null) {
    		    extra_info = res.info;
    	    }	    if (bpf_filter == null) {
                let url_params = ntopng_url_manager.get_url_params();
                let url_request = `${http_prefix}/lua/pro/rest/v2/get/db/filter/bpf.lua?${url_params}`;
                let res = await ntopng_utility.http_request(url_request);
                if (res == null || res.bpf == null) {
                    console.error(`modal-traffic-extraction: ${url_request} return null value`);
                    return;
                }
                bpf_filter = res.bpf;
    	    }
    	    this.set_descriptions(epoch_interval.epoch_begin, epoch_interval.epoch_end, extra_info);
    	    
    	    // let url_params = ntopng_url_manager.get_url_params();
    	    // let url_request = `${http_prefix}/lua/pro/rest/v2/get/db/filter/bpf.lua?${url_params}`;
    	    // let res = await ntopng_utility.http_request(url_request);
    	    // this.bpf_filter = res.bpf;
    	    this.bpf_filter = bpf_filter;
    	    this.$refs["modal"].show();
    	},
    	set_descriptions: function(epoch_begin, epoch_end, info) {
    	    let date_begin = new Date(epoch_begin * 1000);
    	    let date_end = new Date(epoch_end * 1000);
    	    
    	    let desc = i18n('traffic_recording.about_to_download_flow');
    	    desc = desc.replace('%{date_begin}', this.format_date(date_begin));
    	    desc = desc.replace('%{date_end}', this.format_date(date_end));
    	    desc = desc.replace('%{extra_info}', info);
    	    this.description = desc;
    	},
    	show_hide_menu: function() {
    	    this.show_menu = !this.show_menu;
    	},
        },
    });

    const _hoisted_1$2f = ["innerHTML"];
    const _hoisted_2$22 = {
      key: 0,
      style: {"height":"95%"}
    };
    const _hoisted_3$1R = {
      class: "tab-content",
      style: {"height":"100%"}
    };
    const _hoisted_4$1O = { class: "row" };
    const _hoisted_5$1x = { class: "form-group mb-3 col-md-3 has-feedback" };
    const _hoisted_6$1i = { class: "form-group mb-3 col-md-9 text-right asd" };
    const _hoisted_7$1b = {
      class: "radio-inline",
      style: {"margin-left":"1rem"}
    };
    const _hoisted_8$11 = { class: "radio-inline" };
    const _hoisted_9$S = {
      class: "row",
      id: "pcapDownloadModal_advanced",
      style: {}
    };
    const _hoisted_10$M = { class: "form-group mb-3 col-md-12 has-feedback" };
    const _hoisted_11$H = { class: "form-label" };
    const _hoisted_12$C = { class: "input-group" };
    const _hoisted_13$w = { class: "form-label" };

    function render$d(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_modal = resolveComponent("modal");

      return (openBlock(), createBlock(_component_modal, {
        id: _ctx.id_modal,
        onApply: _ctx.apply,
        ref: "modal"
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_ctx.i18n('traffic_recording.pcap_extract')), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", {
            class: normalizeClass(["alert", { 'alert-info': _ctx.data_available != 2, 'alert-warning': _ctx.data_available == 2 }]),
            innerHTML: _ctx.description
          }, null, 10 /* CLASS, PROPS */, _hoisted_1$2f),
          (_ctx.data_available == 1)
            ? (openBlock(), createElementBlock("form", _hoisted_2$22, [
                createBaseVNode("div", _hoisted_3$1R, [
                  createBaseVNode("div", _hoisted_4$1O, [
                    createBaseVNode("div", _hoisted_5$1x, [
                      createBaseVNode("button", {
                        class: "btn btn-sm btn-secondary",
                        type: "button",
                        onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.show_hide_menu && _ctx.show_hide_menu(...args)))
                      }, [
                        createTextVNode(toDisplayString(_ctx.i18n('advanced')), 1 /* TEXT */),
                        createBaseVNode("i", {
                          class: normalizeClass({ 'fas fa-caret-down': _ctx.show_menu, 'fas fa-caret-up': !_ctx.show_menu})
                        }, null, 2 /* CLASS */)
                      ])
                    ]),
                    createBaseVNode("div", _hoisted_6$1i, [
                      createBaseVNode("label", _hoisted_7$1b, [
                        withDirectives(createBaseVNode("input", {
                          type: "radio",
                          name: "extract_now",
                          "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((_ctx.extract_now) = $event)),
                          value: "true",
                          checked: ""
                        }, null, 512 /* NEED_PATCH */), [
                          [vModelRadio, _ctx.extract_now]
                        ]),
                        createTextVNode(" " + toDisplayString(_ctx.i18n('traffic_recording.extract_now')), 1 /* TEXT */)
                      ]),
                      createBaseVNode("label", _hoisted_8$11, [
                        withDirectives(createBaseVNode("input", {
                          type: "radio",
                          name: "extract_now",
                          "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((_ctx.extract_now) = $event)),
                          value: "false"
                        }, null, 512 /* NEED_PATCH */), [
                          [vModelRadio, _ctx.extract_now]
                        ]),
                        createTextVNode(" " + toDisplayString(_ctx.i18n('traffic_recording.queue_as_job')), 1 /* TEXT */)
                      ])
                    ])
                  ]),
                  withDirectives(createBaseVNode("div", _hoisted_9$S, [
                    createBaseVNode("div", _hoisted_10$M, [
                      _cache[8] || (_cache[8] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                      createBaseVNode("label", _hoisted_11$H, [
                        createTextVNode(toDisplayString(_ctx.i18n('traffic_recording.filter_bpf')) + " ", 1 /* TEXT */),
                        _cache[6] || (_cache[6] = createBaseVNode("a", {
                          class: "ntopng-external-link",
                          href: "https://www.ntop.org/guides/n2disk/filters.html"
                        }, [
                          createBaseVNode("i", { class: "fas fa-external-link-alt" })
                        ], -1 /* CACHED */))
                      ]),
                      createBaseVNode("div", _hoisted_12$C, [
                        _cache[7] || (_cache[7] = createBaseVNode("span", { class: "input-group-addon" }, [
                          createBaseVNode("span", { class: "glyphicon glyphicon-filter" })
                        ], -1 /* CACHED */)),
                        withDirectives(createBaseVNode("input", {
                          name: "bpf_filter",
                          "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((_ctx.bpf_filter) = $event)),
                          class: "form-control input-sm",
                          "data-bpf": "bpf",
                          autocomplete: "off",
                          spellcheck: "false"
                        }, null, 512 /* NEED_PATCH */), [
                          [vModelText, _ctx.bpf_filter]
                        ]),
                        withDirectives(createBaseVNode("span", {
                          style: {"margin":"0px","padding":"0","display":"block"},
                          class: "invalid-feedback"
                        }, toDisplayString(_ctx.i18n('invalid_value')), 513 /* TEXT, NEED_PATCH */), [
                          [vShow, _ctx.invalid_bpf]
                        ])
                      ]),
                      _cache[9] || (_cache[9] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                      createBaseVNode("label", _hoisted_13$w, toDisplayString(_ctx.i18n('traffic_recording.filter_examples')) + ":", 1 /* TEXT */),
                      _cache[10] || (_cache[10] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                      _cache[11] || (_cache[11] = createBaseVNode("ul", null, [
                        createBaseVNode("li", null, [
                          createTextVNode("Host: "),
                          createBaseVNode("i", null, "host 192.168.1.2")
                        ]),
                        createBaseVNode("li", null, [
                          createTextVNode("HTTP: "),
                          createBaseVNode("i", null, "tcp and port 80")
                        ]),
                        createBaseVNode("li", null, [
                          createTextVNode("Traffic between hosts: "),
                          createBaseVNode("i", null, "ip host 192.168.1.1 and 192.168.1.2")
                        ]),
                        createBaseVNode("li", null, [
                          createTextVNode("Traffic from an host to another: "),
                          createBaseVNode("i", null, "ip src 192.168.1.1 and dst 192.168.1.2")
                        ])
                      ], -1 /* CACHED */))
                    ])
                  ], 512 /* NEED_PATCH */), [
                    [vShow, _ctx.show_menu]
                  ])
                ])
              ]))
            : createCommentVNode("v-if", true)
        ]),
        footer: withCtx(() => [
          withDirectives(createBaseVNode("button", {
            type: "button",
            onClick: _cache[4] || (_cache[4] = (...args) => (_ctx.apply && _ctx.apply(...args))),
            class: "btn btn-primary"
          }, toDisplayString(_ctx.i18n('apply')), 513 /* TEXT, NEED_PATCH */), [
            [vShow, _ctx.data_available != 2]
          ]),
          withDirectives(createBaseVNode("button", {
            type: "button",
            onClick: _cache[5] || (_cache[5] = (...args) => (_ctx.close && _ctx.close(...args))),
            class: "btn btn-primary"
          }, toDisplayString(_ctx.i18n('ok')), 513 /* TEXT, NEED_PATCH */), [
            [vShow, _ctx.data_available == 2]
          ])
        ]),
        _: 1 /* STABLE */
      }, 8 /* PROPS */, ["id", "onApply"]))
    }

    var css_248z$y = "\ninput ~ .alert[data-v-0be978c4] {\n  display: none;\n}\ninput:invalid ~ .alert[data-v-0be978c4] {\n  display: block;\n}\n";
    styleInject(css_248z$y);

    script$2t.render = render$d;
    script$2t.__scopeId = "data-v-0be978c4";
    script$2t.__file = "http_src/vue/modal-traffic-extraction.vue";

    const _hoisted_1$2e = { class: "nav nav-tabs" };
    const _hoisted_2$21 = ["disabled"];
    const _hoisted_3$1Q = {
      key: 0,
      style: {"min-height":"8.5rem"}
    };
    const _hoisted_4$1N = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_5$1w = { class: "col-form-label col-sm-4" };
    const _hoisted_6$1h = { class: "col-sm-6" };
    const _hoisted_7$1a = ["pattern"];
    const _hoisted_8$10 = {
      key: 1,
      style: {"min-height":"8.5rem"}
    };
    const _hoisted_9$R = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_10$L = { class: "col-form-label col-sm-4" };
    const _hoisted_11$G = { class: "col-sm-8" };
    const _hoisted_12$B = { value: "name" };
    const _hoisted_13$v = { value: "date" };
    const _hoisted_14$s = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_15$p = { class: "col-form-label col-sm-4" };
    const _hoisted_16$p = { class: "col-sm-8" };
    const _hoisted_17$m = ["value"];
    const _hoisted_18$j = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_19$i = { class: "custom-control custom-switch" };
    const _hoisted_20$i = { class: "custom-control-label ms-1 form-check-label" };
    const _hoisted_21$i = ["disabled"];
    const _hoisted_22$i = ["disabled"];
    const _hoisted_23$i = ["disabled"];


    var script$2s = {
      __name: 'modal-snapshot',
      props: {
        csrf: String,
        page: String,
    },
      emits: ['added_snapshot', 'deleted_snapshot', 'deleted_all_snapshots'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const action = ref("add"); // add / select
    const showed = () => {};
    const snapshot_selected = ref("");
    const apply_time = ref(false);
    const apply_time_text = ref("");
    const save_time = ref(true);
    const save_time_text = ref("");
    const snapshot_name = ref("");
    const snapshots = ref([]);
    const order_by = ref("date"); // name / date

    const props = __props;

    const emit = __emit;

    let pattern_singleword = NtopUtils.REGEXES.singleword;

    const disable_add = computed$1(() => {
        let rg = new RegExp(pattern_singleword);
        return !rg.test(snapshot_name.value);
    });

    const disable_select = computed$1(() => {
        return snapshot_selected.value == "";
    });

    const show = () => {
        init();
        modal_id.value.show();
    };

    const update_action = (a) => {
        if (snapshots.value.length == 0 && a == "select") { return; }
        action.value = a;
    };

    function get_page() {
        if (props.page != null) {
    	return props.page;
        }
        let is_alert_stats_url = window.location.toString().match(/alert_stats.lua/) != null;
        let page = "alerts";
        if (!is_alert_stats_url) {
    	page = "flows";
        }
        return page;
    }

    function display_name(snapshot) {
        let utc_ms = snapshot.utc * 1000;
        let date = ntopng_utility.from_utc_to_server_date_format(utc_ms, "DD/MM/YYYY");
        return `${snapshot.name} (${date})`
    }

    let last_order_by = null;
    function sort_snapshots_by() {
        if (last_order_by == order_by.value) { return; }
        
        snapshots.value.sort((a, b) => {
    	if (order_by.value == "name") {
    	    return a.name.localeCompare(b.name);
    	}
    	return a.utc - b.utc;
        });
        if (snapshots.value.length > 0) {
    	snapshot_selected.value = snapshots.value[0];
        }
        last_order_by = order_by.value;    
    }

    let load_snapshots = true;
    async function init() {
        action.value = "add";
        snapshot_name.value = "";
        save_time.value = true;
        apply_time.value = false;
        let status = ntopng_status_manager.get_status();
        let save_time_filter_text = _i18n("snapshots.save_time");
        let begin_time = ntopng_utility.from_utc_to_server_date_format(status.epoch_begin * 1000, "DD/MM/YYYY HH:mm");
        let end_time = ntopng_utility.from_utc_to_server_date_format(status.epoch_end * 1000, "DD/MM/YYYY HH:mm");
        save_time_filter_text = save_time_filter_text.replace(/\%begin_time/, begin_time);
        save_time_filter_text = save_time_filter_text.replace(/\%end_time/, end_time);
        save_time_text.value = save_time_filter_text;
        let apply_time_filter_text = _i18n("snapshots.apply_time");
        apply_time_filter_text = apply_time_filter_text.replace(/\%begin_time/, begin_time);
        apply_time_filter_text = apply_time_filter_text.replace(/\%end_time/, end_time);
        apply_time_text.value = apply_time_filter_text;
        if (load_snapshots) {
    	load_snapshots = false;
    	let page = get_page();
    	let url = `${http_prefix}/lua/pro/rest/v2/get/filters/snapshots.lua?page=${page}`;
    	let snapshots_obj = await ntopng_utility.http_request(url);
    	snapshots.value = ntopng_utility.object_to_array(snapshots_obj);
        }
        sort_snapshots_by();
        if (snapshots.value.length > 0) {
    	snapshot_selected.value = snapshots.value[0];
        }
    }

    const add_snapshot = async () => {
        let filters;
        if (save_time.value) {
    	filters = ntopng_url_manager.get_url_params();
        } else {
    	let params_obj = ntopng_url_manager.get_url_object();
    	delete params_obj.epoch_begin;
    	delete params_obj.epoch_end;
    	filters = ntopng_url_manager.obj_to_url_params(params_obj);
        }
        let page = get_page();
        let params = {
    	snapshot_name: snapshot_name.value,
    	filters,
    	page
        };
        
        params.csrf = props.csrf;
        let url = `${http_prefix}/lua/pro/rest/v2/add/filters/snapshot.lua`;
        try {
    	let headers = {
    	    'Content-Type': 'application/json'
    	};
    	await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
    	load_snapshots = true;
        } catch(err) {
    	console.error(err);
        }
        close();
        emit('added_snapshots', params);
    };

    const select_snapshot = () => {
        close();    
        let filters = snapshot_selected.value.filters;
        if (apply_time.value == true) {
        	let status = ntopng_status_manager.get_status();
        	let params_obj = ntopng_url_manager.get_url_object(filters);
        	params_obj.epoch_begin = status.epoch_begin;
        	params_obj.epoch_end = status.epoch_end;
        	filters = ntopng_url_manager.obj_to_url_params(params_obj);
        }
        ntopng_url_manager.replace_url_and_reload(filters);
    };

    const delete_snapshot = async (delete_all) => {
        let name = snapshot_selected.value.name;
        if (delete_all == true) { name = "*"; }
        let page = get_page();
        let params = {
        	snapshot_name: name,
    	page,
        };
        params.csrf = props.csrf;
        let url = `${http_prefix}/lua/pro/rest/v2/delete/filters/snapshot.lua`;
        try {
        	let headers = {
        	    'Content-Type': 'application/json'
        	};
        	await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
        	load_snapshots = true;
        } catch(err) {
        	console.error(err);
        }
        if (delete_all == true) {
    	emit('deleted_all_snapshots', params);
        } else {
    	emit('deleted_snapshot', params);
        }
        close();
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[8] || (_cache[8] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("snapshots.manage")) + " ", 1 /* TEXT */),
          _cache[9] || (_cache[9] = createBaseVNode("a", {
            target: "_newtab",
            href: "#",
            style: {},
            class: "nav-item text-muted"
          }, [
            createBaseVNode("i", {
              class: "fas fa-question-circle",
              style: {"margin-left":"28rem","padding-top":"0.8rem"}
            })
          ], -1 /* CACHED */))
        ]),
        body: withCtx(() => [
          createBaseVNode("ul", _hoisted_1$2e, [
            createBaseVNode("li", {
              class: "nav-item",
              onClick: _cache[0] || (_cache[0] = $event => (action.value='add'))
            }, [
              createBaseVNode("a", {
                class: normalizeClass(["nav-link", {'active': action.value == 'add'}]),
                href: "#"
              }, toDisplayString(_i18n("snapshots.add")), 3 /* TEXT, CLASS */)
            ]),
            createBaseVNode("li", {
              class: "nav-item",
              onClick: _cache[1] || (_cache[1] = $event => (update_action('select')))
            }, [
              createBaseVNode("a", {
                disabled: snapshots.value.length == 0,
                class: normalizeClass(["nav-link", {'active': action.value == 'select', 'not-allowed': snapshots.value.length == 0 }]),
                href: "#"
              }, toDisplayString(_i18n("snapshots.manage")), 11 /* TEXT, CLASS, PROPS */, _hoisted_2$21)
            ])
          ]),
          (action.value == 'add')
            ? (openBlock(), createElementBlock("div", _hoisted_3$1Q, [
                createBaseVNode("div", _hoisted_4$1N, [
                  createBaseVNode("label", _hoisted_5$1w, [
                    createBaseVNode("b", null, toDisplayString(_i18n("snapshots.name")) + ":", 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_6$1h, [
                    withDirectives(createBaseVNode("input", {
                      pattern: unref(pattern_singleword),
                      placeholder: "",
                      required: "",
                      type: "text",
                      class: "form-control",
                      "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((snapshot_name).value = $event))
                    }, null, 8 /* PROPS */, _hoisted_7$1a), [
                      [vModelText, snapshot_name.value]
                    ])
                  ])
                ]),
                createCommentVNode(" <div class=\"form-group ms-2 me-2 mt-3 row\"> "),
                createCommentVNode(" \t<div class=\"custom-control custom-switch\"> "),
                createCommentVNode(" \t  <input type=\"checkbox\" class=\"custom-control-input whitespace form-check-input\" v-model=\"save_time\"> "),
                createCommentVNode(" \t  <label class=\"custom-control-label ms-1 form-check-label\">{{save_time_text}}</label> "),
                createCommentVNode(" \t</div> "),
                createCommentVNode(" </div>       ")
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" action add "),
          (action.value == 'select')
            ? (openBlock(), createElementBlock("div", _hoisted_8$10, [
                createBaseVNode("div", _hoisted_9$R, [
                  createBaseVNode("label", _hoisted_10$L, [
                    createBaseVNode("b", null, toDisplayString(_i18n("snapshots.order_by")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_11$G, [
                    withDirectives(createBaseVNode("select", {
                      class: "form-select",
                      onChange: _cache[3] || (_cache[3] = $event => (sort_snapshots_by())),
                      "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((order_by).value = $event))
                    }, [
                      createBaseVNode("option", _hoisted_12$B, toDisplayString(_i18n("snapshots.name")), 1 /* TEXT */),
                      createBaseVNode("option", _hoisted_13$v, toDisplayString(_i18n("snapshots.date")), 1 /* TEXT */)
                    ], 544 /* NEED_HYDRATION, NEED_PATCH */), [
                      [vModelSelect, order_by.value]
                    ])
                  ])
                ]),
                createBaseVNode("div", _hoisted_14$s, [
                  createBaseVNode("label", _hoisted_15$p, [
                    createBaseVNode("b", null, toDisplayString(_i18n("snapshots.select")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_16$p, [
                    withDirectives(createBaseVNode("select", {
                      class: "form-select",
                      "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((snapshot_selected).value = $event))
                    }, [
                      (openBlock(true), createElementBlock(Fragment, null, renderList(snapshots.value, (item) => {
                        return (openBlock(), createElementBlock("option", { value: item }, toDisplayString(display_name(item)), 9 /* TEXT, PROPS */, _hoisted_17$m))
                      }), 256 /* UNKEYED_FRAGMENT */))
                    ], 512 /* NEED_PATCH */), [
                      [vModelSelect, snapshot_selected.value]
                    ])
                  ])
                ]),
                createBaseVNode("div", _hoisted_18$j, [
                  createBaseVNode("div", _hoisted_19$i, [
                    withDirectives(createBaseVNode("input", {
                      type: "checkbox",
                      class: "custom-control-input whitespace form-check-input",
                      "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((apply_time).value = $event))
                    }, null, 512 /* NEED_PATCH */), [
                      [vModelCheckbox, apply_time.value]
                    ]),
                    createBaseVNode("label", _hoisted_20$i, toDisplayString(apply_time_text.value), 1 /* TEXT */)
                  ])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" action select ")
        ]),
        footer: withCtx(() => [
          (action.value == 'add')
            ? (openBlock(), createElementBlock("button", {
                key: 0,
                type: "button",
                onClick: add_snapshot,
                disabled: disable_add.value,
                class: "btn btn-primary"
              }, toDisplayString(_i18n("snapshots.add")), 9 /* TEXT, PROPS */, _hoisted_21$i))
            : createCommentVNode("v-if", true),
          (action.value == 'select')
            ? (openBlock(), createElementBlock("button", {
                key: 1,
                onClick: _cache[7] || (_cache[7] = $event => (delete_snapshot(true))),
                type: "button",
                style: {"text-align":"left","margin-left":"0px"},
                class: "btn btn-danger start-0 position-absolute ms-3"
              }, toDisplayString(_i18n("snapshots.delete_all")), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (action.value == 'select')
            ? (openBlock(), createElementBlock("button", {
                key: 2,
                type: "button",
                onClick: delete_snapshot,
                disabled: disable_select.value,
                class: "btn btn-danger"
              }, toDisplayString(_i18n("snapshots.delete")), 9 /* TEXT, PROPS */, _hoisted_22$i))
            : createCommentVNode("v-if", true),
          (action.value == 'select')
            ? (openBlock(), createElementBlock("button", {
                key: 3,
                type: "button",
                onClick: select_snapshot,
                disabled: disable_select.value,
                class: "btn btn-primary"
              }, toDisplayString(_i18n("snapshots.apply")), 9 /* TEXT, PROPS */, _hoisted_23$i))
            : createCommentVNode("v-if", true)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$x = "\ninput[data-v-089b0491]:invalid {\n  border-color: #ff0000;\n}\n.not-allowed[data-v-089b0491] {\n  cursor: not-allowed;\n}\n";
    styleInject(css_248z$x);

    script$2s.__scopeId = "data-v-089b0491";
    script$2s.__file = "http_src/vue/modal-snapshot.vue";

    const _hoisted_1$2d = ["innerHTML"];
    const _hoisted_2$20 = { class: "form-group mb-3" };
    const _hoisted_3$1P = ["innerHTML"];
    const _hoisted_4$1M = { class: "alert_label" };
    const _hoisted_5$1v = { class: "form-check" };
    const _hoisted_6$1g = { class: "form-check-label" };
    const _hoisted_7$19 = {
      key: 0,
      class: "form-check"
    };
    const _hoisted_8$$ = { class: "form-check-label" };
    const _hoisted_9$Q = { class: "form-check" };
    const _hoisted_10$K = { class: "form-check-label" };
    const _hoisted_11$F = { class: "form-check" };
    const _hoisted_12$A = { class: "form-check-label" };
    const _hoisted_13$u = {
      key: 0,
      class: "exclude-domain-certificate"
    };
    const _hoisted_14$r = { class: "alert_label" };
    const _hoisted_15$o = {
      key: 0,
      class: "form-check"
    };
    const _hoisted_16$o = { class: "form-check-label whitespace" };
    const _hoisted_17$l = ["pattern", "disabled"];
    const _hoisted_18$i = {
      key: 1,
      class: "form-check"
    };
    const _hoisted_19$h = { class: "form-check-label whitespace" };
    const _hoisted_20$h = ["disabled", "pattern"];
    const _hoisted_21$h = {
      key: 0,
      class: "form-group row"
    };
    const _hoisted_22$h = { class: "col-form-label" };
    const _hoisted_23$h = { class: "mt-1" };
    const _hoisted_24$f = ["placeholder"];
    const _hoisted_25$e = { class: "separator" };
    const _hoisted_26$c = { class: "form-group mb-3" };
    const _hoisted_27$c = { class: "custom-control custom-switch" };
    const _hoisted_28$a = { class: "custom-control-label" };
    const _hoisted_29$a = {
      class: "alert alert-warning border",
      role: "alert"
    };
    const _hoisted_30$a = ["disabled"];


    var script$2r = {
      __name: 'modal-alerts-filter',
      props: {
        alert: Object,
        page: String,
    },
      emits: ['exclude'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const radio_selected = ref("any");
    const disable_alerts = ref(true);
    const domain = ref(null);
    const tls_certificate = ref(null);
    const comment = ref(null);
    const comment_placeholder = i18n("details.comment_placeholder");


    const emit = __emit;

    const showed = () => {};

    const props = __props;

    watch(() => props.alert, (current_value, old_value) => {
        if (current_value == null) { return; }
        radio_selected.value = "any";
        disable_alerts.value = true;
        domain.value = current_value.info?.value == "" ? null : extract_domain_name_from_info(current_value.info?.value);
        tls_certificate.value = current_value.info?.issuerdn == "" ? null : current_value.info?.issuerdn;
    });

    const extract_domain_name_from_info = (info) => {

      let domain_name = info;
      // remove schema https:// etc
      let info_splitted_ = info.split("://");

      if (info_splitted_.length > 1) domain_name = info_splitted_[1];

      // remove net port
      domain_name = domain_name.split(":")[0];

      // remove www.
      let info_splitted_on_www = domain_name.split("www.");
      if (info_splitted_on_www.length > 1) domain_name = info_splitted_on_www[1];

      // remove path
      domain_name = domain_name.split("/")[0];
      
      return domain_name;

    };
    const check_disable_apply = () => {
        if (radio_selected.value == "domain") {
    	let regex_domain = new RegExp(pattern_domain);
    	return domain.value == null || regex_domain.test(domain.value) == false;
        } else if (radio_selected.value == "certificate") {
    	let regex_certificate = new RegExp(pattern_certificate);
    	return tls_certificate.value == null || regex_certificate.test(tls_certificate.value) == false;
        }
        return false;
    };

    const alert_name = computed$1(() => props.alert?.alert_name);

    const host_addr = computed$1(() => {
        let res = { value: "", label: "" };
        if (props.page != "host" || props.alert == null) { return res; }
        let alert = props.alert;
        res.value = alert.ip.value;
        if (alert.vlan != null && alert.vlan.value != null && alert.vlan.value != 0) {
    	res.value = res.value + '@' + alert.vlan.value;
        }
        res.label = (alert.ip.label) ? `${alert.ip.label} (${alert.ip.value})` : alert.ip.value;
        return res;
    });

    const flow_addr = computed$1(() => {
        let res = { cli_value: "", cli_label: "", srv_value: "", srv_label: "" };
        if (props.page != "flow" || props.alert == null) { return res; }
        let alert = props.alert;
        res.cli_value = alert.flow.cli_ip.value;
        res.srv_value = alert.flow.srv_ip.value;
        let cli_label = `${alert.flow.cli_ip.label || alert.flow.cli_ip.value}`;
        let srv_label = `${alert.flow.srv_ip.label || alert.flow.srv_ip.value}`;

        if(alert.flow.vlan != null && alert.flow.vlan.value != null && alert.flow.vlan.value != 0) {
          cli_label = `${cli_label}@${alert.flow.vlan.label}`;
          srv_label = `${srv_label}@${alert.flow.vlan.label}`;
          res.cli_value = res.cli_value + '@' + alert.flow.vlan.value;
          res.srv_value = res.srv_value + '@' + alert.flow.vlan.value;
        }

        if(cli_label !== res.cli_value) {
          cli_label = `${cli_label} (${res.cli_value})`;
        }
        if(srv_label !== res.srv_value) {
          srv_label = `${srv_label} (${res.srv_value})`;
        }
        
        res.cli_label = cli_label;
        res.srv_label = srv_label;
        return res;
    });

    const show = () => {
        comment.value = "";
        modal_id.value.show();
    };

    function get_type() {
        if (radio_selected.value == "domain" || radio_selected.value == "certificate") {
    	return radio_selected.value;
        }
        return "host";
    }

    let pattern_domain = NtopUtils.REGEXES.domain_name_not_strict;
    let pattern_certificate = NtopUtils.REGEXES.tls_certificate;

    const exclude = () => {
        let page = props.page;
        let type = get_type();
        let params = {
        	delete_alerts: disable_alerts.value,
          label: comment.value,

    	type,	
        };
        let addr = null;
        if (type == "host") {	
    	if (radio_selected.value == "host") {
    	    addr = host_addr.value.value;
    	} else if (radio_selected.value == "server_host") {
    	    addr = flow_addr.value.srv_value;
    	} else if (radio_selected.value == "client_host") {
    	    addr = flow_addr.value.cli_value;
    	}
    	params.alert_addr = addr;
    	if (page == "flow") {
    	    params.flow_alert_key = props.alert.alert_id.value;
    	} else if (page == "host") {
    	    params.host_alert_key = props.alert.alert_id.value;
    	}
        } else if (type == "domain") {
    	params.delete_alerts = false;
    	params.alert_domain = domain.value;
        } else if (type == "certificate") {
    	params.delete_alerts = false;
    	params.alert_certificate = tls_certificate.value;
        }
        close();
        emit('exclude', params);
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[10] || (_cache[10] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n('exclude_check')) + ": ", 1 /* TEXT */),
          createBaseVNode("span", { innerHTML: alert_name.value }, null, 8 /* PROPS */, _hoisted_1$2d)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_2$20, [
            createBaseVNode("div", null, [
              createBaseVNode("label", null, [
                createTextVNode(toDisplayString(_i18n('exclude_check')) + " \"", 1 /* TEXT */),
                createBaseVNode("b", { innerHTML: alert_name.value }, null, 8 /* PROPS */, _hoisted_3$1P),
                _cache[11] || (_cache[11] = createTextVNode("\". ", -1 /* CACHED */)),
                createBaseVNode("span", _hoisted_4$1M, toDisplayString(_i18n('exclude_for')) + ":", 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_5$1v, [
                withDirectives(createBaseVNode("input", {
                  class: "form-check-input",
                  type: "radio",
                  value: "any",
                  "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((radio_selected).value = $event))
                }, null, 512 /* NEED_PATCH */), [
                  [vModelRadio, radio_selected.value]
                ]),
                createBaseVNode("label", _hoisted_6$1g, [
                  createBaseVNode("span", null, toDisplayString(_i18n('show_alerts.filter_disable_check')), 1 /* TEXT */)
                ])
              ]),
              (__props.page == 'host')
                ? (openBlock(), createElementBlock("div", _hoisted_7$19, [
                    withDirectives(createBaseVNode("input", {
                      class: "form-check-input",
                      type: "radio",
                      value: "host",
                      "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((radio_selected).value = $event))
                    }, null, 512 /* NEED_PATCH */), [
                      [vModelRadio, radio_selected.value]
                    ]),
                    createBaseVNode("label", _hoisted_8$$, [
                      createBaseVNode("span", null, toDisplayString(host_addr.value.label), 1 /* TEXT */)
                    ])
                  ]))
                : createCommentVNode("v-if", true),
              (__props.page == 'flow')
                ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                    createBaseVNode("div", _hoisted_9$Q, [
                      withDirectives(createBaseVNode("input", {
                        class: "form-check-input",
                        type: "radio",
                        value: "client_host",
                        "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((radio_selected).value = $event))
                      }, null, 512 /* NEED_PATCH */), [
                        [vModelRadio, radio_selected.value]
                      ]),
                      createBaseVNode("label", _hoisted_10$K, [
                        createBaseVNode("span", null, toDisplayString(_i18n('client')) + ": " + toDisplayString(flow_addr.value.cli_label), 1 /* TEXT */)
                      ])
                    ]),
                    createBaseVNode("div", _hoisted_11$F, [
                      withDirectives(createBaseVNode("input", {
                        class: "form-check-input",
                        type: "radio",
                        value: "server_host",
                        "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((radio_selected).value = $event))
                      }, null, 512 /* NEED_PATCH */), [
                        [vModelRadio, radio_selected.value]
                      ]),
                      createBaseVNode("label", _hoisted_12$A, [
                        createBaseVNode("span", null, toDisplayString(_i18n('server')) + ": " + toDisplayString(flow_addr.value.srv_label), 1 /* TEXT */)
                      ])
                    ])
                  ], 64 /* STABLE_FRAGMENT */))
                : createCommentVNode("v-if", true)
            ]),
            (domain.value != null || tls_certificate.value != null)
              ? (openBlock(), createElementBlock("div", _hoisted_13$u, [
                  createBaseVNode("label", null, [
                    createBaseVNode("span", _hoisted_14$r, toDisplayString(_i18n('check_exclusion.exclude_all_checks_for')) + ":", 1 /* TEXT */)
                  ]),
                  (domain.value != null)
                    ? (openBlock(), createElementBlock("div", _hoisted_15$o, [
                        withDirectives(createBaseVNode("input", {
                          class: "form-check-input",
                          type: "radio",
                          value: "domain",
                          "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((radio_selected).value = $event))
                        }, null, 512 /* NEED_PATCH */), [
                          [vModelRadio, radio_selected.value]
                        ]),
                        createBaseVNode("label", _hoisted_16$o, [
                          createBaseVNode("span", null, toDisplayString(_i18n("check_exclusion.domain")) + ":", 1 /* TEXT */)
                        ]),
                        withDirectives(createBaseVNode("input", {
                          type: "text",
                          pattern: unref(pattern_domain),
                          disabled: radio_selected.value != 'domain',
                          required: "",
                          "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((domain).value = $event)),
                          class: "form-check-label custom-width"
                        }, null, 8 /* PROPS */, _hoisted_17$l), [
                          [vModelText, domain.value]
                        ])
                      ]))
                    : createCommentVNode("v-if", true),
                  (tls_certificate.value != null)
                    ? (openBlock(), createElementBlock("div", _hoisted_18$i, [
                        withDirectives(createBaseVNode("input", {
                          class: "form-check-input",
                          type: "radio",
                          value: "certificate",
                          "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((radio_selected).value = $event))
                        }, null, 512 /* NEED_PATCH */), [
                          [vModelRadio, radio_selected.value]
                        ]),
                        createBaseVNode("label", _hoisted_19$h, [
                          createBaseVNode("span", null, toDisplayString(_i18n("check_exclusion.tls_certificate")) + ":", 1 /* TEXT */)
                        ]),
                        withDirectives(createBaseVNode("input", {
                          type: "text",
                          disabled: radio_selected.value != 'certificate',
                          "onUpdate:modelValue": _cache[7] || (_cache[7] = $event => ((tls_certificate).value = $event)),
                          pattern: unref(pattern_certificate),
                          required: "",
                          class: "form-check-label custom-width"
                        }, null, 8 /* PROPS */, _hoisted_20$h), [
                          [vModelText, tls_certificate.value]
                        ])
                      ]))
                    : createCommentVNode("v-if", true)
                ]))
              : createCommentVNode("v-if", true)
          ]),
          (radio_selected.value != 'any')
            ? (openBlock(), createElementBlock("div", _hoisted_21$h, [
                createBaseVNode("div", null, [
                  createBaseVNode("label", _hoisted_22$h, [
                    createBaseVNode("b", null, toDisplayString(_i18n("show_alerts.add_a_comment")), 1 /* TEXT */)
                  ])
                ]),
                createBaseVNode("div", _hoisted_23$h, [
                  withDirectives(createBaseVNode("textarea", {
                    placeholder: unref(comment_placeholder),
                    id: "alert-label",
                    name: "label",
                    rows: "3",
                    class: "form-control",
                    type: "text",
                    "onUpdate:modelValue": _cache[8] || (_cache[8] = $event => ((comment).value = $event)),
                    maxlength: "450"
                  }, null, 8 /* PROPS */, _hoisted_24$f), [
                    [vModelText, comment.value]
                  ])
                ])
              ]))
            : createCommentVNode("v-if", true),
          withDirectives(createBaseVNode("hr", _hoisted_25$e, null, 512 /* NEED_PATCH */), [
            [vShow, disable_alerts.value]
          ]),
          (radio_selected.value != 'domain' && radio_selected.value != 'certificate')
            ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                withDirectives(createBaseVNode("div", { class: "message alert alert-danger" }, toDisplayString(_i18n("show_alerts.confirm_delete_filtered_alerts")), 513 /* TEXT, NEED_PATCH */), [
                  [vShow, disable_alerts.value]
                ]),
                _cache[12] || (_cache[12] = createBaseVNode("hr", { class: "separator" }, null, -1 /* CACHED */)),
                createBaseVNode("div", _hoisted_26$c, [
                  createBaseVNode("div", _hoisted_27$c, [
                    withDirectives(createBaseVNode("input", {
                      type: "checkbox",
                      class: "custom-control-input whitespace",
                      "onUpdate:modelValue": _cache[9] || (_cache[9] = $event => ((disable_alerts).value = $event))
                    }, null, 512 /* NEED_PATCH */), [
                      [vModelCheckbox, disable_alerts.value]
                    ]),
                    createBaseVNode("label", _hoisted_28$a, toDisplayString(_i18n("delete_disabled_alerts")), 1 /* TEXT */)
                  ])
                ])
              ], 64 /* STABLE_FRAGMENT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", _hoisted_29$a, toDisplayString(_i18n("show_alerts.confirm_filter_alert")), 1 /* TEXT */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: exclude,
            disabled: check_disable_apply(),
            class: "btn btn-warning"
          }, toDisplayString(_i18n("filter")), 9 /* TEXT, PROPS */, _hoisted_30$a)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$w = "\n.whitespace[data-v-19c5b1f1] {\n  margin-right: 0.2rem;\n}\n.custom-width[data-v-19c5b1f1] {\n  display: block;\n  min-width: 100%;\n}\ninput[data-v-19c5b1f1]:invalid {\n  border-color: #ff0000;\n}\n.exclude-domain-certificate[data-v-19c5b1f1] {\n  margin-top: 0.4rem;\n}\n";
    styleInject(css_248z$w);

    script$2r.__scopeId = "data-v-19c5b1f1";
    script$2r.__file = "http_src/vue/modal-alerts-filter.vue";

    const _hoisted_1$2c = { class: "form-group row mb-2" };
    const _hoisted_2$1$ = { class: "col-sm-6" };
    const _hoisted_3$1O = { class: "col-form-label" };
    const _hoisted_4$1L = { class: "col-sm-6 mt-1" };


    var script$2q = {
      __name: 'modal-acknowledge-alert',
      props: {
      context: Object,
      page: String,
    },
      emits: ["acknowledge"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);
    const alert_info = ref(null);
    const comment = ref("");
    const title_desc = ref("");

    const emit = __emit;

    const props = __props;

    onMounted(() => {
    });

    async function acknowledge() {
      let url = `${http_prefix}/lua/rest/v2/acknowledge/${props.page}/alerts.lua`;
      /* The SNMP page is the only exception where the url totally changes */
      if (props.page == 'snmp_device') {
        url = `${http_prefix}/lua/pro/rest/v2/acknowledge/snmp/device/alerts.lua`;
      }
      const params = {
        csrf: props.context.csrf,
        ifid: props.context.ifid,
        label: comment.value,
        row_id: alert.value.row_id,
      };
      let headers = {
        'Content-Type': 'application/json'
      };
      await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
      emit("acknowledge");
      close();
    }


    const alert = ref({});
    const show = (_alert) => {
      alert.value = _alert;
      const $type = $(`<span>${_alert.alert_id.label}</span>`);
      title_desc.value = $type.text().trim();
      comment.value = _alert.user_label;

      let message_body = _i18n("show_alerts.confirm_acknowledge_alert");
      alert_info.value.show(message_body, "alert-warning");
      modal_id.value.show();
    };

    const close = () => {
      modal_id.value.close();
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("show_alerts.acknowledge_alert")) + ": " + toDisplayString(title_desc.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$2c, [
            createBaseVNode("div", _hoisted_2$1$, [
              createBaseVNode("label", _hoisted_3$1O, [
                createBaseVNode("b", null, toDisplayString(_i18n("show_alerts.add_a_comment")), 1 /* TEXT */)
              ])
            ]),
            createBaseVNode("div", _hoisted_4$1L, [
              withDirectives(createBaseVNode("input", {
                "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((comment).value = $event)),
                class: "form-control",
                type: "text",
                maxlength: "255"
              }, null, 512 /* NEED_PATCH */), [
                [vModelText, comment.value]
              ])
            ])
          ]),
          createVNode(script$2J, {
            no_close_button: true,
            ref_key: "alert_info",
            ref: alert_info
          }, null, 512 /* NEED_PATCH */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: acknowledge,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("acknowledge")), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$2q.__file = "http_src/vue/modal-acknowledge-alert.vue";

    var script$2p = {
      __name: 'modal-delete-alert',
      props: {
      context: Object,
      page: String,
    },
      emits: ["delete_alert"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);
    const alert_info = ref(null);

    const emit = __emit;
    const alert = ref({});
    let status_view = "historical";

    const props = __props;

    onMounted(() => {
    });

    async function delete_alert() {
      let url = `${http_prefix}/lua/rest/v2/delete/${props.page}/alerts.lua`;
      /* The SNMP page is the only exception where the url totally changes */
      if (props.page == 'snmp_device') {
        url = `${http_prefix}/lua/pro/rest/v2/delete/snmp/device/alerts.lua`;
      }
      const params = {
        csrf: props.context.csrf,
        ifid: props.context.ifid,
        status: status_view,
        row_id: alert.value.row_id,
      };
      let headers = {
        'Content-Type': 'application/json'
      };
      await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
      emit("delete_alert");
      close();
    }


    const show = (_alert, _status_view) => {
      alert.value = _alert;
      status_view = _status_view;
      let message_body = _i18n("show_alerts.confirm_label_alert");
      alert_info.value.show(message_body, "alert-danger");
      modal_id.value.show();
    };

    const close = () => {
      modal_id.value.close();
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("show_alerts.delete_alert")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createVNode(script$2J, {
            no_close_button: true,
            ref_key: "alert_info",
            ref: alert_info
          }, null, 512 /* NEED_PATCH */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: delete_alert,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("delete")), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$2p.__file = "http_src/vue/modal-delete-alert.vue";

    const _hoisted_1$2b = { class: "form-group row" };
    const _hoisted_2$1_ = { class: "col-sm-6" };
    const _hoisted_3$1N = { class: "col-form-label" };
    const _hoisted_4$1K = { class: "col-sm-6" };
    const _hoisted_5$1u = ["value"];
    const _hoisted_6$1f = { class: "form-group row mb-2" };
    const _hoisted_7$18 = { class: "col-sm-6" };
    const _hoisted_8$_ = { class: "col-form-label" };
    const _hoisted_9$P = { class: "col-sm-6 mt-1" };
    const _hoisted_10$J = ["value"];

    var script$2o = {
      __name: 'modal-acknowledge-alerts',
      props: {
      page: String,
      context: Object,
    },
      emits: ["acknowledge_alerts"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const props = __props;
    const emit = __emit;

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);
    const alert_info = ref(null);
    const begin_time = ref("");
    const end_time = ref("");
    ref([]);

    onMounted(() => {
    });

    const show = (status) => {
      begin_time.value = ntopng_utility$1.from_utc_to_server_date_format(status.epoch_begin * 1000);
      end_time.value = ntopng_utility$1.from_utc_to_server_date_format(status.epoch_end * 1000);
      let message_body = _i18n("show_alerts.confirm_acknowledge_alerts");
      alert_info.value.show(message_body, "alert-danger");
      modal_id.value.show();
    };

    async function acknowledge_alerts() {
      let url = `${http_prefix}/lua/rest/v2/acknowledge/${props.page}/alerts.lua`;
      /* The SNMP page is the only exception where the url totally changes */
      if (props.page == 'snmp_device') {
        url = `${http_prefix}/lua/pro/rest/v2/acknowledge/snmp/device/alerts.lua`;
      }
      const url_params_obj = ntopng_url_manager$1.get_url_object();
      const params = {
        csrf: props.context.csrf,
        ...url_params_obj,
      };
      await ntopng_utility$1.http_post_request(url, params);
      emit("acknowledge_alerts");
      close();
    }

    const close = () => {
      modal_id.value.close();
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("acknowledge_alerts")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$2b, [
            createBaseVNode("div", _hoisted_2$1_, [
              createBaseVNode("label", _hoisted_3$1N, [
                createBaseVNode("b", null, toDisplayString(_i18n("date_from")), 1 /* TEXT */)
              ])
            ]),
            createBaseVNode("div", _hoisted_4$1K, [
              createBaseVNode("input", {
                class: "form-control",
                type: "text",
                value: begin_time.value,
                disabled: ""
              }, null, 8 /* PROPS */, _hoisted_5$1u)
            ])
          ]),
          createBaseVNode("div", _hoisted_6$1f, [
            createBaseVNode("div", _hoisted_7$18, [
              createBaseVNode("label", _hoisted_8$_, [
                createBaseVNode("b", null, toDisplayString(_i18n("date_from")), 1 /* TEXT */)
              ])
            ]),
            createBaseVNode("div", _hoisted_9$P, [
              createBaseVNode("input", {
                class: "form-control",
                type: "text",
                value: end_time.value,
                disabled: ""
              }, null, 8 /* PROPS */, _hoisted_10$J)
            ])
          ]),
          createVNode(script$2J, {
            no_close_button: true,
            ref_key: "alert_info",
            ref: alert_info
          }, null, 512 /* NEED_PATCH */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: acknowledge_alerts,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("acknowledge")), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$2o.__file = "http_src/vue/modal-acknowledge-alerts.vue";

    const _hoisted_1$2a = { class: "form-group row" };
    const _hoisted_2$1Z = { class: "col-sm-6" };
    const _hoisted_3$1M = { class: "col-form-label" };
    const _hoisted_4$1J = { class: "col-sm-6" };
    const _hoisted_5$1t = ["value"];
    const _hoisted_6$1e = { class: "form-group row" };
    const _hoisted_7$17 = { class: "col-sm-6" };
    const _hoisted_8$Z = { class: "col-form-label" };
    const _hoisted_9$O = { class: "col-sm-6 mt-1" };
    const _hoisted_10$I = ["value"];
    const _hoisted_11$E = { class: "form-group filters-list mb-2" };
    const _hoisted_12$z = { class: "form-group row" };
    const _hoisted_13$t = { class: "col-sm-6" };
    const _hoisted_14$q = { class: "col-form-label" };
    const _hoisted_15$n = { class: "col-sm-6 mt-1" };
    const _hoisted_16$n = ["value"];

    var script$2n = {
      __name: 'modal-delete-alerts',
      props: {
      page: String,
      context: Object,
    },
      emits: ["delete_alerts"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const props = __props;
    const emit = __emit;

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);
    const alert_info = ref(null);
    const begin_time = ref("");
    const end_time = ref("");
    const filters = ref([]);

    onMounted(() => {
    });

    const show = (status) => {
      begin_time.value = ntopng_utility$1.from_utc_to_server_date_format(status.epoch_begin * 1000);
      end_time.value = ntopng_utility$1.from_utc_to_server_date_format(status.epoch_end * 1000);
      const tag_operator_label_dict = filtersManager.tag_operator_label_dict;
      filters.value = status.filters.map((f) => {
        return {
          label: f.label,
          value: `${tag_operator_label_dict[f.operator]} ${f.value_label}`,
        };
      });
      let message_body = _i18n("show_alerts.confirm_delete_alerts");
      alert_info.value.show(message_body, "alert-danger");
      modal_id.value.show();
    };

    async function delete_alerts() {
      let url = `${http_prefix}/lua/rest/v2/delete/${props.page}/alerts.lua`;
      /* The SNMP page is the only exception where the url totally changes */
      if (props.page == 'snmp_device') {
        url = `${http_prefix}/lua/pro/rest/v2/delete/snmp/device/alerts.lua`;
      }
      const url_params_obj = ntopng_url_manager$1.get_url_object();
      const params = {
        csrf: props.context.csrf,
        ...url_params_obj,
      };
      await ntopng_utility$1.http_post_request(url, params);
      emit("delete_alerts");
      close();
    }

    const close = () => {
      modal_id.value.close();
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("delete_alerts")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$2a, [
            createBaseVNode("div", _hoisted_2$1Z, [
              createBaseVNode("label", _hoisted_3$1M, [
                createBaseVNode("b", null, toDisplayString(_i18n("date_from")), 1 /* TEXT */)
              ])
            ]),
            createBaseVNode("div", _hoisted_4$1J, [
              createBaseVNode("input", {
                class: "form-control",
                type: "text",
                value: begin_time.value,
                disabled: ""
              }, null, 8 /* PROPS */, _hoisted_5$1t)
            ])
          ]),
          createBaseVNode("div", _hoisted_6$1e, [
            createBaseVNode("div", _hoisted_7$17, [
              createBaseVNode("label", _hoisted_8$Z, [
                createBaseVNode("b", null, toDisplayString(_i18n("date_from")), 1 /* TEXT */)
              ])
            ]),
            createBaseVNode("div", _hoisted_9$O, [
              createBaseVNode("input", {
                class: "form-control",
                type: "text",
                value: end_time.value,
                disabled: ""
              }, null, 8 /* PROPS */, _hoisted_10$I)
            ])
          ]),
          createBaseVNode("div", _hoisted_11$E, [
            (openBlock(true), createElementBlock(Fragment, null, renderList(filters.value, (item) => {
              return (openBlock(), createElementBlock("div", _hoisted_12$z, [
                createBaseVNode("div", _hoisted_13$t, [
                  createBaseVNode("label", _hoisted_14$q, [
                    createBaseVNode("b", null, toDisplayString(item.label), 1 /* TEXT */)
                  ])
                ]),
                createBaseVNode("div", _hoisted_15$n, [
                  createBaseVNode("input", {
                    class: "form-control",
                    type: "text",
                    value: item.value,
                    disabled: ""
                  }, null, 8 /* PROPS */, _hoisted_16$n)
                ])
              ]))
            }), 256 /* UNKEYED_FRAGMENT */))
          ]),
          createVNode(script$2J, {
            no_close_button: true,
            ref_key: "alert_info",
            ref: alert_info
          }, null, 512 /* NEED_PATCH */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: delete_alerts,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("delete")), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$2n.__file = "http_src/vue/modal-delete-alerts.vue";

    const _hoisted_1$29 = { class: "row" };
    const _hoisted_2$1Y = { class: "col-12" };
    const _hoisted_3$1L = { class: "mb-2" };
    const _hoisted_4$1I = { class: "w-100" };
    const _hoisted_5$1s = { clas: "range-container d-flex flex-wrap" };
    const _hoisted_6$1d = { class: "range-picker d-flex m-auto flex-wrap" };
    const _hoisted_7$16 = {
      key: 0,
      class: "ms-1 me-2"
    };
    const _hoisted_8$Y = ["value"];
    const _hoisted_9$N = ["label"];
    const _hoisted_10$H = ["value"];
    const _hoisted_11$D = ["title"];
    const _hoisted_12$y = ["title", "href"];
    const _hoisted_13$s = ["title"];
    const _hoisted_14$p = ["title"];
    const _hoisted_15$m = { class: "col-12" };
    const _hoisted_16$m = { class: "card card-shadow" };
    const _hoisted_17$k = { class: "card-body" };
    const _hoisted_18$h = {
      key: 0,
      class: "row"
    };
    const _hoisted_19$g = {
      class: "col-12 mb-2",
      id: "chart-vue"
    };
    const _hoisted_20$g = { class: "card h-100 overflow-hidden" };
    const _hoisted_21$g = ["title"];
    const _hoisted_22$g = ["onClick", "title"];
    const _hoisted_23$g = { class: "card-footer" };

    const page_id$1 = "page-alert-stats";

    var script$2m = {
      __name: 'page-alert-stats',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const alert_info = ref(null);
    const chart = ref(null);
    const table_alerts = ref(null);
    const modal_traffic_extraction = ref(null);
    const modal_snapshot = ref(null);
    const range_picker = ref(null);
    const permanent_link_button = ref(null);
    const modal_alerts_filter = ref(null);
    const modal_acknowledge = ref(null);
    const modal_delete = ref(null);
    const modal_acknowledge_alerts = ref(null);
    const modal_delete_alerts = ref(null);
    const count_page_components_reloaded = ref(0);
    const display_message = ref(false);
    const message_to_display = ref('');

    const current_alert = ref(null);
    const default_ifid = props.context.ifid;
    let page;
    const table_config_id = ref("");
    const table_id = ref("");
    let chart_data_url = `${http_prefix}/lua/pro/rest/v2/get/db/ts.lua`;
    const chart_type = ntopChartApex$1.typeChart.TS_COLUMN;
    const top_table_array = ref([]);
    const top_table_dropdown_array = ref([]);
    const note_list = ref([_i18n('show_alerts.alerts_info')]);
    const selected_query_preset = ref({});
    const query_presets = ref([]);
    const mount_range_picker = ref(false);

    const href_download_records = computed$1(() => {
        if (!props.context.show_chart || table_alerts.value == null) {
            return ``;
        }
        // add impossible if on ref variable to reload this expression every time count_page_components_reloaded.value change
        if (count_page_components_reloaded.value < 0) { throw "never run"; }
        const download_endpoint = props.context.download.endpoint.replace('PAGE', page);
        let params = ntopng_url_manager$1.get_url_object();
        let columns = table_alerts.value.get_columns_defs();
        let visible_columns = columns.filter((c) => c.visible).map((c) => c.id).join(",");
        params.format = "txt";
        params.visible_columns = visible_columns;
        const url_params = ntopng_url_manager$1.obj_to_url_params(params);
        return `${location.origin}/${download_endpoint}?${url_params}`;
    });

    onBeforeMount(async () => {
        message_to_display.value = `<div class="alert alert-success alert-dismissable"><span>${i18n('no_alerts_require_attention')}</span></div>`;
        if (props.context.is_va) {
            ntopng_utility$1.check_and_set_default_time_interval("day");
        }
        init_params();
        init_url_params();
        await set_query_presets();
        mount_range_picker.value = true;
        await load_top_table_array_overview();
    });

    onMounted(async () => {
        register_components_on_status_update();
    });

    async function init_params() {
        page = ntopng_url_manager$1.get_url_entry("page");
        const status = ntopng_url_manager$1.get_url_entry("status");
        if (page == null) { page = "all"; }
        if (status == 'engaged' && page == "flow") { ntopng_url_manager$1.set_key_to_url("status", "historical"); }
        chart_data_url = (page == "snmp_device") ? `${http_prefix}/lua/pro/rest/v2/get/snmp/device/alert/ts.lua` : `${http_prefix}/lua/rest/v2/get/${page}/alert/ts.lua`;
        selected_query_preset.value = {
            value: ntopng_url_manager$1.get_url_entry("query_preset"),
            count: ntopng_url_manager$1.get_url_entry("count"),
        };
        if (selected_query_preset.value.value == null) {
            selected_query_preset.value.value = "";
        }
        table_config_id.value = `alert_${page}`;
        table_id.value = `${table_config_id.value}_${selected_query_preset.value.value}`;
    }

    function init_url_params() {
        if (ntopng_url_manager$1.get_url_entry("ifid") == null) {
            ntopng_url_manager$1.set_key_to_url("ifid", default_ifid);
        }
        if (ntopng_url_manager$1.get_url_entry("epoch_begin") == null
            || ntopng_url_manager$1.get_url_entry("epoch_end") == null) {
            let default_epoch_begin = Number.parseInt((Date.now() - 1000 * 30 * 60) / 1000);
            let default_epoch_end = Number.parseInt(Date.now() / 1000);
            ntopng_url_manager$1.set_key_to_url("epoch_begin", default_epoch_begin);
            ntopng_url_manager$1.set_key_to_url("epoch_end", default_epoch_end);
        }
        if (ntopng_url_manager$1.get_url_entry("page") == "flow"
            && ntopng_url_manager$1.get_url_entry("status") == "engaged") {
            ntopng_url_manager$1.set_key_to_url("status", "historical");
        }
    }

    async function set_query_presets() {
        if (!props.context.is_ntop_enterprise_l || ntopng_url_manager$1.get_url_entry("status") == "engaged") {
            ntopng_sync$1.ready(get_query_presets_sync_key());
            return;
        }
        let url_request = `${http_prefix}/lua/pro/rest/v2/get/alert/preset/consts.lua?page=${page}`;
        let res = await ntopng_utility$1.http_request(url_request);
        if (res == null || res.length == 0) {
            query_presets.value = [];
            ntopng_url_manager$1.set_key_to_url("query_preset", "");
            ntopng_url_manager$1.set_key_to_url("count", "");
            ntopng_sync$1.ready(get_query_presets_sync_key());
            return;
        }

        query_presets.value = res[0].list.map((el) => {
            return {
                value: el.id,
                name: el.name,
                count: el.count,
                builtin: true,
            };
        });
        if (res.length > 1) {
            res[1].list.forEach((el) => {
                let query = {
                    value: el.id,
                    name: el.name,
                    count: el.count,
                    is_preset: true,
                };
                query_presets.value.push(query);
            });
        }
        if (selected_query_preset.value == null || selected_query_preset.value.value == "") {
            selected_query_preset.value = query_presets.value[0];
        } else {
            let q = query_presets.value.find((i) => i.value == selected_query_preset.value.value);
            selected_query_preset.value = q || query_presets.value[0];
        }
        ntopng_url_manager$1.set_key_to_url("query_preset", selected_query_preset.value.value);
        ntopng_url_manager$1.set_key_to_url("count", selected_query_preset.value.count);
        ntopng_sync$1.ready(get_query_presets_sync_key());
    }

    function get_query_presets_sync_key() {
        return `${page_id$1}_query_presets`;
    }

    async function load_top_table_array_overview(action) {
        if (props.context.show_cards != true || selected_query_preset.value.is_preset == true) { return; }
        top_table_array.value = await load_top_table_array("overview");
    }

    async function load_top_table_details(top, top_index) {
        top.show_spinner = true;
        await nextTick();
        if (top.data_loaded == false) {
            let new_top_array = await load_top_table_array(top.id);
            top.options = new_top_array.find((t) => t.id == top.id).options;
            await nextTick();
            let dropdown = top_table_dropdown_array.value[top_index];
            dropdown.load_menu();
        }
        top.show_spinner = false;
    }

    async function load_top_table_array(action, top) {
        // top_table.value = [];
        const url_params = ntopng_url_manager$1.get_url_params();
        const url = `${props.context.endpoint_cards}?${url_params}&action=${action}`;
        let res = await ntopng_utility$1.http_request(url);
        return res.map((t) => {
            if (t.value) {
                t.value.forEach((e, index) => {
                    if (e.count < 1) {
                        t.value[index].count = '<1'; 
                    }
                });
            }
            return {
                id: t.name,
                label: t.label,
                title: t.tooltip,
                show_spinner: false,
                data_loaded: action != 'overview',
                options: t.value,
            };
        });
    }

    const get_open_top_table_dropdown = (top, top_index) => {
        return (d) => {
            load_top_table_details(top, top_index);
        };
    };

    async function register_components_on_status_update() {
        await ntopng_sync$1.on_ready("range_picker");
        //if (show_chart) {      
        chart.value.register_status();
        //}
        //updateDownloadButton();
        ntopng_status_manager$1.on_status_change(page, (new_status) => {
            ntopng_url_manager$1.get_url_params();
            table_alerts.value.refresh_table();
            load_top_table_array_overview();
        }, false);
    }

    function on_table_loaded() {
        register_table_alerts_events();
    }

    function register_table_alerts_events() {

        let jquery_table_alerts = $(`#${table_id.value}`);
        jquery_table_alerts.on('click', `a.tag-filter`, async function (e) {

            add_table_row_filter(e, $(this));
        });
    }

    function update_select_query_presets() {
        ntopng_url_manager$1.get_url_params();
        ntopng_url_manager$1.set_key_to_url("query_preset", selected_query_preset.value.value);
        ntopng_url_manager$1.set_key_to_url("count", selected_query_preset.value.count);
        ntopng_url_manager$1.reload_url();
    }

    const map_table_def_columns = async (columns) => {
        await ntopng_sync$1.on_ready(get_query_presets_sync_key());
        let map_columns = {
            "l7_proto": (proto, row) => {
                let confidence = "";
                if (proto.confidence !== undefined) {
                    const title = proto.confidence;
                    (title == "DPI") ? confidence = `<span class="badge bg-success" title="${title}">${title}</span>` : confidence = `<span class="badge bg-warning" title="${title}">${title}</span>`;
                }

                if (row.proto.label !== proto.label) {
                    return DataTableRenders$1.filterize('l4proto', row.proto.value, row.proto.label) + ":" + DataTableRenders$1.filterize('l7proto', proto.value, proto.label.split(":")[1]) + " " + `${confidence}`;
                }
                return DataTableRenders$1.filterize('l4proto', row.proto.value, row.proto.label) + " " + `${confidence}`;
            },
            "info": (info, row) => {
                return `${DataTableRenders$1.filterize('info', info.value, info.label)}`;
            },
            "address": (address, row) => {
                return `${DataTableRenders$1.filterize('mac', address.value, address.label)}`;
            },
            "cli2srv_bytes": (info, row) => {

                return `${DataTableRenders$1.filterize('cli2srv_bytes', row.total_bytes.bytes_sent, formatterUtils.getFormatter("bytes")(row.total_bytes.bytes_sent))}`;
            },
            "srv2cli_bytes": (info, row) => {
                return `${DataTableRenders$1.filterize('srv2cli_bytes', row.total_bytes.bytes_rcvd, formatterUtils.getFormatter("bytes")(row.total_bytes.bytes_rcvd))}`;
            },
            "asn": (info, row) =>{
                return `${DataTableRenders$1.filterize('asn', info, row.name)}`;
            }

        };
        let set_query_preset_columns = selected_query_preset.value.is_preset && columns.length > 0;
        if (set_query_preset_columns) {
            // add action button that is the first button
            columns = [columns[0]].concat(props.context.columns_def);
        }
        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];

            if (c.id == "actions") {
                if (set_query_preset_columns == true) {
                    c.button_def_array = [
                        {
                            "id": "expand",
                            "icon": "fas fa fa-search-plus",
                            "class": [],
                            "title_i18n": "db_search.expand_button",
                            "event_id": "click_button_expand"
                        },
                    ];
                    return;
                }
                const visible_dict = {
                    snmp_info: props.context.actions.show_snmp_info,
                    info: props.context.actions.show_info,
                    historical_data: props.context.actions.show_historical,
                    acknowledge: props.context.actions.show_acknowledge,
                    disable: props.context.actions.show_disable,
                    settings: props.context.actions.show_settings,
                    remove: props.context.actions.show_delete,
                };
                c.button_def_array.forEach((b) => {
                    if (!visible_dict[b.id]) {
                        b.class.push("disabled");
                        return;
                    }
                    if (b.id == "snmp_info") {
                        b.f_map_class = (current_class, row) => {
                            current_class = current_class.filter((class_item) => class_item != "disabled");
                            if (row.disable_info) {
                                current_class.push("disabled");
                            }
                            return current_class;
                        };
                    } else if (b.id == "acknowledge" || b.id == "remove") {
                        /* Engaged alerts have no acknowledge nor remove */
                        b.f_map_class = (current_class, row) => {
                            current_class = current_class.filter((class_item) => class_item != "disabled");
                            if (row.is_engaged) {
                                current_class.push("disabled");
                            }
                            return current_class;
                        };
                    }
                });
            }
        });
        return columns;
    };

    const add_table_row_filter = (e, a) => {
        e.stopPropagation();

        let key = undefined;
        let realValue = undefined;
        let operator = 'eq';

        // Read tag key and value from the <a> itself if provided
        if (a.data('tagKey') != undefined) key = a.data('tagKey');
        if (a.data('tagRealvalue') != undefined) realValue = a.data('tagRealvalue');
        else if (a.data('tagValue') != undefined) realValue = a.data('tagValue');
        if (a.data('tagOperator') != undefined) operator = a.data('tagOperator');

        let filter = {
            id: key,
            value: realValue,
            operator: operator,
        };
        add_filter(filter);
    };

    function add_top_table_filter(opt, event) {
        event.stopPropagation();
        let filter = {
            id: opt.key,
            value: opt.value,
            operator: opt.operator,
        };
        add_filter(filter);
    }

    function add_filter(filter) {
        if (range_picker.value.is_filter_defined(filter)) {
            ntopng_events_manager.emit_custom_event(ntopng_custom_events$1.SHOW_MODAL_FILTERS, filter);
        } else {
            throw `Filter ${filter.value} not defined`;
        }
    }

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    function click_navbar_item(item) {
        ntopng_url_manager$1.set_key_to_url('page', item.page_name);
        ntopng_url_manager$1.reload_url();
    }

    function show_modal_alerts_filter(alert) {
        current_alert.value = alert;
        modal_alerts_filter.value.show();
    }

    function get_permanent_link() {
        const $this = $(permanent_link_button.value);
        const placeholder = document.createElement('input');
        placeholder.value = location.href;
        document.body.appendChild(placeholder);
        placeholder.select();

        // copy the url to the clipboard from the placeholder
        document.execCommand("copy");
        document.body.removeChild(placeholder);

        $this.attr("title", `${_i18n('copied')}!`)
            .tooltip("dispose")
            .tooltip()
            .tooltip("show");
    }

    function show_modal_traffic_extraction() {
        modal_traffic_extraction.value.show();

    }

    function show_modal_snapshot() {
        modal_snapshot.value.show();
    }

    async function add_exclude(params) {
        params.csrf = props.context.csrf;
        let url = `${http_prefix}/lua/pro/rest/v2/add/alert/exclusion.lua`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
            let url_params = ntopng_url_manager$1.get_url_params();
            setTimeout(() => {
                //todo reloadTable($table, url_params);
                ntopng_events_manager.emit_custom_event(ntopng_custom_events$1.SHOW_GLOBAL_ALERT_INFO, { text_html: _i18n('check_exclusion.disable_warn'), type: "alert-info", timeout: 2 });
            }, 1000);
        } catch (err) {
            console.error(err);
        }
    }

    function refresh_page_components() {
        let t = table_alerts.value;
        let c = chart.value;
        setTimeout(() => {
            t.refresh_table();
            c.update_chart();
        }, 1 * 1000);
    }

    /* In case no rows are printed, then the message has to be displayed */
    function rows_loaded(res) {
        if (res?.rows != null) {
            display_message.value = (res.rows.length == 0);
        }
    }

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_snmp_info": click_button_snmp_info,
            "click_button_info": click_button_info,
            "click_button_historical_flows": click_button_historical_flows,
            "click_button_acknowledge": click_button_acknowledge,
            "click_button_disable": click_button_disable,
            "click_button_settings": click_button_settings,
            "click_button_remove": click_button_remove,
            "click_button_expand": click_button_expand,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    function click_button_expand(event) {
        const alert = event.row;
        ntopng_url_manager$1.set_key_to_url("query_preset", "");
        ntopng_url_manager$1.set_key_to_url("count", "");
        let status = ntopng_status_manager$1.get_status();
        let filters = status.filters;
        let row_filters = alert?.filter?.tag_filters;
        if (row_filters?.length > 0) {
            row_filters = row_filters.map((f) => {
                return {
                    id: f.id,
                    operator: f.op,
                    value: f.value,
                };
            });
            filters = filters.concat(row_filters);
        }
        // remove duplicate filters
        let filters_dict = {};
        filters.forEach((f) => filters_dict[`${f.id}_${f.operator}_${f.value}`] = f);
        filters = ntopng_utility$1.object_to_array(filters_dict);

        let filters_object = filtersManager.get_filters_object(filters);
        ntopng_url_manager$1.add_obj_to_url(filters_object);
        ntopng_url_manager$1.reload_url();
    }

    function show_modal_acknowledge_alerts() {
        let status = ntopng_status_manager$1.get_status();
        modal_acknowledge_alerts.value.show(status);
    }

    function show_modal_delete_alerts() {
        let status = ntopng_status_manager$1.get_status();
        modal_delete_alerts.value.show(status);
    }

    function click_button_remove(event) {
        const alert = event.row;
        let status_view = get_status_view();
        modal_delete.value.show(alert, status_view);
    }

    function click_button_settings(event) {
        const alert = event.row;
        const check_settings_href = $(alert.msg.configset_ref).attr('href');
        window.location.href = check_settings_href;
    }

    function click_button_disable(event) {
        const alert = event.row;
        show_modal_alerts_filter(alert);
    }

    function click_button_acknowledge(event) {
        const alert = event.row;
        modal_acknowledge.value.show(alert, props.context);
    }

    function click_button_historical_flows(event) {
        const alert = event.row;

        let url = `${http_prefix}/lua/pro/db_search.lua&`;
        let host_ip = alert?.ip?.value; // get alert host ip

        // If host is attacker set it as filter for historical value
        if (alert.is_attacker) {
            url += `cli_ip=${host_ip};eq`;
        }

        if (alert.is_victim) {
            url += `srv_ip=${host_ip};eq`;
        }

        if (alert.link_to_past_flows) {
            window.location.href = alert.link_to_past_flows;
        } else {
            window.location.href = url;
        }
    }

    function click_button_snmp_info(event) {
        const alert = event.row;
        let href = ``;
        if (alert.port.value != null) {
            href = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${alert.ip}&snmp_port_idx=${alert.port.value}`;
        } else {
            href = `${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?host=${alert.ip}`;
        }
        window.open(href, "_blank");
    }

    function click_button_info(event) {
        const alert = event.row;
        let status_view = get_status_view();
        let params_obj = {
            page: page,
            status: status_view,
            row_id: alert.row_id,
            tstamp: alert.tstamp.value,
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params_obj);
        const href = `${props.context.alert_details_url}?${url_params}`;
        window.open(href, "_blank");
    }

    function get_status_view() {
        let status_view = ntopng_url_manager$1.get_url_entry("status");
        if (status_view == null || status_view == "") {
            status_view = "historical";
        }
        return status_view;
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createVNode(script$2K, {
          id: "navbar",
          main_title: __props.context.navbar.main_title,
          base_url: __props.context.navbar.base_url,
          help_link: __props.context.navbar.help_link,
          items_table: __props.context.navbar.items_table,
          onClick_item: click_navbar_item
        }, null, 8 /* PROPS */, ["main_title", "base_url", "help_link", "items_table"]),
        createBaseVNode("div", _hoisted_1$29, [
          createBaseVNode("div", _hoisted_2$1Y, [
            createBaseVNode("div", _hoisted_3$1L, [
              createBaseVNode("div", _hoisted_4$1I, [
                createBaseVNode("div", _hoisted_5$1s, [
                  createBaseVNode("div", _hoisted_6$1d, [
                    createVNode(script$2J, {
                      id: "alert_info",
                      global: true,
                      ref_key: "alert_info",
                      ref: alert_info
                    }, null, 512 /* NEED_PATCH */),
                    createVNode(script$2t, {
                      id: "modal_traffic_extraction",
                      ref_key: "modal_traffic_extraction",
                      ref: modal_traffic_extraction
                    }, null, 512 /* NEED_PATCH */),
                    createVNode(script$2s, {
                      ref_key: "modal_snapshot",
                      ref: modal_snapshot,
                      csrf: __props.context.csrf
                    }, null, 8 /* PROPS */, ["csrf"]),
                    (mount_range_picker.value)
                      ? (openBlock(), createBlock(script$2D, {
                          key: 0,
                          ref_key: "range_picker",
                          ref: range_picker,
                          id: "range_picker"
                        }, {
                          begin: withCtx(() => [
                            (query_presets.value.length > 0)
                              ? (openBlock(), createElementBlock("div", _hoisted_7$16, [
                                  withDirectives(createBaseVNode("select", {
                                    class: "me-2 form-select",
                                    "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((selected_query_preset).value = $event)),
                                    onChange: _cache[1] || (_cache[1] = $event => (update_select_query_presets()))
                                  }, [
                                    (openBlock(true), createElementBlock(Fragment, null, renderList(query_presets.value, (item) => {
                                      return (openBlock(), createElementBlock(Fragment, null, [
                                        (item.builtin == true)
                                          ? (openBlock(), createElementBlock("option", {
                                              key: 0,
                                              value: item
                                            }, toDisplayString(item.name), 9 /* TEXT, PROPS */, _hoisted_8$Y))
                                          : createCommentVNode("v-if", true)
                                      ], 64 /* STABLE_FRAGMENT */))
                                    }), 256 /* UNKEYED_FRAGMENT */)),
                                    (unref(page) != 'analysis')
                                      ? (openBlock(), createElementBlock("optgroup", {
                                          key: 0,
                                          label: _i18n('queries.queries')
                                        }, [
                                          (openBlock(true), createElementBlock(Fragment, null, renderList(query_presets.value, (item) => {
                                            return (openBlock(), createElementBlock(Fragment, null, [
                                              (!item.builtin)
                                                ? (openBlock(), createElementBlock("option", {
                                                    key: 0,
                                                    value: item
                                                  }, toDisplayString(item.name), 9 /* TEXT, PROPS */, _hoisted_10$H))
                                                : createCommentVNode("v-if", true)
                                            ], 64 /* STABLE_FRAGMENT */))
                                          }), 256 /* UNKEYED_FRAGMENT */))
                                        ], 8 /* PROPS */, _hoisted_9$N))
                                      : createCommentVNode("v-if", true)
                                  ], 544 /* NEED_HYDRATION, NEED_PATCH */), [
                                    [vModelSelect, selected_query_preset.value]
                                  ])
                                ]))
                              : createCommentVNode("v-if", true)
                          ]),
                          extra_range_buttons: withCtx(() => [
                            (__props.context.show_permalink)
                              ? (openBlock(), createElementBlock("button", {
                                  key: 0,
                                  class: "btn btn-link btn-sm",
                                  onClick: get_permanent_link,
                                  title: _i18n('graphs.get_permanent_link'),
                                  ref_key: "permanent_link_button",
                                  ref: permanent_link_button
                                }, [...(_cache[2] || (_cache[2] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-link" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_11$D))
                              : createCommentVNode("v-if", true),
                            (__props.context.show_download)
                              ? (openBlock(), createElementBlock("a", {
                                  key: 1,
                                  class: "btn btn-link btn-sm",
                                  id: "dt-btn-download",
                                  title: _i18n('graphs.download_records'),
                                  href: href_download_records.value
                                }, [...(_cache[3] || (_cache[3] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-file" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_12$y))
                              : createCommentVNode("v-if", true),
                            (__props.context.show_pcap_download)
                              ? (openBlock(), createElementBlock("button", {
                                  key: 2,
                                  class: "btn btn-link btn-sm",
                                  onClick: show_modal_traffic_extraction,
                                  title: _i18n('traffic_recording.pcap_download')
                                }, [...(_cache[4] || (_cache[4] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-download" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_13$s))
                              : createCommentVNode("v-if", true),
                            (__props.context.is_ntop_enterprise_m)
                              ? (openBlock(), createElementBlock("button", {
                                  key: 3,
                                  class: "btn btn-link btn-sm",
                                  onClick: show_modal_snapshot,
                                  title: _i18n('datatable.manage_snapshots')
                                }, [...(_cache[5] || (_cache[5] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-camera-retro" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_14$p))
                              : createCommentVNode("v-if", true)
                          ]),
                          _: 1 /* STABLE */
                        }, 512 /* NEED_PATCH */))
                      : createCommentVNode("v-if", true)
                  ])
                ])
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_15$m, [
            createBaseVNode("div", _hoisted_16$m, [
              createBaseVNode("div", _hoisted_17$k, [
                (__props.context.show_chart)
                  ? (openBlock(), createElementBlock("div", _hoisted_18$h, [
                      createBaseVNode("div", _hoisted_19$g, [
                        createBaseVNode("div", _hoisted_20$g, [
                          createVNode(script$2I, {
                            ref_key: "chart",
                            ref: chart,
                            id: "chart_alert_stats",
                            chart_type: unref(chart_type),
                            base_url_request: unref(chart_data_url),
                            register_on_status_change: false
                          }, null, 8 /* PROPS */, ["chart_type", "base_url_request"])
                        ])
                      ]),
                      _cache[6] || (_cache[6] = createBaseVNode("div", null, null, -1 /* CACHED */)),
                      createVNode(script$2w, {
                        ref_key: "table_alerts",
                        ref: table_alerts,
                        table_config_id: table_config_id.value,
                        table_id: table_id.value,
                        csrf: __props.context.csrf,
                        showLoading: true,
                        f_map_columns: map_table_def_columns,
                        get_extra_params_obj: get_extra_params_obj,
                        display_message: display_message.value,
                        message_to_display: message_to_display.value,
                        onLoaded: on_table_loaded,
                        onCustom_event: on_table_custom_event,
                        onRows_loaded: rows_loaded
                      }, {
                        custom_header: withCtx(() => [
                          (openBlock(true), createElementBlock(Fragment, null, renderList(top_table_array.value, (t, t_index) => {
                            return (openBlock(), createBlock(script$2B, {
                              f_on_open: get_open_top_table_dropdown(t, t_index),
                              ref_for: true,
                              ref: el => { top_table_dropdown_array.value[t_index] = el; }
                            }, {
                              title: withCtx(() => [
                                createVNode(script$2v, {
                                  show: t.show_spinner,
                                  size: "1rem",
                                  class: "me-1"
                                }, null, 8 /* PROPS */, ["show"]),
                                createBaseVNode("a", {
                                  class: "ntopng-truncate",
                                  title: t.title
                                }, toDisplayString(t.label), 9 /* TEXT, PROPS */, _hoisted_21$g)
                              ]),
                              menu: withCtx(() => [
                                (openBlock(true), createElementBlock(Fragment, null, renderList(t.options, (opt) => {
                                  return (openBlock(), createElementBlock("a", {
                                    style: {"cursor":"pointer","display":"block"},
                                    onClick: $event => (add_top_table_filter(opt, $event)),
                                    class: "ntopng-truncate tag-filter",
                                    title: opt.value
                                  }, toDisplayString(opt.label + " (" +
                                                opt.count + "%)"), 9 /* TEXT, PROPS */, _hoisted_22$g))
                                }), 256 /* UNKEYED_FRAGMENT */))
                              ]),
                              _: 2 /* DYNAMIC */
                            }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["f_on_open"]))
                          }), 256 /* UNKEYED_FRAGMENT */)),
                          createCommentVNode(" Dropdown columns ")
                        ]),
                        _: 1 /* STABLE */
                      }, 8 /* PROPS */, ["table_config_id", "table_id", "csrf", "display_message", "message_to_display"])
                    ]))
                  : createCommentVNode("v-if", true)
              ]),
              createCommentVNode(" card body "),
              withDirectives(createBaseVNode("div", _hoisted_23$g, [
                (__props.context.show_acknowledge_all)
                  ? (openBlock(), createElementBlock("button", {
                      key: 0,
                      onClick: show_modal_acknowledge_alerts,
                      class: "btn btn-primary me-1"
                    }, [
                      _cache[7] || (_cache[7] = createBaseVNode("i", { class: "fas fa fa-user-check" }, null, -1 /* CACHED */)),
                      createTextVNode(" " + toDisplayString(_i18n("acknowledge_alerts")), 1 /* TEXT */)
                    ]))
                  : createCommentVNode("v-if", true),
                (__props.context.show_delete_all)
                  ? (openBlock(), createElementBlock("button", {
                      key: 1,
                      onClick: show_modal_delete_alerts,
                      class: "btn btn-danger"
                    }, [
                      _cache[8] || (_cache[8] = createBaseVNode("i", { class: "fas fa fa-trash" }, null, -1 /* CACHED */)),
                      createTextVNode(" " + toDisplayString(_i18n("delete_alerts")), 1 /* TEXT */)
                    ]))
                  : createCommentVNode("v-if", true)
              ], 512 /* NEED_PATCH */), [
                [vShow, unref(page) != 'all']
              ]),
              createCommentVNode(" card footer ")
            ]),
            createCommentVNode(" card-shadow ")
          ]),
          createCommentVNode(" div col "),
          createVNode(script$2u, { note_list: note_list.value }, null, 8 /* PROPS */, ["note_list"])
        ]),
        createCommentVNode(" div row "),
        createVNode(script$2q, {
          ref_key: "modal_acknowledge",
          ref: modal_acknowledge,
          context: __props.context,
          page: unref(page),
          onAcknowledge: refresh_page_components
        }, null, 8 /* PROPS */, ["context", "page"]),
        createVNode(script$2p, {
          ref_key: "modal_delete",
          ref: modal_delete,
          context: __props.context,
          page: unref(page),
          onDelete_alert: refresh_page_components
        }, null, 8 /* PROPS */, ["context", "page"]),
        createVNode(script$2o, {
          ref_key: "modal_acknowledge_alerts",
          ref: modal_acknowledge_alerts,
          context: __props.context,
          page: unref(page),
          onAcknowledge_alerts: refresh_page_components
        }, null, 8 /* PROPS */, ["context", "page"]),
        createVNode(script$2n, {
          ref_key: "modal_delete_alerts",
          ref: modal_delete_alerts,
          context: __props.context,
          page: unref(page),
          onDelete_alerts: refresh_page_components
        }, null, 8 /* PROPS */, ["context", "page"]),
        createVNode(script$2r, {
          alert: current_alert.value,
          page: unref(page),
          onExclude: add_exclude,
          ref_key: "modal_alerts_filter",
          ref: modal_alerts_filter
        }, null, 8 /* PROPS */, ["alert", "page"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$2m.__file = "http_src/vue/page-alert-stats.vue";

    const _hoisted_1$28 = ["innerHTML"];
    const _hoisted_2$1X = { style: {"height":"95%"} };
    const _hoisted_3$1K = {
      class: "tab-content",
      style: {"height":"100%"}
    };
    const _hoisted_4$1H = { class: "row" };
    const _hoisted_5$1r = { class: "col-md-12" };
    const _hoisted_6$1c = { class: "form-group mb-3" };
    const _hoisted_7$15 = { class: "form-label" };
    const _hoisted_8$X = { class: "d-flex flex-wrap gap-2 mt-2" };
    const _hoisted_9$M = ["id", "value"];
    const _hoisted_10$G = ["for"];
    const _hoisted_11$C = { class: "form-group mb-3" };
    const _hoisted_12$x = { class: "d-flex gap-2" };
    const _hoisted_13$r = ["href"];


    var script$2l = {
      __name: 'modal-historical-flows-downloader',
      props: {
        id: {
            type: String,
            required: true
        }
    },
      setup(__props, { expose: __expose }) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const modal = ref(null);
    const availableColumns = ref([]);
    const selectedColumns = ref([]);
    const description = ref("");
    const id_modal = computed$1(() => `${props.id}_modal`);


    const downloadUrl = computed$1(() => {
        // no col selected
        if (selectedColumns.value.length === 0) {
            return '#';
        }

        // create url for download
        try {
            const entries = ntopng_url_manager$1.get_url_entries();

            // epoch eend and begin
            let epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
            let epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");

            // base download historical flows URL
            let base_url = `${http_prefix}/lua/pro/rest/v2/get/host/flows/download_flow_records.lua?epoch_begin=${epoch_begin}&epoch_end=${epoch_end}`;

            // Add visible columns
            base_url += `&visible_columns=${selectedColumns.value.join(',')}`;

            // check if I already added the param
            const addedParams = new Set(['epoch_begin', 'epoch_end', 'visible_columns']);

            // extract all params from url to set where clause in backend before db query
            for (const [key, value] of entries) {
                // only add non added params
                if (!addedParams.has(key)) {
                    base_url += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
                }
            }
            return base_url;

        } catch (error) {
            console.error('Error parsing download URL:', error);
            return '#';
        }
    });

    // update counter of selected columns description on top
    watch(selectedColumns, () => {
        updateDescription();
    }, { deep: true });

    // select all columns to export
    const selectAllCols = () => {
        selectedColumns.value = availableColumns.value.map(col => col.id);
    };

    // deselect all
    const deselectCols = () => {
        selectedColumns.value = [];
    };

    //compute description based on selected columns length
    const updateDescription = () => {
        if (selectedColumns.value.length === 0) {
            description.value = _i18n('export.select_cols_to_export');
        } else {
            description.value = `${_i18n('export.about_to_download')} ${selectedColumns.value.length}`;
        }
    };

    const close = () => {
        modal.value?.close();
    };

    const show = (columns = null) => {
        if (!columns) {
            console.error('No columns provided');
            return;
        }

        // convert columns to array
        if (!Array.isArray(columns)) {
            columns = Object.entries(columns).map(([id, name]) => ({
                id,
                name
            }));
        }

        // sort by ascending name
        columns.sort((a, b) => {
            const nameA = (a.name).toLowerCase();
            const nameB = (b.name).toLowerCase();
            return nameA.localeCompare(nameB);
        });

        availableColumns.value = columns;

        // set all columns as selected by default
        selectedColumns.value = availableColumns.value
            .map(col => col.id);

        updateDescription();
        modal.value?.show();
    };


    // expose methods like all modals
    __expose({
        show,
        close
    });

    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        id: id_modal.value,
        ref_key: "modal",
        ref: modal
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n('export.select_cols')), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Description of how many columns are selected "),
          createBaseVNode("div", {
            class: "alert alert-info",
            innerHTML: description.value
          }, null, 8 /* PROPS */, _hoisted_1$28),
          createCommentVNode(" Show all columns and toggle switch "),
          createBaseVNode("form", _hoisted_2$1X, [
            createBaseVNode("div", _hoisted_3$1K, [
              createBaseVNode("div", _hoisted_4$1H, [
                createBaseVNode("div", _hoisted_5$1r, [
                  createBaseVNode("div", _hoisted_6$1c, [
                    createBaseVNode("label", _hoisted_7$15, toDisplayString(_i18n('export.select_cols_to_export')) + ":", 1 /* TEXT */),
                    createBaseVNode("div", _hoisted_8$X, [
                      createCommentVNode(" Display all available columns to toggle "),
                      (openBlock(true), createElementBlock(Fragment, null, renderList(availableColumns.value, (column) => {
                        return (openBlock(), createElementBlock("div", {
                          key: column.id,
                          class: "form-check form-switch"
                        }, [
                          createCommentVNode(" Toggle button "),
                          withDirectives(createBaseVNode("input", {
                            id: `column_${column.id}`,
                            "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((selectedColumns).value = $event)),
                            value: column.id,
                            type: "checkbox",
                            class: "form-check-input"
                          }, null, 8 /* PROPS */, _hoisted_9$M), [
                            [vModelCheckbox, selectedColumns.value]
                          ]),
                          createCommentVNode(" Column name "),
                          createBaseVNode("label", {
                            for: `column_${column.id}`,
                            class: "form-check-label ms-2"
                          }, toDisplayString(column.name || column.id), 9 /* TEXT, PROPS */, _hoisted_10$G)
                        ]))
                      }), 128 /* KEYED_FRAGMENT */))
                    ])
                  ]),
                  createCommentVNode(" Buttons to select or deselect all columsn"),
                  createBaseVNode("div", _hoisted_11$C, [
                    createBaseVNode("div", _hoisted_12$x, [
                      createBaseVNode("button", {
                        type: "button",
                        onClick: selectAllCols,
                        class: "btn btn-sm btn-outline-primary"
                      }, toDisplayString(_i18n('export.select_all')), 1 /* TEXT */),
                      createBaseVNode("button", {
                        type: "button",
                        onClick: deselectCols,
                        class: "btn btn-sm btn-outline-secondary"
                      }, toDisplayString(_i18n('export.select_none')), 1 /* TEXT */)
                    ])
                  ])
                ])
              ])
            ])
          ])
        ]),
        footer: withCtx(() => [
          (selectedColumns.value.length > 0)
            ? (openBlock(), createElementBlock("a", {
                key: 0,
                href: downloadUrl.value,
                class: "btn btn-success me-2",
                target: "_blank",
                onClick: close
              }, [
                _cache[1] || (_cache[1] = createBaseVNode("i", { class: "fas fa-download me-1" }, null, -1 /* CACHED */)),
                createTextVNode(toDisplayString(_i18n('export.download')), 1 /* TEXT */)
              ], 8 /* PROPS */, _hoisted_13$r))
            : createCommentVNode("v-if", true)
        ]),
        _: 1 /* STABLE */
      }, 8 /* PROPS */, ["id"]))
    }
    }

    };

    var css_248z$v = "\n.form-check[data-v-5d6891e3] {\n    min-width: 200px;\n    margin-bottom: 0.5rem;\n}\n.form-check-input[data-v-5d6891e3] {\n    margin-top: 0.125rem;\n}\n.form-check-label[data-v-5d6891e3] {\n    cursor: pointer;\n    user-select: none;\n}\n.gap-2[data-v-5d6891e3] {\n    gap: 0.5rem;\n}\n.d-flex[data-v-5d6891e3] {\n    display: flex;\n}\n.flex-wrap[data-v-5d6891e3] {\n    flex-wrap: wrap;\n}\n.me-1[data-v-5d6891e3] {\n    margin-right: 0.25rem;\n}\n.me-2[data-v-5d6891e3] {\n    margin-right: 0.5rem;\n}\n.ms-2[data-v-5d6891e3] {\n    margin-left: 0.5rem;\n}\n.mt-2[data-v-5d6891e3] {\n    margin-top: 0.5rem;\n}\n";
    styleInject(css_248z$v);

    script$2l.__scopeId = "data-v-5d6891e3";
    script$2l.__file = "http_src/vue/modal-historical-flows-downloader.vue";

    const _hoisted_1$27 = { class: "btn-group align-items-center" };
    const _hoisted_2$1W = ["title"];


    var script$2k = {
      __name: 'custom-switch',
      props: {
        value: Boolean,
        title: String,
        label: String,
        icon: String /* e.g. fa-truck-fast */
    },
      emits: ['update:value', 'change_value'],
      setup(__props, { emit: __emit }) {

    const emit = __emit;

    const props = __props;

    const isOn = ref(false);

    onMounted(() => {
        isOn.value = props.value;
    });

    watch(() => props.value, (cur_value, old_value) => {
        isOn.value = props.value;
    }, { flush: 'pre' });

    function change_value() {
        emit('update:value', !isOn.value);
        emit('change_value', !isOn.value);
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$27, [
        createBaseVNode("a", {
          class: normalizeClass(["btn btn-link btn-sm", isOn.value ? '' : 'link-secondary']),
          type: "button",
          onClick: change_value,
          title: __props.title
        }, [
          createBaseVNode("i", {
            class: normalizeClass(["fas fa-lg", [isOn.value ? 'btn-success' : '', __props.icon]])
          }, null, 2 /* CLASS */)
        ], 10 /* CLASS, PROPS */, _hoisted_2$1W)
      ]))
    }
    }

    };

    script$2k.__file = "http_src/vue/custom-switch.vue";

    const _hoisted_1$26 = { class: "row" };
    const _hoisted_2$1V = { class: "col-12" };
    const _hoisted_3$1J = { class: "mb-2" };
    const _hoisted_4$1G = { class: "w-100" };
    const _hoisted_5$1q = { clas: "range-container d-flex flex-wrap" };
    const _hoisted_6$1b = { class: "range-picker d-flex m-auto flex-wrap" };
    const _hoisted_7$14 = { class: "me-2" };
    const _hoisted_8$W = ["value"];
    const _hoisted_9$L = ["label"];
    const _hoisted_10$F = ["value"];
    const _hoisted_11$B = ["title"];
    const _hoisted_12$w = ["title"];
    const _hoisted_13$q = ["title", "href"];
    const _hoisted_14$o = ["title"];
    const _hoisted_15$l = ["title"];
    const _hoisted_16$l = { class: "col-12" };
    const _hoisted_17$j = { class: "card card-shadow" };
    const _hoisted_18$g = { class: "card-body" };
    const _hoisted_19$f = { class: "row" };
    const _hoisted_20$f = {
      key: 0,
      class: "col-12 mb-2",
      id: "chart-vue"
    };
    const _hoisted_21$f = ["title"];
    const _hoisted_22$f = ["onClick", "title"];
    const _hoisted_23$f = {
      key: 0,
      class: "card-footer"
    };
    const _hoisted_24$e = {
      key: 0,
      id: "dt-btn-acknowledge",
      disabled: true,
      "data-bs-target": "#dt-acknowledge-modal",
      "data-bs-toggle": "modal",
      class: "btn btn-primary me-1"
    };
    const _hoisted_25$d = {
      key: 1,
      id: "dt-btn-delete",
      disabled: true,
      "data-bs-target": "#dt-delete-modal",
      "data-bs-toggle": "modal",
      class: "btn btn-danger"
    };

    const page_id = "page-flow-historical";

    var script$2j = {
      __name: 'page-flow-historical',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const alert_info = ref(null);
    const chart = ref(null);
    const table_flows = ref(null);
    const modal_traffic_extraction = ref(null);
    const modal_choose_columns_export = ref(null);
    const modal_snapshot = ref(null);
    const range_picker = ref(null);
    const permanent_link_button = ref(null);
    const modal_alerts_filter = ref(null);
    const modal_acknowledge = ref(null);
    const modal_delete = ref(null);
    const show_pcap_download = ref(false);
    const columns_tags_values = ref(null);

    const current_alert = ref(null);
    const default_ifid = props.context.ifid;
    const page = ref("");
    const table_config_id = ref("");
    const table_id = computed$1(() => {
        if (selected_query_preset.value?.value == null) { return table_config_id.value; }
        let id = `${table_config_id.value}_${selected_query_preset.value.value}`;
        return id;
    });

    /*******************************************************/
    /* SELECT EXPORT COLUMNS MODAL */

    function show_modal_select_export_columns() {
        // Get current columns from table_flows
        modal_choose_columns_export.value.show(columns_tags_values.value);
    }

    // function to get the values of columns tag and formatted value
    async function get_modal_columns_names() {
        const columns_tag_url = `${http_prefix}/lua/pro/rest/v2/get/flow/historical/column_names.lua`;
        let res = await ntopng_utility$1.http_request(columns_tag_url);
        columns_tags_values.value = res;
    }

    /*******************************************************/

    const href_analyse_records = computed$1(() => {
        if (count_page_components_reloaded.value < 0) { throw "never run"; }
        const analyse_endpoint = props.context.analyse.endpoint;
        let params = ntopng_url_manager$1.get_url_object();
        const url_params = ntopng_url_manager$1.obj_to_url_params(params);
        return `${analyse_endpoint}?${url_params}`;
    });

    let chart_data_url = `${http_prefix}/lua/pro/rest/v2/get/db/ts.lua`;

    const chart_style = computed$1(() => {
        return `height:${props.context?.chart_height ? props.context.chart_height : "300"}px!important`;

    });
    const chart_type = computed$1(() => {
        /* Chart type defined the json template (defaults in db_search.lua) */
        if (props.context?.chart_type == "topk-timeseries") {
            return ntopChartApex$1.typeChart.TS_STACKED;
        } else if (props.context?.chart_type == "heatmap") {
            return ntopChartApex$1.typeChart.HEATMAP;
        }
        return ntopChartApex$1.typeChart.TS_COLUMN;
    });

    const top_table_array = ref([]);
    const top_table_dropdown_array = ref([]);

    const selected_query_preset = ref({});
    const query_presets = ref([]);
    ref([]);
    const mount_range_picker = ref(false);

    const flows_aggregated = ref(false);
    const flow_type_label = ref(_i18n("datatable.aggregated"));
    const min_time_interval_id = ref(null);
    const round_time = ref(false);
    const count_page_components_reloaded = ref(0);

    onBeforeMount(async () => {
        init_params();
        await init_url_params();
        await set_query_presets();
        ntopng_events_manager$1.on_event_change('range_picker', ntopng_events.EPOCH_CHANGE, (new_status) => { update_show_download_pcap(new_status); }, true);
        mount_range_picker.value = true;
    });

    onMounted(async () => {
        // used in modal for flow csv export, fetch column tags (for url creation) and i18n value (for modal show)
        await get_modal_columns_names();
        register_components_on_status_update();
        load_top_table_array_overview();
    });

    function init_params() {
        page.value = ntopng_url_manager$1.get_url_entry("page");
        if (page.value == null) { page.value = "overview"; }
        chart_data_url = `${http_prefix}/lua/pro/rest/v2/get/db/ts.lua`;

        selected_query_preset.value = {
            value: ntopng_url_manager$1.get_url_entry("query_preset"),
        };
        if (selected_query_preset.value.value == null) {
            selected_query_preset.value.value = "";
        }
        table_config_id.value = props.context.is_enterprise_l ? `flow_historical_l` : `flow_historical`;
        const aggregated = ntopng_url_manager$1.get_url_entry("aggregated");
        if (aggregated == "true") {
            table_config_id.value = `flow_historical_aggregated`;
            flows_aggregated.value = true;
            min_time_interval_id.value = "hour";
            round_time.value = true;
        }
    }

    async function init_url_params() {
        const url_params_obj = ntopng_url_manager$1.get_url_object(ntopng_url_manager$1.get_url_params());
        if (ntopng_url_manager$1.get_url_entry("ifid") == null) {
            ntopng_url_manager$1.set_key_to_url("ifid", default_ifid);
        }
        for (const key in url_params_obj) {
            const value = url_params_obj[key];
            value.split(";"); /* Do not add the values like ;eq, it causes problems with linting */
    /*        if (dataUtils.isEmptyOrNull(result[0])) {
                ntopng_url_manager.delete_key_from_url(key)
            }
    */    }
        // 30 min default
        // chiamare set default_time interval
        if (flows_aggregated.value == false) {
            ntopng_utility$1.check_and_set_default_time_interval();
        }
        else {
            const f_check_last_minute_epoch_end = (epoch) => {
                let min_time_interval = ntopng_utility$1.get_timeframe_from_timeframe_id(min_time_interval_id.value);
                return epoch.epoch_end - epoch.epoch_begin < min_time_interval;
            };
            const epoch_interval = ntopng_utility$1.check_and_set_default_time_interval(min_time_interval_id.value, f_check_last_minute_epoch_end);
            if (epoch_interval != null) {
                epoch_interval.epoch_begin = ntopng_utility$1.round_time_by_timeframe_id(epoch_interval.epoch_begin, min_time_interval_id.value);
                epoch_interval.epoch_end = ntopng_utility$1.round_time_by_timeframe_id(epoch_interval.epoch_end, min_time_interval_id.value);
                ntopng_url_manager$1.set_key_to_url("epoch_begin", epoch_interval.epoch_begin);
                ntopng_url_manager$1.set_key_to_url("epoch_end", epoch_interval.epoch_end);
            }
        }

        if (ntopng_url_manager$1.get_url_entry("page") == "flow"
            && ntopng_url_manager$1.get_url_entry("status") == "engaged") {
            ntopng_url_manager$1.set_key_to_url("status", "historical");
        }
        if (ntopng_url_manager$1.get_url_entry("aggregated") == null) {
            ntopng_url_manager$1.set_key_to_url("aggregated", "false");
        }
        if (ntopng_url_manager$1.get_url_entry("count") == null) {
            ntopng_url_manager$1.set_key_to_url("count", "traffic_presence");
        }

    }

    function get_chart_config_from_preset_const(preset_const) {
        let chart = preset_const?.chart;
        if (chart != null && chart.length > 0) {
            return chart[0];
        }
        return {};
    }

    async function set_query_presets() {
        let url_request = `${http_prefix}/lua/pro/rest/v2/get/db/preset/consts.lua?page=${page.value}&aggregated=${flows_aggregated.value}`;
        let res = await ntopng_utility$1.http_request(url_request);

        query_presets.value = res[0].list.map((el) => {
            let chart_config = get_chart_config_from_preset_const(el);
            return {
                value: el.id, //== null ? "flow" : el.id,
                name: el.name,
                count: chart_config?.params?.count,
                chart_config: chart_config,
                builtin: true,
            };
        });
        if (res.length > 1) {
            res[1].list.forEach((el) => {
                let chart_config = get_chart_config_from_preset_const(el);
                let query = {
                    value: el.id,
                    name: el.name,
                    count: chart_config?.params?.count,
                    chart_config: chart_config,
                    is_preset: true,
                };
                query_presets.value.push(query);
            });
        }
        if (selected_query_preset.value == null || selected_query_preset.value.value == "") {
            selected_query_preset.value = query_presets.value[0];
        } else {
            let q = query_presets.value.find((i) => i.value == selected_query_preset.value.value);
            selected_query_preset.value = q || query_presets.value[0];
        }
        ntopng_url_manager$1.set_key_to_url("query_preset", selected_query_preset.value.value);
        ntopng_url_manager$1.set_key_to_url("count", selected_query_preset.value.count);
        ntopng_sync$1.ready(get_query_presets_sync_key());
    }

    const f_map_chart_options = async (chart_options) => {
        await ntopng_sync$1.on_ready(get_query_presets_sync_key());
        let formatter_type = selected_query_preset.value.chart_config?.unit_measure;
        if (formatter_type == null) {
            formatter_type = "number";
        }
        chart_options.yaxis.labels.formatter = formatterUtils.getFormatter(formatter_type);
        return chart_options;
    };

    function change_flow_type() {
        // currently we can't refresh component without reload the page because we need refresh props.context
        if (flows_aggregated.value == false) {
            ntopng_url_manager$1.delete_params(["aggregated"]);
        } else {
            ntopng_url_manager$1.set_key_to_url("aggregated", "true");
        }
        ntopng_url_manager$1.reload_url();
    }

    function update_select_query_presets() {
        ntopng_url_manager$1.set_key_to_url("query_preset", selected_query_preset.value.value);
        ntopng_url_manager$1.set_key_to_url("count", selected_query_preset.value.count);
        ntopng_url_manager$1.reload_url();
    }

    async function load_top_table_array_overview(action) {
        if (props.context.show_cards != true) { return; }
        top_table_array.value = await load_top_table_array("overview");
    }

    async function load_top_table_details(top, top_index) {
        top.show_spinner = true;
        await nextTick();
        if (top.data_loaded == false) {
            let new_top_array = await load_top_table_array(top.id);
            top.options = new_top_array.find((t) => t.id == top.id).options;
            await nextTick();
            let dropdown = top_table_dropdown_array.value[top_index];
            dropdown.load_menu();
        }
        top.show_spinner = false;
    }

    async function load_top_table_array(action, top) {
        // top_table.value = [];
        const url_params = ntopng_url_manager$1.get_url_params();
        const url = `${http_prefix}/lua/pro/rest/v2/get/flow/top.lua?${url_params}&action=${action}`;
        let res = await ntopng_utility$1.http_request(url);
        return res.map((t) => {
            if (t.value) {
                t.value.forEach((e, index) => {
                    if (e.count < 1) {
                        t.value[index].count = '<1';
                    }
                });
            }
            return {
                id: t.action || t.name,
                label: t.label,
                title: t.tooltip,
                show_spinner: false,
                data_loaded: action != 'overview',
                options: t.value,
            };
        });
    }

    const get_open_top_table_dropdown = (top, top_index) => {
        return (d) => {
            load_top_table_details(top, top_index);
        };
    };

    function match_traffic_recording_window(begin_epoch, end_epoch) {
        const w_first_epoch = props.context.n2disk_window_first_epoch;
        const w_last_epoch = props.context.n2disk_window_last_epoch;

        return begin_epoch >= w_first_epoch &&
            begin_epoch <= w_last_epoch &&
            end_epoch >= w_first_epoch &&
            end_epoch <= w_last_epoch;
    }

    function update_show_download_pcap(new_status) {
        const tmp_begin_epoch = new_status.epoch_begin;
        const tmp_end_epoch = new_status.epoch_end;
        show_pcap_download.value = match_traffic_recording_window(tmp_begin_epoch, tmp_end_epoch);
    }

    async function register_components_on_status_update() {
        await ntopng_sync$1.on_ready("range_picker");
        if (props.context.show_chart) {
            chart.value.register_status();
        }
        //updateDownloadButton();
        ntopng_status_manager$1.on_status_change(page.value, (new_status) => {
            ntopng_url_manager$1.get_url_params();
            table_flows.value.refresh_table();
            load_top_table_array_overview();
            count_page_components_reloaded.value += 1;
        }, false);
    }

    function on_table_loaded() {
        register_table_flows_events();
    }

    function register_table_flows_events() {
        let jquery_table_flows = $(`#${table_id.value}`);
        jquery_table_flows.on('click', `a.tag-filter`, async function (e) {
            add_table_row_filter(e, $(this));
        });
    }

    const map_table_def_columns = async (columns) => {
        await ntopng_sync$1.on_ready(get_query_presets_sync_key());
        const f_print_asn = (key, asn, row) => {
            if (asn !== undefined) {
                return `<a class='tag-filter' data-tag-key='${key}' data-tag-value='${asn.value}' title='${asn.title}' href='javascript:void(0)'>${asn.label}</a>`;
            }
            return "";
        };
        const f_print_latency = (key, latency, row) => {
            if (latency == null || latency == 0) { return ""; }
            return `<a class='tag-filter' data-tag-key='${key}' data-tag-value='${latency}' href='javascript:void(0)'>${latency} ms</a>`;
        };
        const f_print_state = (key, state, row) => {
            if (state == null || state == 0) { return ""; }
            return `<a class='tag-filter' data-tag-key='${key}' data-tag-value='${state.value}' href='javascript:void(0)'>${state.title}</a>`;

        };
        let map_columns = {
            "first_seen": (first_seen, row) => {
                if (first_seen !== undefined)
                    return formatterUtils.formatDateTime(first_seen.epoch);
            },
            "last_seen": (last_seen, row) => {
                if (last_seen !== undefined)
                    return formatterUtils.formatDateTime(last_seen.epoch);
            },
            "DURATION": (duration, row) => {
                return NtopUtils$1.secondsToTime(duration)
            },
            "THROUGHPUT": (throughput, row) => {
                return formatterUtils.getFormatter("bps_no_scale")(throughput);
            },
            "l4proto": (value, row) => { 
                let l4proto_string = DataTableRenders$1.filterize('l4proto', value.value, value.label); 
                if (row.protocol_info_json && row.protocol_info_json.verdict && row.protocol_info_json.verdict.pass === 0)
                    l4proto_string = `<strike>${l4proto_string}</strike>`;
                return l4proto_string
            },
            "l7proto": (proto, row) => {
                let confidence = "";
                if (proto.confidence !== undefined) {
                    const title = proto.confidence;
                    (title == "DPI") ? confidence = `<span class="badge bg-success" title="${title}">${title}</span>` : confidence = `<span class="badge bg-warning" title="${title}">${title}</span>`;
                }
                let l7proto_string = DataTableRenders$1.filterize('l7proto', proto.value, proto.label) + " " + `${confidence}`;
                if (row.protocol_info_json && row.protocol_info_json.verdict && row.protocol_info_json.verdict.pass === 0)
                    l7proto_string = `<strike>${l7proto_string}</strike>`;
                return l7proto_string;
            },
            "asn": (asn, row) => f_print_asn("asn", asn),
            "cli_asn": (cli_asn, row) => f_print_asn("cli_asn", cli_asn),
            "srv_asn": (srv_asn, row) => f_print_asn("srv_asn", srv_asn),
            "flow_risk": (flow_risks, row) => {
                if (flow_risks == null) { return ""; }
                let res = [];

                for (let i = 0; i < flow_risks.length; i++) {
                    const flow_risk = flow_risks[i];
                    const flow_risk_label = (flow_risk.label || flow_risk.value);
                    const flow_risk_remediation = (flow_risk.remediation);
                    res.push(`${flow_risk_label} ${flow_risk_remediation}`);
                }
                return res.join(', ');
            },
            "cli_nw_latency": (cli_nw_latency, row) => f_print_latency("cli_nw_latency", cli_nw_latency),
            "srv_nw_latency": (srv_nw_latency, row) => f_print_latency("srv_nw_latency", srv_nw_latency),
            "major_connection_state": (major_connection_state, row) => f_print_state("major_connection_state", major_connection_state),
            "minor_connection_state": (minor_connection_state, row) => f_print_state("minor_connection_state", minor_connection_state),
            "post_nat_ipv4_src_addr": (value, row) => { return DataTableRenders$1.filterize('post_nat_ipv4_src_addr', value.value, value.label) },
            "post_nat_src_port": (value, row) => { return DataTableRenders$1.filterize('post_nat_src_port', value.value, value.label) },
            "post_nat_ipv4_dst_addr": (value, row) => { return DataTableRenders$1.filterize('post_nat_ipv4_dst_addr', value.value, value.label) },
            "post_nat_dst_port": (value, row) => { return DataTableRenders$1.filterize('post_nat_dst_port', value.value, value.label) },
            "info": (info, row) => {
                if (info == null) { return ""; }
                return `<a class='tag-filter' data-tag-key='info' data-tag-value='${info.title}' title='${info.title}' href='javascript:void(0)'>${info.label}</a>`;
            },
        };
        columns = columns.filter((c) => props.context?.visible_columns[c.data_field] != false);
        if (selected_query_preset.value.is_preset && columns.length > 0) {
            // add action button that is the first button
            columns = [columns[0]].concat(props.context.columns_def);
        }

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];

            if (c.id == "actions") {
                const visible_dict = {
                    info: props.context.actions.show_info,
                    historical_data: props.context.actions.show_historical,
                    flow_alerts: props.context.actions.show_alerts,
                    pcap_download: props.context.actions.show_pcap_download,
                    row_data: props.context.is_enterprise_xl && flows_aggregated.value,
                };
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        if (b.id == 'pcap_download') {
                            if (!(row.first_seen && row.first_seen.epoch &&
                                row.last_seen && row.last_seen.epoch &&
                                match_traffic_recording_window(row.first_seen.epoch, row.last_seen.epoch))) {
                                current_class.push("disabled");
                            }
                        } else if (visible_dict[b.id] != null && visible_dict[b.id] == false) {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });
        return columns;
    };

    const add_table_row_filter = (e, a) => {
        e.stopPropagation();

        let key = undefined;
        let realValue = undefined;
        let operator = 'eq';

        // Read tag key and value from the <a> itself if provided
        if (a.data('tagKey') != undefined) key = a.data('tagKey');
        if (a.data('tagRealvalue') != undefined) realValue = a.data('tagRealvalue');
        else if (a.data('tagValue') != undefined) realValue = a.data('tagValue');
        if (a.data('tagOperator') != undefined) operator = a.data('tagOperator');

        let filter = {
            id: key,
            value: realValue,
            operator: operator,
        };
        add_filter(filter);
    };

    function add_top_table_filter(opt, event) {
        event.stopPropagation();
        let filter = {
            id: opt.key,
            value: opt.value,
            operator: opt.operator,
        };
        add_filter(filter);
    }

    function add_filter(filter) {
        if (range_picker.value.is_filter_defined(filter)) {
            ntopng_events_manager$1.emit_custom_event(ntopng_custom_events$1.SHOW_MODAL_FILTERS, filter);
        } else {
            ntopng_url_manager$1.set_key_to_url("query_preset", "");
            ntopng_url_manager$1.set_key_to_url(filter.id, `${filter.value};${filter.operator}`);
            ntopng_url_manager$1.reload_url();
        }
    }

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    function click_navbar_item(item) {
        ntopng_url_manager$1.set_key_to_url('page', item.page_name);
        ntopng_url_manager$1.reload_url();
    }

    function get_permanent_link() {
        const $this = $(permanent_link_button.value);
        const placeholder = document.createElement('input');
        placeholder.value = location.href;
        document.body.appendChild(placeholder);
        placeholder.select();

        // copy the url to the clipboard from the placeholder
        document.execCommand("copy");
        document.body.removeChild(placeholder);
        $this.attr("title", `${_i18n('copied')}!`)
            .tooltip("dispose")
            .tooltip()
            .tooltip("show");
    }

    function show_modal_traffic_extraction() {
        modal_traffic_extraction.value.show();
    }

    function show_modal_snapshot() {
        modal_snapshot.value.show();
    }

    async function add_exclude(params) {
        params.csrf = props.context.csrf;
        let url = `${http_prefix}/lua/pro/rest/v2/add/alert/exclusion.lua`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
            let url_params = ntopng_url_manager$1.get_url_params();
            setTimeout(() => {
                //todo reloadTable($table, url_params);
                ntopng_events_manager$1.emit_custom_event(ntopng_custom_events$1.SHOW_GLOBAL_ALERT_INFO, { text_html: _i18n('check_exclusion.disable_warn'), type: "alert-info", timeout: 2 });
            }, 1000);
        } catch (err) {
            console.error(err);
        }
    }

    function refresh_page_components(not_refresh_table) {
        let t = table_flows.value;
        let c = chart.value;
        setTimeout(() => {
            if (!not_refresh_table) {
                t.refresh_table();
            }
            c.update_chart();
        }, 1 * 1000);
    }

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_info": click_button_info,
            "click_button_flow_alerts": click_button_flow_alerts,
            "click_button_historical_flows": click_button_historical_flows,
            "click_button_pcap_download": click_button_pcap_download,
            "click_button_flows": click_button_flows,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    function click_button_info(event) {
        const flow = event.row;
        const href = `${http_prefix}/lua/pro/db_flow_details.lua?row_id=${flow.rowid}&tstamp=${flow.tstamp}&instance_name=${flow.NTOPNG_INSTANCE_NAME}`;
        window.open(href, "_blank");
    }

    function click_button_pcap_download(event) {
        const flow = event.row;
        const epoch_interval = { epoch_begin: flow?.filter?.epoch_begin, epoch_end: flow?.filter?.epoch_end };
        modal_traffic_extraction.value.show(flow?.filter?.bpf, epoch_interval);
    }

    function click_button_historical_flows(event) {
        const flow = event.row;
        let filters_params_object = {};
        for (let key in flow) {
            let filter_key = key;
            let filter_value = "";
            if (typeof flow[key] === 'object') {
                if (flow[key].value == null && flow[key].value != "") { continue; }
                if (flow[key].tag_key != null && flow[key].tag_key != "") {
                    filter_key = flow[key].tag_key;
                }
                filter_value = flow[key].value;
            } else {
                // Skip field
                continue;
            }

            let filter = `${filter_value};eq`;
            filters_params_object[filter_key] = filter;
        }
        ntopng_url_manager$1.set_key_to_url("query_preset", "");
        ntopng_url_manager$1.add_obj_to_url(filters_params_object);
        ntopng_url_manager$1.reload_url();
    }

    function click_button_flow_alerts(event) {
        const flow = event.row;
        if (flow.alerts_url) {
            ntopng_url_manager$1.go_to_url(flow.alerts_url);
        }
    }

    function click_button_flows(event) {
        const row_data = event.row;
        const query_preset = ntopng_url_manager$1.get_url_entry("query_preset");
        const epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
        const epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");
        const aggregated = ntopng_url_manager$1.get_url_entry("aggregated");
        const cli_ip = row_data?.cli_ip?.value || row_data?.flow?.cli_ip?.value;
        const srv_ip = row_data?.srv_ip?.value || row_data?.flow?.srv_ip?.value;
        const srv_port = row_data?.srv_port?.value;
        const probe_ip = row_data?.probe_ip?.value;
        const instance_name = row_data?.NTOPNG_INSTANCE_NAME;
        const vlan_id = row_data?.vlan_id?.value;
        const output_snmp = row_data?.output_snmp?.value;
        const input_snmp = row_data?.input_snmp?.value;

        let url = `${http_prefix}/lua/pro/db_search.lua?epoch_begin=${epoch_begin}&epoch_end=${epoch_end}`;

        if (instance_name) {
            url = url + `&instance_name=${instance_name}`;
        }

        if (aggregated && !dataUtils.isEmptyString(query_preset)) {
            url = url + `&aggregated=${aggregated}`;
        }

        if (vlan_id && vlan_id != 0) {
            url = url + `&vlan_id=${vlan_id};eq`;
        }

        if (cli_ip) {
            url = url + `&cli_ip=${cli_ip};eq`;
        }

        if (srv_ip) {
            url = url + `&srv_ip=${srv_ip};eq`;
        }

        if (srv_port) {
            url = url + `&srv_port=${srv_port};eq`;
        }

        if (probe_ip) {
            url = url + `&probe_ip=${probe_ip};eq`;
        }

        if (input_snmp && input_snmp != 0) {
            url = url + `&input_snmp=${input_snmp};eq`;
        }

        if (output_snmp && output_snmp != 0) {
            url = url + `&output_snmp=${output_snmp};eq`;
        }

        ntopng_url_manager$1.go_to_url(url);
    }

    function get_query_presets_sync_key() {
        return `${page_id}_query_presets`;
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createVNode(script$2K, {
          id: "navbar",
          main_title: __props.context.navbar.main_title,
          base_url: __props.context.navbar.base_url,
          help_link: __props.context.navbar.help_link,
          items_table: __props.context.navbar.items_table,
          onClick_item: click_navbar_item
        }, null, 8 /* PROPS */, ["main_title", "base_url", "help_link", "items_table"]),
        createBaseVNode("div", _hoisted_1$26, [
          createBaseVNode("div", _hoisted_2$1V, [
            createBaseVNode("div", _hoisted_3$1J, [
              createBaseVNode("div", _hoisted_4$1G, [
                createBaseVNode("div", _hoisted_5$1q, [
                  createBaseVNode("div", _hoisted_6$1b, [
                    createVNode(script$2J, {
                      id: "alert_info",
                      global: true,
                      ref_key: "alert_info",
                      ref: alert_info
                    }, null, 512 /* NEED_PATCH */),
                    (mount_range_picker.value)
                      ? (openBlock(), createBlock(script$2D, {
                          key: 0,
                          ref_key: "range_picker",
                          ref: range_picker,
                          id: "range_picker",
                          min_time_interval_id: min_time_interval_id.value,
                          round_time: round_time.value
                        }, {
                          begin: withCtx(() => [
                            createBaseVNode("div", _hoisted_7$14, [
                              withDirectives(createBaseVNode("select", {
                                class: "me-2 form-select",
                                style: {"min-width":"8rem"},
                                "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((selected_query_preset).value = $event)),
                                onChange: _cache[1] || (_cache[1] = $event => (update_select_query_presets()))
                              }, [
                                (openBlock(true), createElementBlock(Fragment, null, renderList(query_presets.value, (item) => {
                                  return (openBlock(), createElementBlock(Fragment, null, [
                                    (item.builtin == true)
                                      ? (openBlock(), createElementBlock("option", {
                                          key: 0,
                                          value: item
                                        }, toDisplayString(item.name), 9 /* TEXT, PROPS */, _hoisted_8$W))
                                      : createCommentVNode("v-if", true)
                                  ], 64 /* STABLE_FRAGMENT */))
                                }), 256 /* UNKEYED_FRAGMENT */)),
                                (page.value != 'analysis')
                                  ? (openBlock(), createElementBlock("optgroup", {
                                      key: 0,
                                      label: _i18n('queries.queries')
                                    }, [
                                      (openBlock(true), createElementBlock(Fragment, null, renderList(query_presets.value, (item) => {
                                        return (openBlock(), createElementBlock(Fragment, null, [
                                          (!item.builtin)
                                            ? (openBlock(), createElementBlock("option", {
                                                key: 0,
                                                value: item
                                              }, toDisplayString(item.name), 9 /* TEXT, PROPS */, _hoisted_10$F))
                                            : createCommentVNode("v-if", true)
                                        ], 64 /* STABLE_FRAGMENT */))
                                      }), 256 /* UNKEYED_FRAGMENT */))
                                    ], 8 /* PROPS */, _hoisted_9$L))
                                  : createCommentVNode("v-if", true)
                              ], 544 /* NEED_HYDRATION, NEED_PATCH */), [
                                [vModelSelect, selected_query_preset.value]
                              ])
                            ]),
                            (props.context.is_enterprise_xl)
                              ? (openBlock(), createBlock(script$2k, {
                                  key: 0,
                                  value: flows_aggregated.value,
                                  "onUpdate:value": _cache[2] || (_cache[2] = $event => ((flows_aggregated).value = $event)),
                                  class: "me-2",
                                  change_label_side: true,
                                  label: flow_type_label.value,
                                  style: {},
                                  icon: "fa-truck-fast",
                                  title: flow_type_label.value,
                                  onChange_value: change_flow_type
                                }, null, 8 /* PROPS */, ["value", "label", "title"]))
                              : createCommentVNode("v-if", true)
                          ]),
                          extra_range_buttons: withCtx(() => [
                            (__props.context.show_permalink)
                              ? (openBlock(), createElementBlock("button", {
                                  key: 0,
                                  class: "btn btn-link btn-sm",
                                  onClick: get_permanent_link,
                                  title: _i18n('graphs.get_permanent_link'),
                                  ref_key: "permanent_link_button",
                                  ref: permanent_link_button
                                }, [...(_cache[3] || (_cache[3] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-link" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_11$B))
                              : createCommentVNode("v-if", true),
                            createCommentVNode(" :href=\"href_download_records\" "),
                            (__props.context.show_download)
                              ? (openBlock(), createElementBlock("a", {
                                  key: 1,
                                  class: "btn btn-link btn-sm",
                                  title: _i18n('graphs.download_records'),
                                  onClick: show_modal_select_export_columns
                                }, [...(_cache[4] || (_cache[4] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-file" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_12$w))
                              : createCommentVNode("v-if", true),
                            (__props.context.show_analyse_records)
                              ? (openBlock(), createElementBlock("a", {
                                  key: 2,
                                  class: "btn btn-link btn-sm",
                                  title: _i18n('graphs.analyse_records'),
                                  href: href_analyse_records.value
                                }, [...(_cache[5] || (_cache[5] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-play" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_13$q))
                              : createCommentVNode("v-if", true),
                            (__props.context.show_pcap_download || show_pcap_download.value)
                              ? (openBlock(), createElementBlock("button", {
                                  key: 3,
                                  class: "btn btn-link btn-sm",
                                  onClick: show_modal_traffic_extraction,
                                  title: _i18n('traffic_recording.pcap_download')
                                }, [...(_cache[6] || (_cache[6] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-download" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_14$o))
                              : createCommentVNode("v-if", true),
                            (__props.context.is_ntop_enterprise_m)
                              ? (openBlock(), createElementBlock("button", {
                                  key: 4,
                                  class: "btn btn-link btn-sm",
                                  onClick: show_modal_snapshot,
                                  title: _i18n('datatable.manage_snapshots')
                                }, [...(_cache[7] || (_cache[7] = [
                                  createBaseVNode("i", { class: "fas fa-lg fa-camera-retro" }, null, -1 /* CACHED */)
                                ]))], 8 /* PROPS */, _hoisted_15$l))
                              : createCommentVNode("v-if", true)
                          ]),
                          _: 1 /* STABLE */
                        }, 8 /* PROPS */, ["min_time_interval_id", "round_time"]))
                      : createCommentVNode("v-if", true)
                  ])
                ])
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_16$l, [
            createBaseVNode("div", _hoisted_17$j, [
              createBaseVNode("div", _hoisted_18$g, [
                createBaseVNode("div", _hoisted_19$f, [
                  (__props.context.show_chart)
                    ? (openBlock(), createElementBlock("div", _hoisted_20$f, [
                        createBaseVNode("div", {
                          class: "card overflow-hidden",
                          style: normalizeStyle(chart_style.value)
                        }, [
                          createCommentVNode(" <div class=\"card h-300 overflow-hidden\"> "),
                          createVNode(script$2I, {
                            ref_key: "chart",
                            ref: chart,
                            id: "chart_0",
                            chart_type: chart_type.value,
                            base_url_request: unref(chart_data_url),
                            map_chart_options: f_map_chart_options,
                            register_on_status_change: false,
                            min_time_interval_id: min_time_interval_id.value,
                            round_time: round_time.value
                          }, null, 8 /* PROPS */, ["chart_type", "base_url_request", "min_time_interval_id", "round_time"])
                        ], 4 /* STYLE */)
                      ]))
                    : createCommentVNode("v-if", true),
                  createVNode(script$2w, {
                    ref_key: "table_flows",
                    ref: table_flows,
                    table_id: table_id.value,
                    table_config_id: table_config_id.value,
                    csrf: __props.context.csrf,
                    showLoading: true,
                    f_map_columns: map_table_def_columns,
                    get_extra_params_obj: get_extra_params_obj,
                    onLoaded: on_table_loaded,
                    onCustom_event: on_table_custom_event
                  }, {
                    custom_header: withCtx(() => [
                      (openBlock(true), createElementBlock(Fragment, null, renderList(top_table_array.value, (t, t_index) => {
                        return (openBlock(), createBlock(script$2B, {
                          f_on_open: get_open_top_table_dropdown(t, t_index),
                          ref_for: true,
                          ref: el => { top_table_dropdown_array.value[t_index] = el; }
                        }, {
                          title: withCtx(() => [
                            createVNode(script$2v, {
                              show: t.show_spinner,
                              size: "1rem",
                              class: "me-1"
                            }, null, 8 /* PROPS */, ["show"]),
                            createBaseVNode("a", {
                              class: "ntopng-truncate",
                              title: t.title
                            }, toDisplayString(t.label), 9 /* TEXT, PROPS */, _hoisted_21$f)
                          ]),
                          menu: withCtx(() => [
                            (openBlock(true), createElementBlock(Fragment, null, renderList(t.options, (opt) => {
                              return (openBlock(), createElementBlock("a", {
                                style: {"cursor":"pointer","display":"block"},
                                onClick: $event => (add_top_table_filter(opt, $event)),
                                class: "ntopng-truncate tag-filter",
                                title: opt.value
                              }, toDisplayString(opt.label + " (" +
                                                    opt.count + "%)"), 9 /* TEXT, PROPS */, _hoisted_22$f))
                            }), 256 /* UNKEYED_FRAGMENT */))
                          ]),
                          _: 2 /* DYNAMIC */
                        }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["f_on_open"]))
                      }), 256 /* UNKEYED_FRAGMENT */)),
                      createCommentVNode(" Dropdown columns ")
                    ]),
                    _: 1 /* STABLE */
                  }, 8 /* PROPS */, ["table_id", "table_config_id", "csrf"])
                ])
              ]),
              createCommentVNode(" card body "),
              (props.context.show_acknowledge_all || props.context.show_delete_all)
                ? (openBlock(), createElementBlock("div", _hoisted_23$f, [
                    (props.context.show_acknowledge_all)
                      ? (openBlock(), createElementBlock("button", _hoisted_24$e, [...(_cache[8] || (_cache[8] = [
                          createBaseVNode("i", { class: "fas fa fa-user-check" }, null, -1 /* CACHED */),
                          createTextVNode(" Acknowledge Alerts ", -1 /* CACHED */)
                        ]))]))
                      : createCommentVNode("v-if", true),
                    (props.context.show_delete_all)
                      ? (openBlock(), createElementBlock("button", _hoisted_25$d, [...(_cache[9] || (_cache[9] = [
                          createBaseVNode("i", { class: "fas fa fa-trash" }, null, -1 /* CACHED */),
                          createTextVNode(" Delete Alerts ", -1 /* CACHED */)
                        ]))]))
                      : createCommentVNode("v-if", true)
                  ]))
                : createCommentVNode("v-if", true),
              createCommentVNode(" card footer ")
            ]),
            createCommentVNode(" card-shadow ")
          ]),
          createCommentVNode(" div col ")
        ]),
        createCommentVNode(" div row "),
        createVNode(script$2t, {
          id: "modal_traffic_extraction",
          ref_key: "modal_traffic_extraction",
          ref: modal_traffic_extraction
        }, null, 512 /* NEED_PATCH */),
        createVNode(script$2s, {
          ref_key: "modal_snapshot",
          ref: modal_snapshot,
          csrf: __props.context.csrf
        }, null, 8 /* PROPS */, ["csrf"]),
        createVNode(script$2q, {
          ref_key: "modal_acknowledge",
          ref: modal_acknowledge,
          context: __props.context,
          onAcknowledge: refresh_page_components
        }, null, 8 /* PROPS */, ["context"]),
        createVNode(script$2p, {
          ref_key: "modal_delete",
          ref: modal_delete,
          context: __props.context,
          onDelete_alert: refresh_page_components
        }, null, 8 /* PROPS */, ["context"]),
        createVNode(script$2r, {
          alert: current_alert.value,
          page: page.value,
          onExclude: add_exclude,
          ref_key: "modal_alerts_filter",
          ref: modal_alerts_filter
        }, null, 8 /* PROPS */, ["alert", "page"]),
        createVNode(script$2l, {
          ref_key: "modal_choose_columns_export",
          ref: modal_choose_columns_export,
          id: 'select_columns'
        }, null, 512 /* NEED_PATCH */)
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$2j.__file = "http_src/vue/page-flow-historical.vue";

    /**
     * @license
     * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /** @type {Ticker} */
    var numericLinearTicks = function(a, b, pixels, opts, dygraph, vals) {
      var nonLogscaleOpts = function(opt) {
        if (opt === 'logscale') return false;
        return opts(opt);
      };
      return numericTicks(a, b, pixels, nonLogscaleOpts, dygraph, vals);
    };

    /** @type {Ticker} */
    var numericTicks = function(a, b, pixels, opts, dygraph, vals) {
      var pixels_per_tick = /** @type{number} */(opts('pixelsPerLabel'));
      var ticks = [];
      var i, j, tickV, nTicks;
      if (vals) {
        for (i = 0; i < vals.length; i++) {
          ticks.push({v: vals[i]});
        }
      } else {
        // TODO(danvk): factor this log-scale block out into a separate function.
        if (opts("logscale")) {
          nTicks  = Math.floor(pixels / pixels_per_tick);
          var minIdx = binarySearch(a, PREFERRED_LOG_TICK_VALUES, 1);
          var maxIdx = binarySearch(b, PREFERRED_LOG_TICK_VALUES, -1);
          if (minIdx == -1) {
            minIdx = 0;
          }
          if (maxIdx == -1) {
            maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1;
          }
          // Count the number of tick values would appear, if we can get at least
          // nTicks / 4 accept them.
          var lastDisplayed = null;
          if (maxIdx - minIdx >= nTicks / 4) {
            for (var idx = maxIdx; idx >= minIdx; idx--) {
              var tickValue = PREFERRED_LOG_TICK_VALUES[idx];
              var pixel_coord = Math.log(tickValue / a) / Math.log(b / a) * pixels;
              var tick = { v: tickValue };
              if (lastDisplayed === null) {
                lastDisplayed = {
                  tickValue : tickValue,
                  pixel_coord : pixel_coord
                };
              } else {
                if (Math.abs(pixel_coord - lastDisplayed.pixel_coord) >= pixels_per_tick) {
                  lastDisplayed = {
                    tickValue : tickValue,
                    pixel_coord : pixel_coord
                  };
                } else {
                  tick.label = "";
                }
              }
              ticks.push(tick);
            }
            // Since we went in backwards order.
            ticks.reverse();
          }
        }

        // ticks.length won't be 0 if the log scale function finds values to insert.
        if (ticks.length === 0) {
          // Basic idea:
          // Try labels every 1, 2, 5, 10, 20, 50, 100, etc.
          // Calculate the resulting tick spacing (i.e. this.height_ / nTicks).
          // The first spacing greater than pixelsPerYLabel is what we use.
          // TODO(danvk): version that works on a log scale.
          var kmg2 = opts("labelsKMG2");
          var mults, base;
          if (kmg2) {
            mults = [1, 2, 4, 8, 16, 32, 64, 128, 256];
            base = 16;
          } else {
            mults = [1, 2, 5, 10, 20, 50, 100];
            base = 10;
          }

          // Get the maximum number of permitted ticks based on the
          // graph's pixel size and pixels_per_tick setting.
          var max_ticks = Math.ceil(pixels / pixels_per_tick);

          // Now calculate the data unit equivalent of this tick spacing.
          // Use abs() since graphs may have a reversed Y axis.
          var units_per_tick = Math.abs(b - a) / max_ticks;

          // Based on this, get a starting scale which is the largest
          // integer power of the chosen base (10 or 16) that still remains
          // below the requested pixels_per_tick spacing.
          var base_power = Math.floor(Math.log(units_per_tick) / Math.log(base));
          var base_scale = Math.pow(base, base_power);

          // Now try multiples of the starting scale until we find one
          // that results in tick marks spaced sufficiently far apart.
          // The "mults" array should cover the range 1 .. base^2 to
          // adjust for rounding and edge effects.
          var scale, low_val, high_val, spacing;
          for (j = 0; j < mults.length; j++) {
            scale = base_scale * mults[j];
            low_val = Math.floor(a / scale) * scale;
            high_val = Math.ceil(b / scale) * scale;
            nTicks = Math.abs(high_val - low_val) / scale;
            spacing = pixels / nTicks;
            if (spacing > pixels_per_tick) break;
          }

          // Construct the set of ticks.
          // Allow reverse y-axis if it's explicitly requested.
          if (low_val > high_val) scale *= -1;
          for (i = 0; i <= nTicks; i++) {
            tickV = low_val + i * scale;
            ticks.push( {v: tickV} );
          }
        }
      }

      var formatter = /**@type{AxisLabelFormatter}*/(opts('axisLabelFormatter'));

      // Add labels to the ticks.
      for (i = 0; i < ticks.length; i++) {
        if (ticks[i].label !== undefined) continue;  // Use current label.
        // TODO(danvk): set granularity to something appropriate here.
        ticks[i].label = formatter.call(dygraph, ticks[i].v, 0, opts, dygraph);
      }

      return ticks;
    };

    /** @type {Ticker} */
    var dateTicker = function(a, b, pixels, opts, dygraph, vals) {
      var chosen = pickDateTickGranularity(a, b, pixels, opts);

      if (chosen >= 0) {
        return getDateAxis(a, b, chosen, opts, dygraph);
      } else {
        // this can happen if self.width_ is zero.
        return [];
      }
    };

    // Time granularity enumeration
    var Granularity = {
      MILLISECONDLY: 0,
      TWO_MILLISECONDLY: 1,
      FIVE_MILLISECONDLY: 2,
      TEN_MILLISECONDLY: 3,
      FIFTY_MILLISECONDLY: 4,
      HUNDRED_MILLISECONDLY: 5,
      FIVE_HUNDRED_MILLISECONDLY: 6,
      SECONDLY: 7,
      TWO_SECONDLY: 8,
      FIVE_SECONDLY: 9,
      TEN_SECONDLY: 10,
      THIRTY_SECONDLY: 11,
      MINUTELY: 12,
      TWO_MINUTELY: 13,
      FIVE_MINUTELY: 14,
      TEN_MINUTELY: 15,
      THIRTY_MINUTELY: 16,
      HOURLY: 17,
      TWO_HOURLY: 18,
      SIX_HOURLY: 19,
      DAILY: 20,
      TWO_DAILY: 21,
      WEEKLY: 22,
      MONTHLY: 23,
      QUARTERLY: 24,
      BIANNUAL: 25,
      ANNUAL: 26,
      DECADAL: 27,
      CENTENNIAL: 28,
      NUM_GRANULARITIES: 29
    };

    // Date components enumeration (in the order of the arguments in Date)
    // TODO: make this an @enum
    var DateField = {
      DATEFIELD_Y: 0,
      DATEFIELD_M: 1,
      DATEFIELD_D: 2,
      DATEFIELD_HH: 3,
      DATEFIELD_MM: 4,
      DATEFIELD_SS: 5,
      DATEFIELD_MS: 6,
      NUM_DATEFIELDS: 7
    };

    /**
     * The value of datefield will start at an even multiple of "step", i.e.
     *   if datefield=SS and step=5 then the first tick will be on a multiple of 5s.
     *
     * For granularities <= HOURLY, ticks are generated every `spacing` ms.
     *
     * At coarser granularities, ticks are generated by incrementing `datefield` by
     *   `step`. In this case, the `spacing` value is only used to estimate the
     *   number of ticks. It should roughly correspond to the spacing between
     *   adjacent ticks.
     *
     * @type {Array.<{datefield:number, step:number, spacing:number}>}
     */
    var TICK_PLACEMENT = [];
    TICK_PLACEMENT[Granularity.MILLISECONDLY]               = {datefield: DateField.DATEFIELD_MS, step:   1, spacing: 1};
    TICK_PLACEMENT[Granularity.TWO_MILLISECONDLY]           = {datefield: DateField.DATEFIELD_MS, step:   2, spacing: 2};
    TICK_PLACEMENT[Granularity.FIVE_MILLISECONDLY]          = {datefield: DateField.DATEFIELD_MS, step:   5, spacing: 5};
    TICK_PLACEMENT[Granularity.TEN_MILLISECONDLY]           = {datefield: DateField.DATEFIELD_MS, step:  10, spacing: 10};
    TICK_PLACEMENT[Granularity.FIFTY_MILLISECONDLY]         = {datefield: DateField.DATEFIELD_MS, step:  50, spacing: 50};
    TICK_PLACEMENT[Granularity.HUNDRED_MILLISECONDLY]       = {datefield: DateField.DATEFIELD_MS, step: 100, spacing: 100};
    TICK_PLACEMENT[Granularity.FIVE_HUNDRED_MILLISECONDLY]  = {datefield: DateField.DATEFIELD_MS, step: 500, spacing: 500};
    TICK_PLACEMENT[Granularity.SECONDLY]        = {datefield: DateField.DATEFIELD_SS, step:   1, spacing: 1000 * 1};
    TICK_PLACEMENT[Granularity.TWO_SECONDLY]    = {datefield: DateField.DATEFIELD_SS, step:   2, spacing: 1000 * 2};
    TICK_PLACEMENT[Granularity.FIVE_SECONDLY]   = {datefield: DateField.DATEFIELD_SS, step:   5, spacing: 1000 * 5};
    TICK_PLACEMENT[Granularity.TEN_SECONDLY]    = {datefield: DateField.DATEFIELD_SS, step:  10, spacing: 1000 * 10};
    TICK_PLACEMENT[Granularity.THIRTY_SECONDLY] = {datefield: DateField.DATEFIELD_SS, step:  30, spacing: 1000 * 30};
    TICK_PLACEMENT[Granularity.MINUTELY]        = {datefield: DateField.DATEFIELD_MM, step:   1, spacing: 1000 * 60};
    TICK_PLACEMENT[Granularity.TWO_MINUTELY]    = {datefield: DateField.DATEFIELD_MM, step:   2, spacing: 1000 * 60 * 2};
    TICK_PLACEMENT[Granularity.FIVE_MINUTELY]   = {datefield: DateField.DATEFIELD_MM, step:   5, spacing: 1000 * 60 * 5};
    TICK_PLACEMENT[Granularity.TEN_MINUTELY]    = {datefield: DateField.DATEFIELD_MM, step:  10, spacing: 1000 * 60 * 10};
    TICK_PLACEMENT[Granularity.THIRTY_MINUTELY] = {datefield: DateField.DATEFIELD_MM, step:  30, spacing: 1000 * 60 * 30};
    TICK_PLACEMENT[Granularity.HOURLY]          = {datefield: DateField.DATEFIELD_HH, step:   1, spacing: 1000 * 3600};
    TICK_PLACEMENT[Granularity.TWO_HOURLY]      = {datefield: DateField.DATEFIELD_HH, step:   2, spacing: 1000 * 3600 * 2};
    TICK_PLACEMENT[Granularity.SIX_HOURLY]      = {datefield: DateField.DATEFIELD_HH, step:   6, spacing: 1000 * 3600 * 6};
    TICK_PLACEMENT[Granularity.DAILY]           = {datefield: DateField.DATEFIELD_D,  step:   1, spacing: 1000 * 86400};
    TICK_PLACEMENT[Granularity.TWO_DAILY]       = {datefield: DateField.DATEFIELD_D,  step:   2, spacing: 1000 * 86400 * 2};
    TICK_PLACEMENT[Granularity.WEEKLY]          = {datefield: DateField.DATEFIELD_D,  step:   7, spacing: 1000 * 604800};
    TICK_PLACEMENT[Granularity.MONTHLY]         = {datefield: DateField.DATEFIELD_M,  step:   1, spacing: 1000 * 7200  * 365.2425}; // 1e3 * 60 * 60 * 24 * 365.2425 / 12
    TICK_PLACEMENT[Granularity.QUARTERLY]       = {datefield: DateField.DATEFIELD_M,  step:   3, spacing: 1000 * 21600 * 365.2425}; // 1e3 * 60 * 60 * 24 * 365.2425 / 4
    TICK_PLACEMENT[Granularity.BIANNUAL]        = {datefield: DateField.DATEFIELD_M,  step:   6, spacing: 1000 * 43200 * 365.2425}; // 1e3 * 60 * 60 * 24 * 365.2425 / 2
    TICK_PLACEMENT[Granularity.ANNUAL]          = {datefield: DateField.DATEFIELD_Y,  step:   1, spacing: 1000 * 86400   * 365.2425}; // 1e3 * 60 * 60 * 24 * 365.2425 * 1
    TICK_PLACEMENT[Granularity.DECADAL]         = {datefield: DateField.DATEFIELD_Y,  step:  10, spacing: 1000 * 864000  * 365.2425}; // 1e3 * 60 * 60 * 24 * 365.2425 * 10
    TICK_PLACEMENT[Granularity.CENTENNIAL]      = {datefield: DateField.DATEFIELD_Y,  step: 100, spacing: 1000 * 8640000 * 365.2425}; // 1e3 * 60 * 60 * 24 * 365.2425 * 100

    /**
     * This is a list of human-friendly values at which to show tick marks on a log
     * scale. It is k * 10^n, where k=1..9 and n=-39..+39, so:
     * ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ...
     * NOTE: this assumes that utils.LOG_SCALE = 10.
     * @type {Array.<number>}
     */
    var PREFERRED_LOG_TICK_VALUES = (function() {
      var vals = [];
      for (var power = -39; power <= 39; power++) {
        var range = Math.pow(10, power);
        for (var mult = 1; mult <= 9; mult++) {
          var val = range * mult;
          vals.push(val);
        }
      }
      return vals;
    })();

    /**
     * Determine the correct granularity of ticks on a date axis.
     *
     * @param {number} a Left edge of the chart (ms)
     * @param {number} b Right edge of the chart (ms)
     * @param {number} pixels Size of the chart in the relevant dimension (width).
     * @param {function(string):*} opts Function mapping from option name -&gt; value.
     * @return {number} The appropriate axis granularity for this chart. See the
     *     enumeration of possible values in dygraph-tickers.js.
     */
    var pickDateTickGranularity = function(a, b, pixels, opts) {
      var pixels_per_tick = /** @type{number} */(opts('pixelsPerLabel'));
      for (var i = 0; i < Granularity.NUM_GRANULARITIES; i++) {
        var num_ticks = numDateTicks(a, b, i);
        if (pixels / num_ticks >= pixels_per_tick) {
          return i;
        }
      }
      return -1;
    };

    /**
     * Compute the number of ticks on a date axis for a given granularity.
     * @param {number} start_time
     * @param {number} end_time
     * @param {number} granularity (one of the granularities enumerated above)
     * @return {number} (Approximate) number of ticks that would result.
     */
    var numDateTicks = function(start_time, end_time, granularity) {
      var spacing = TICK_PLACEMENT[granularity].spacing;
      return Math.round(1.0 * (end_time - start_time) / spacing);
    };

    /**
     * Compute the positions and labels of ticks on a date axis for a given granularity.
     * @param {number} start_time
     * @param {number} end_time
     * @param {number} granularity (one of the granularities enumerated above)
     * @param {function(string):*} opts Function mapping from option name -&gt; value.
     * @param {Dygraph=} dg
     * @return {!TickList}
     */
    var getDateAxis = function(start_time, end_time, granularity, opts, dg) {
      var formatter = /** @type{AxisLabelFormatter} */(
          opts("axisLabelFormatter"));
      var utc = opts("labelsUTC");
      var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal;

      var datefield = TICK_PLACEMENT[granularity].datefield;
      var step = TICK_PLACEMENT[granularity].step;
      var spacing = TICK_PLACEMENT[granularity].spacing;

      // Choose a nice tick position before the initial instant.
      // Currently, this code deals properly with the existent daily granularities:
      // DAILY (with step of 1) and WEEKLY (with step of 7 but specially handled).
      // Other daily granularities (say TWO_DAILY) should also be handled specially
      // by setting the start_date_offset to 0.
      var start_date = new Date(start_time);
      var date_array = [];
      date_array[DateField.DATEFIELD_Y]  = accessors.getFullYear(start_date);
      date_array[DateField.DATEFIELD_M]  = accessors.getMonth(start_date);
      date_array[DateField.DATEFIELD_D]  = accessors.getDate(start_date);
      date_array[DateField.DATEFIELD_HH] = accessors.getHours(start_date);
      date_array[DateField.DATEFIELD_MM] = accessors.getMinutes(start_date);
      date_array[DateField.DATEFIELD_SS] = accessors.getSeconds(start_date);
      date_array[DateField.DATEFIELD_MS] = accessors.getMilliseconds(start_date);

      var start_date_offset = date_array[datefield] % step;
      if (granularity == Granularity.WEEKLY) {
        // This will put the ticks on Sundays.
        start_date_offset = accessors.getDay(start_date);
      }

      date_array[datefield] -= start_date_offset;
      for (var df = datefield + 1; df < DateField.NUM_DATEFIELDS; df++) {
        // The minimum value is 1 for the day of month, and 0 for all other fields.
        date_array[df] = (df === DateField.DATEFIELD_D) ? 1 : 0;
      }

      // Generate the ticks.
      // For granularities not coarser than HOURLY we use the fact that:
      //   the number of milliseconds between ticks is constant
      //   and equal to the defined spacing.
      // Otherwise we rely on the 'roll over' property of the Date functions:
      //   when some date field is set to a value outside of its logical range,
      //   the excess 'rolls over' the next (more significant) field.
      // However, when using local time with DST transitions,
      // there are dates that do not represent any time value at all
      // (those in the hour skipped at the 'spring forward'),
      // and the JavaScript engines usually return an equivalent value.
      // Hence we have to check that the date is properly increased at each step,
      // returning a date at a nice tick position.
      var ticks = [];
      var tick_date = accessors.makeDate.apply(null, date_array);
      var tick_time = tick_date.getTime();
      if (granularity <= Granularity.HOURLY) {
        if (tick_time < start_time) {
          tick_time += spacing;
          tick_date = new Date(tick_time);
        }
        while (tick_time <= end_time) {
          ticks.push({ v: tick_time,
                       label: formatter.call(dg, tick_date, granularity, opts, dg)
                     });
          tick_time += spacing;
          tick_date = new Date(tick_time);
        }
      } else {
        if (tick_time < start_time) {
          date_array[datefield] += step;
          tick_date = accessors.makeDate.apply(null, date_array);
          tick_time = tick_date.getTime();
        }
        while (tick_time <= end_time) {
          if (granularity >= Granularity.DAILY ||
              accessors.getHours(tick_date) % step === 0) {
            ticks.push({ v: tick_time,
                         label: formatter.call(dg, tick_date, granularity, opts, dg)
                       });
          }
          date_array[datefield] += step;
          tick_date = accessors.makeDate.apply(null, date_array);
          tick_time = tick_date.getTime();
        }
      }
      return ticks;
    };

    /**
     * @license
     * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    var LOG_SCALE = 10;
    var LN_TEN = Math.log(LOG_SCALE);

    /**
     * @private
     * @param {number} x
     * @return {number}
     */
    var log10 = function(x) {
      return Math.log(x) / LN_TEN;
    };

    /**
     * @private
     * @param {number} r0
     * @param {number} r1
     * @param {number} pct
     * @return {number}
     */
    var logRangeFraction = function(r0, r1, pct) {
      // Computing the inverse of toPercentXCoord. The function was arrived at with
      // the following steps:
      //
      // Original calcuation:
      // pct = (log(x) - log(xRange[0])) / (log(xRange[1]) - log(xRange[0]));
      //
      // Multiply both sides by the right-side denominator.
      // pct * (log(xRange[1] - log(xRange[0]))) = log(x) - log(xRange[0])
      //
      // add log(xRange[0]) to both sides
      // log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0]))) = log(x);
      //
      // Swap both sides of the equation,
      // log(x) = log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])))
      //
      // Use both sides as the exponent in 10^exp and we're done.
      // x = 10 ^ (log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0]))))

      var logr0 = log10(r0);
      var logr1 = log10(r1);
      var exponent = logr0 + (pct * (logr1 - logr0));
      var value = Math.pow(LOG_SCALE, exponent);
      return value;
    };
    /** A dashed line stroke pattern. */
    var DASHED_LINE = [7, 3];
    /** A dot dash stroke pattern. */
    var DOT_DASH_LINE = [7, 2, 2, 2];

    // Directions for panning and zooming. Use bit operations when combined
    // values are possible.
    var HORIZONTAL = 1;
    var VERTICAL = 2;

    /**
     * Return the 2d context for a dygraph canvas.
     *
     * This method is only exposed for the sake of replacing the function in
     * automated tests.
     *
     * @param {!HTMLCanvasElement} canvas
     * @return {!CanvasRenderingContext2D}
     * @private
     */
    var getContext = function(canvas) {
      return /** @type{!CanvasRenderingContext2D}*/(canvas.getContext("2d"));
    };

    /**
     * Add an event handler.
     * @param {!Node} elem The element to add the event to.
     * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
     * @param {function(Event):(boolean|undefined)} fn The function to call
     *     on the event. The function takes one parameter: the event object.
     * @private
     */
    var addEvent = function addEvent(elem, type, fn) {
      elem.addEventListener(type, fn, false);
    };

    /**
     * Remove an event handler.
     * @param {!Node} elem The element to remove the event from.
     * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
     * @param {function(Event):(boolean|undefined)} fn The function to call
     *     on the event. The function takes one parameter: the event object.
     */
    function removeEvent(elem, type, fn) {
      elem.removeEventListener(type, fn, false);
    }

    /**
     * Cancels further processing of an event. This is useful to prevent default
     * browser actions, e.g. highlighting text on a double-click.
     * Based on the article at
     * http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel
     * @param {!Event} e The event whose normal behavior should be canceled.
     * @private
     */
    function cancelEvent(e) {
      e = e ? e : window.event;
      if (e.stopPropagation) {
        e.stopPropagation();
      }
      if (e.preventDefault) {
        e.preventDefault();
      }
      e.cancelBubble = true;
      e.cancel = true;
      e.returnValue = false;
      return false;
    }

    /**
     * Convert hsv values to an rgb(r,g,b) string. Taken from MochiKit.Color. This
     * is used to generate default series colors which are evenly spaced on the
     * color wheel.
     * @param {number} hue Range is 0.0-1.0.
     * @param {number} saturation Range is 0.0-1.0.
     * @param {number} value Range is 0.0-1.0.
     * @return {string} "rgb(r,g,b)" where r, g and b range from 0-255.
     * @private
     */
    function hsvToRGB(hue, saturation, value) {
      var red;
      var green;
      var blue;
      if (saturation === 0) {
        red = value;
        green = value;
        blue = value;
      } else {
        var i = Math.floor(hue * 6);
        var f = (hue * 6) - i;
        var p = value * (1 - saturation);
        var q = value * (1 - (saturation * f));
        var t = value * (1 - (saturation * (1 - f)));
        switch (i) {
          case 1: red = q; green = value; blue = p; break;
          case 2: red = p; green = value; blue = t; break;
          case 3: red = p; green = q; blue = value; break;
          case 4: red = t; green = p; blue = value; break;
          case 5: red = value; green = p; blue = q; break;
          case 6: // fall through
          case 0: red = value; green = t; blue = p; break;
        }
      }
      red = Math.floor(255 * red + 0.5);
      green = Math.floor(255 * green + 0.5);
      blue = Math.floor(255 * blue + 0.5);
      return 'rgb(' + red + ',' + green + ',' + blue + ')';
    }

    /**
     * Find the coordinates of an object relative to the top left of the page.
     *
     * @param {Node} obj
     * @return {{x:number,y:number}}
     * @private
     */
    function findPos(obj) {
      var p = obj.getBoundingClientRect(),
          w = window,
          d = document.documentElement;

      return {
        x: p.left + (w.pageXOffset || d.scrollLeft),
        y: p.top  + (w.pageYOffset || d.scrollTop)
      }
    }

    /**
     * Returns the x-coordinate of the event in a coordinate system where the
     * top-left corner of the page (not the window) is (0,0).
     * Taken from MochiKit.Signal
     * @param {!Event} e
     * @return {number}
     * @private
     */
    function pageX(e) {
      return (!e.pageX || e.pageX < 0) ? 0 : e.pageX;
    }

    /**
     * Returns the y-coordinate of the event in a coordinate system where the
     * top-left corner of the page (not the window) is (0,0).
     * Taken from MochiKit.Signal
     * @param {!Event} e
     * @return {number}
     * @private
     */
    function pageY(e) {
      return (!e.pageY || e.pageY < 0) ? 0 : e.pageY;
    }

    /**
     * Converts page the x-coordinate of the event to pixel x-coordinates on the
     * canvas (i.e. DOM Coords).
     * @param {!Event} e Drag event.
     * @param {!DygraphInteractionContext} context Interaction context object.
     * @return {number} The amount by which the drag has moved to the right.
     */
    function dragGetX_(e, context) {
      return pageX(e) - context.px;
    }

    /**
     * Converts page the y-coordinate of the event to pixel y-coordinates on the
     * canvas (i.e. DOM Coords).
     * @param {!Event} e Drag event.
     * @param {!DygraphInteractionContext} context Interaction context object.
     * @return {number} The amount by which the drag has moved down.
     */
    function dragGetY_(e, context) {
      return pageY(e) - context.py;
    }

    /**
     * This returns true unless the parameter is 0, null, undefined or NaN.
     * TODO(danvk): rename this function to something like 'isNonZeroNan'.
     *
     * @param {number} x The number to consider.
     * @return {boolean} Whether the number is zero or NaN.
     * @private
     */
    function isOK(x) {
      return !!x && !isNaN(x);
    }

    /**
     * @param {{x:?number,y:?number,yval:?number}} p The point to consider, valid
     *     points are {x, y} objects
     * @param {boolean=} opt_allowNaNY Treat point with y=NaN as valid
     * @return {boolean} Whether the point has numeric x and y.
     * @private
     */
    function isValidPoint(p, opt_allowNaNY) {
      if (!p) return false;  // null or undefined object
      if (p.yval === null) return false;  // missing point
      if (p.x === null || p.x === undefined) return false;
      if (p.y === null || p.y === undefined) return false;
      if (isNaN(p.x) || (!opt_allowNaNY && isNaN(p.y))) return false;
      return true;
    }

    /**
     * Number formatting function which mimics the behavior of %g in printf, i.e.
     * either exponential or fixed format (without trailing 0s) is used depending on
     * the length of the generated string.  The advantage of this format is that
     * there is a predictable upper bound on the resulting string length,
     * significant figures are not dropped, and normal numbers are not displayed in
     * exponential notation.
     *
     * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g.
     * It creates strings which are too long for absolute values between 10^-4 and
     * 10^-6, e.g. '0.00001' instead of '1e-5'. See tests/number-format.html for
     * output examples.
     *
     * @param {number} x The number to format
     * @param {number=} opt_precision The precision to use, default 2.
     * @return {string} A string formatted like %g in printf.  The max generated
     *                  string length should be precision + 6 (e.g 1.123e+300).
     */
    function floatFormat(x, opt_precision) {
      // Avoid invalid precision values; [1, 21] is the valid range.
      var p = Math.min(Math.max(1, opt_precision || 2), 21);

      // This is deceptively simple.  The actual algorithm comes from:
      //
      // Max allowed length = p + 4
      // where 4 comes from 'e+n' and '.'.
      //
      // Length of fixed format = 2 + y + p
      // where 2 comes from '0.' and y = # of leading zeroes.
      //
      // Equating the two and solving for y yields y = 2, or 0.00xxxx which is
      // 1.0e-3.
      //
      // Since the behavior of toPrecision() is identical for larger numbers, we
      // don't have to worry about the other bound.
      //
      // Finally, the argument for toExponential() is the number of trailing digits,
      // so we take off 1 for the value before the '.'.
      return (Math.abs(x) < 1.0e-3 && x !== 0.0) ?
          x.toExponential(p - 1) : x.toPrecision(p);
    }

    /**
     * Converts '9' to '09' (useful for dates)
     * @param {number} x
     * @return {string}
     * @private
     */
    function zeropad(x) {
      if (x < 10) return "0" + x; else return "" + x;
    }

    /**
     * Date accessors to get the parts of a calendar date (year, month,
     * day, hour, minute, second and millisecond) according to local time,
     * and factory method to call the Date constructor with an array of arguments.
     */
    var DateAccessorsLocal = {
      getFullYear:     d => d.getFullYear(),
      getMonth:        d => d.getMonth(),
      getDate:         d => d.getDate(),
      getHours:        d => d.getHours(),
      getMinutes:      d => d.getMinutes(),
      getSeconds:      d => d.getSeconds(),
      getMilliseconds: d => d.getMilliseconds(),
      getDay:          d => d.getDay(),
      makeDate:        function(y, m, d, hh, mm, ss, ms) {
        return new Date(y, m, d, hh, mm, ss, ms);
      }
    };

    /**
     * Date accessors to get the parts of a calendar date (year, month,
     * day of month, hour, minute, second and millisecond) according to UTC time,
     * and factory method to call the Date constructor with an array of arguments.
     */
    var DateAccessorsUTC = {
      getFullYear:     d => d.getUTCFullYear(),
      getMonth:        d => d.getUTCMonth(),
      getDate:         d => d.getUTCDate(),
      getHours:        d => d.getUTCHours(),
      getMinutes:      d => d.getUTCMinutes(),
      getSeconds:      d => d.getUTCSeconds(),
      getMilliseconds: d => d.getUTCMilliseconds(),
      getDay:          d => d.getUTCDay(),
      makeDate:        function(y, m, d, hh, mm, ss, ms) {
        return new Date(Date.UTC(y, m, d, hh, mm, ss, ms));
      }
    };

    /**
     * Return a string version of the hours, minutes and seconds portion of a date.
     * @param {number} hh The hours (from 0-23)
     * @param {number} mm The minutes (from 0-59)
     * @param {number} ss The seconds (from 0-59)
     * @return {string} A time of the form "HH:MM" or "HH:MM:SS"
     * @private
     */
    function hmsString_(hh, mm, ss, ms) {
      var ret = zeropad(hh) + ":" + zeropad(mm);
      if (ss) {
        ret += ":" + zeropad(ss);
        if (ms) {
          var str = "" + ms;
          ret += "." + ('000'+str).substring(str.length);
        }
      }
      return ret;
    }

    /**
     * Convert a JS date (millis since epoch) to a formatted string.
     * @param {number} time The JavaScript time value (ms since epoch)
     * @param {boolean} utc Whether output UTC or local time
     * @return {string} A date of one of these forms:
     *     "YYYY/MM/DD", "YYYY/MM/DD HH:MM" or "YYYY/MM/DD HH:MM:SS"
     * @private
     */
    function dateString_(time, utc) {
      var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal;
      var date = new Date(time);
      var y = accessors.getFullYear(date);
      var m = accessors.getMonth(date);
      var d = accessors.getDate(date);
      var hh = accessors.getHours(date);
      var mm = accessors.getMinutes(date);
      var ss = accessors.getSeconds(date);
      var ms = accessors.getMilliseconds(date);
      // Get a year string:
      var year = "" + y;
      // Get a 0 padded month string
      var month = zeropad(m + 1);  //months are 0-offset, sigh
      // Get a 0 padded day string
      var day = zeropad(d);
      var frac = hh * 3600 + mm * 60 + ss + 1e-3 * ms;
      var ret = year + "/" + month + "/" + day;
      if (frac) {
        ret += " " + hmsString_(hh, mm, ss, ms);
      }
      return ret;
    }

    /**
     * Round a number to the specified number of digits past the decimal point.
     * @param {number} num The number to round
     * @param {number} places The number of decimals to which to round
     * @return {number} The rounded number
     * @private
     */
    function round_(num, places) {
      var shift = Math.pow(10, places);
      return Math.round(num * shift)/shift;
    }

    /**
     * Implementation of binary search over an array.
     * Currently does not work when val is outside the range of arry's values.
     * @param {number} val the value to search for
     * @param {Array.<number>} arry is the value over which to search
     * @param {number} abs If abs > 0, find the lowest entry greater than val
     *     If abs < 0, find the highest entry less than val.
     *     If abs == 0, find the entry that equals val.
     * @param {number=} low The first index in arry to consider (optional)
     * @param {number=} high The last index in arry to consider (optional)
     * @return {number} Index of the element, or -1 if it isn't found.
     * @private
     */
    function binarySearch(val, arry, abs, low, high) {
      if (low === null || low === undefined ||
          high === null || high === undefined) {
        low = 0;
        high = arry.length - 1;
      }
      if (low > high) {
        return -1;
      }
      if (abs === null || abs === undefined) {
        abs = 0;
      }
      var validIndex = function(idx) {
        return idx >= 0 && idx < arry.length;
      };
      var mid = parseInt((low + high) / 2, 10);
      var element = arry[mid];
      var idx;
      if (element == val) {
        return mid;
      } else if (element > val) {
        if (abs > 0) {
          // Accept if element > val, but also if prior element < val.
          idx = mid - 1;
          if (validIndex(idx) && arry[idx] < val) {
            return mid;
          }
        }
        return binarySearch(val, arry, abs, low, mid - 1);
      } else if (element < val) {
        if (abs < 0) {
          // Accept if element < val, but also if prior element > val.
          idx = mid + 1;
          if (validIndex(idx) && arry[idx] > val) {
            return mid;
          }
        }
        return binarySearch(val, arry, abs, mid + 1, high);
      }
      return -1;  // can't actually happen, but makes closure compiler happy
    }

    /**
     * Parses a date, returning the number of milliseconds since epoch. This can be
     * passed in as an xValueParser in the Dygraph constructor.
     * TODO(danvk): enumerate formats that this understands.
     *
     * @param {string} dateStr A date in a variety of possible string formats.
     * @return {number} Milliseconds since epoch.
     * @private
     */
    function dateParser(dateStr) {
      var dateStrSlashed;
      var d;

      // Let the system try the format first, with one caveat:
      // YYYY-MM-DD[ HH:MM:SS] is interpreted as UTC by a variety of browsers.
      // dygraphs displays dates in local time, so this will result in surprising
      // inconsistencies. But if you specify "T" or "Z" (i.e. YYYY-MM-DDTHH:MM:SS),
      // then you probably know what you're doing, so we'll let you go ahead.
      // Issue: http://code.google.com/p/dygraphs/issues/detail?id=255
      if (dateStr.search("-") == -1 ||
          dateStr.search("T") != -1 || dateStr.search("Z") != -1) {
        d = dateStrToMillis(dateStr);
        if (d && !isNaN(d)) return d;
      }

      if (dateStr.search("-") != -1) {  // e.g. '2009-7-12' or '2009-07-12'
        dateStrSlashed = dateStr.replace("-", "/", "g");
        while (dateStrSlashed.search("-") != -1) {
          dateStrSlashed = dateStrSlashed.replace("-", "/");
        }
        d = dateStrToMillis(dateStrSlashed);
      } else {
        // Any format that Date.parse will accept, e.g. "2009/07/12" or
        // "2009/07/12 12:34:56"
        d = dateStrToMillis(dateStr);
      }

      if (!d || isNaN(d)) {
        console.error("Couldn't parse " + dateStr + " as a date");
      }
      return d;
    }

    /**
     * This is identical to JavaScript's built-in Date.parse() method, except that
     * it doesn't get replaced with an incompatible method by aggressive JS
     * libraries like MooTools or Joomla.
     * @param {string} str The date string, e.g. "2011/05/06"
     * @return {number} millis since epoch
     * @private
     */
    function dateStrToMillis(str) {
      return new Date(str).getTime();
    }

    // These functions are all based on MochiKit.
    /**
     * Copies all the properties from o to self.
     *
     * @param {!Object} self
     * @param {!Object} o
     * @return {!Object}
     */
    function update(self, o) {
      if (typeof(o) != 'undefined' && o !== null) {
        for (var k in o) {
          if (o.hasOwnProperty(k)) {
            self[k] = o[k];
          }
        }
      }
      return self;
    }

    // internal: check if o is a DOM node, and we know it’s not null
    var _isNode = (typeof(Node) !== 'undefined' &&
                   Node !== null && typeof(Node) === 'object') ?
      function _isNode(o) {
        return (o instanceof Node);
      } : function _isNode(o) {
        return (typeof(o) === 'object' &&
                typeof(o.nodeType) === 'number' &&
                typeof(o.nodeName) === 'string');
    };

    /**
     * Copies all the properties from o to self.
     *
     * @param {!Object} self
     * @param {!Object} o
     * @return {!Object}
     * @private
     */
    function updateDeep(self, o) {
      if (typeof(o) != 'undefined' && o !== null) {
        for (var k in o) {
          if (o.hasOwnProperty(k)) {
            const v = o[k];
            if (v === null) {
              self[k] = null;
            } else if (isArrayLike(v)) {
              self[k] = v.slice();
            } else if (_isNode(v)) {
              // DOM objects are shallowly-copied.
              self[k] = v;
            } else if (typeof(v) == 'object') {
              if (typeof(self[k]) != 'object' || self[k] === null) {
                self[k] = {};
              }
              updateDeep(self[k], v);
            } else {
              self[k] = v;
            }
          }
        }
      }
      return self;
    }

    /**
     * @param {*} o
     * @return {string}
     * @private
     */
    function typeArrayLike(o) {
      if (o === null)
        return 'null';
      const t = typeof(o);
      if ((t === 'object' ||
           (t === 'function' && typeof(o.item) === 'function')) &&
          typeof(o.length) === 'number' &&
          o.nodeType !== 3 && o.nodeType !== 4)
        return 'array';
      return t;
    }

    /**
     * @param {*} o
     * @return {boolean}
     * @private
     */
    function isArrayLike(o) {
      const t = typeof(o);
      return (o !== null &&
              (t === 'object' ||
               (t === 'function' && typeof(o.item) === 'function')) &&
              typeof(o.length) === 'number' &&
              o.nodeType !== 3 && o.nodeType !== 4);
    }

    /**
     * @param {Object} o
     * @return {boolean}
     * @private
     */
    function isDateLike(o) {
      return (o !== null && typeof(o) === 'object' &&
              typeof(o.getTime) === 'function');
    }

    /**
     * Note: this only seems to work for arrays.
     * @param {!Array} o
     * @return {!Array}
     * @private
     */
    function clone(o) {
      // TODO(danvk): figure out how MochiKit's version works
      var r = [];
      for (var i = 0; i < o.length; i++) {
        if (isArrayLike(o[i])) {
          r.push(clone(o[i]));
        } else {
          r.push(o[i]);
        }
      }
      return r;
    }

    /**
     * Create a new canvas element.
     *
     * @return {!HTMLCanvasElement}
     * @private
     */
    function createCanvas() {
      return document.createElement('canvas');
    }

    /**
     * Returns the context's pixel ratio, which is the ratio between the device
     * pixel ratio and the backing store ratio. Typically this is 1 for conventional
     * displays, and > 1 for HiDPI displays (such as the Retina MBP).
     * See http://www.html5rocks.com/en/tutorials/canvas/hidpi/ for more details.
     *
     * @param {!CanvasRenderingContext2D} context The canvas's 2d context.
     * @return {number} The ratio of the device pixel ratio and the backing store
     * ratio for the specified context.
     */
    function getContextPixelRatio(context) {
      try {
        var devicePixelRatio = window.devicePixelRatio;
        var backingStoreRatio = context.webkitBackingStorePixelRatio ||
                                context.mozBackingStorePixelRatio ||
                                context.msBackingStorePixelRatio ||
                                context.oBackingStorePixelRatio ||
                                context.backingStorePixelRatio || 1;
        if (devicePixelRatio !== undefined) {
          return devicePixelRatio / backingStoreRatio;
        } else {
          // At least devicePixelRatio must be defined for this ratio to make sense.
          // We default backingStoreRatio to 1: this does not exist on some browsers
          // (i.e. desktop Chrome).
          return 1;
        }
      } catch (e) {
        return 1;
      }
    }

    /**
     * TODO(danvk): use @template here when it's better supported for classes.
     * @param {!Array} array
     * @param {number} start
     * @param {number} length
     * @param {function(!Array,?):boolean=} predicate
     * @constructor
     */
    function Iterator(array, start, length, predicate) {
      start = start || 0;
      length = length || array.length;
      this.hasNext = true; // Use to identify if there's another element.
      this.peek = null; // Use for look-ahead
      this.start_ = start;
      this.array_ = array;
      this.predicate_ = predicate;
      this.end_ = Math.min(array.length, start + length);
      this.nextIdx_ = start - 1; // use -1 so initial advance works.
      this.next(); // ignoring result.
    }

    /**
     * @return {Object}
     */
    Iterator.prototype.next = function() {
      if (!this.hasNext) {
        return null;
      }
      var obj = this.peek;

      var nextIdx = this.nextIdx_ + 1;
      var found = false;
      while (nextIdx < this.end_) {
        if (!this.predicate_ || this.predicate_(this.array_, nextIdx)) {
          this.peek = this.array_[nextIdx];
          found = true;
          break;
        }
        nextIdx++;
      }
      this.nextIdx_ = nextIdx;
      if (!found) {
        this.hasNext = false;
        this.peek = null;
      }
      return obj;
    };

    /**
     * Returns a new iterator over array, between indexes start and
     * start + length, and only returns entries that pass the accept function
     *
     * @param {!Array} array the array to iterate over.
     * @param {number} start the first index to iterate over, 0 if absent.
     * @param {number} length the number of elements in the array to iterate over.
     *     This, along with start, defines a slice of the array, and so length
     *     doesn't imply the number of elements in the iterator when accept doesn't
     *     always accept all values. array.length when absent.
     * @param {function(?):boolean=} opt_predicate a function that takes
     *     parameters array and idx, which returns true when the element should be
     *     returned.  If omitted, all elements are accepted.
     * @private
     */
    function createIterator(array, start, length, opt_predicate) {
      return new Iterator(array, start, length, opt_predicate);
    }

    // Shim layer with setTimeout fallback.
    // From: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
    // Should be called with the window context:
    //   Dygraph.requestAnimFrame.call(window, function() {})
    var requestAnimFrame = (function() {
      return window.requestAnimationFrame       ||
              window.webkitRequestAnimationFrame ||
              window.mozRequestAnimationFrame    ||
              window.oRequestAnimationFrame      ||
              window.msRequestAnimationFrame     ||
              function (callback) {
                window.setTimeout(callback, 1000 / 60);
              };
    })();

    /**
     * Call a function at most maxFrames times at an attempted interval of
     * framePeriodInMillis, then call a cleanup function once. repeatFn is called
     * once immediately, then at most (maxFrames - 1) times asynchronously. If
     * maxFrames==1, then cleanup_fn() is also called synchronously.  This function
     * is used to sequence animation.
     * @param {function(number)} repeatFn Called repeatedly -- takes the frame
     *     number (from 0 to maxFrames-1) as an argument.
     * @param {number} maxFrames The max number of times to call repeatFn
     * @param {number} framePeriodInMillis Max requested time between frames.
     * @param {function()} cleanupFn A function to call after all repeatFn calls.
     * @private
     */
    function repeatAndCleanup(repeatFn, maxFrames, framePeriodInMillis,
        cleanupFn) {
      var frameNumber = 0;
      var previousFrameNumber;
      var startTime = new Date().getTime();
      repeatFn(frameNumber);
      if (maxFrames == 1) {
        cleanupFn();
        return;
      }
      var maxFrameArg = maxFrames - 1;

      (function loop() {
        if (frameNumber >= maxFrames) return;
        requestAnimFrame.call(window, function() {
          // Determine which frame to draw based on the delay so far.  Will skip
          // frames if necessary.
          var currentTime = new Date().getTime();
          var delayInMillis = currentTime - startTime;
          previousFrameNumber = frameNumber;
          frameNumber = Math.floor(delayInMillis / framePeriodInMillis);
          var frameDelta = frameNumber - previousFrameNumber;
          // If we predict that the subsequent repeatFn call will overshoot our
          // total frame target, so our last call will cause a stutter, then jump to
          // the last call immediately.  If we're going to cause a stutter, better
          // to do it faster than slower.
          var predictOvershootStutter = (frameNumber + frameDelta) > maxFrameArg;
          if (predictOvershootStutter || (frameNumber >= maxFrameArg)) {
            repeatFn(maxFrameArg);  // Ensure final call with maxFrameArg.
            cleanupFn();
          } else {
            if (frameDelta !== 0) {  // Don't call repeatFn with duplicate frames.
              repeatFn(frameNumber);
            }
            loop();
          }
        });
      })();
    }

    // A whitelist of options that do not change pixel positions.
    var pixelSafeOptions = {
      'annotationClickHandler': true,
      'annotationDblClickHandler': true,
      'annotationMouseOutHandler': true,
      'annotationMouseOverHandler': true,
      'axisLineColor': true,
      'axisLineWidth': true,
      'clickCallback': true,
      'drawCallback': true,
      'drawHighlightPointCallback': true,
      'drawPoints': true,
      'drawPointCallback': true,
      'drawGrid': true,
      'fillAlpha': true,
      'gridLineColor': true,
      'gridLineWidth': true,
      'hideOverlayOnMouseOut': true,
      'highlightCallback': true,
      'highlightCircleSize': true,
      'interactionModel': true,
      'labelsDiv': true,
      'labelsKMB': true,
      'labelsKMG2': true,
      'labelsSeparateLines': true,
      'labelsShowZeroValues': true,
      'legend': true,
      'panEdgeFraction': true,
      'pixelsPerYLabel': true,
      'pointClickCallback': true,
      'pointSize': true,
      'rangeSelectorPlotFillColor': true,
      'rangeSelectorPlotFillGradientColor': true,
      'rangeSelectorPlotStrokeColor': true,
      'rangeSelectorBackgroundStrokeColor': true,
      'rangeSelectorBackgroundLineWidth': true,
      'rangeSelectorPlotLineWidth': true,
      'rangeSelectorForegroundStrokeColor': true,
      'rangeSelectorForegroundLineWidth': true,
      'rangeSelectorAlpha': true,
      'showLabelsOnHighlight': true,
      'showRoller': true,
      'strokeWidth': true,
      'underlayCallback': true,
      'unhighlightCallback': true,
      'zoomCallback': true
    };

    /**
     * This function will scan the option list and determine if they
     * require us to recalculate the pixel positions of each point.
     * TODO: move this into dygraph-options.js
     * @param {!Array.<string>} labels a list of options to check.
     * @param {!Object} attrs
     * @return {boolean} true if the graph needs new points else false.
     * @private
     */
    function isPixelChangingOptionList(labels, attrs) {
      // Assume that we do not require new points.
      // This will change to true if we actually do need new points.

      // Create a dictionary of series names for faster lookup.
      // If there are no labels, then the dictionary stays empty.
      var seriesNamesDictionary = { };
      if (labels) {
        for (var i = 1; i < labels.length; i++) {
          seriesNamesDictionary[labels[i]] = true;
        }
      }

      // Scan through a flat (i.e. non-nested) object of options.
      // Returns true/false depending on whether new points are needed.
      var scanFlatOptions = function(options) {
        for (var property in options) {
          if (options.hasOwnProperty(property) &&
              !pixelSafeOptions[property]) {
            return true;
          }
        }
        return false;
      };

      // Iterate through the list of updated options.
      for (var property in attrs) {
        if (!attrs.hasOwnProperty(property)) continue;

        // Find out of this field is actually a series specific options list.
        if (property == 'highlightSeriesOpts' ||
            (seriesNamesDictionary[property] && !attrs.series)) {
          // This property value is a list of options for this series.
          if (scanFlatOptions(attrs[property])) return true;
        } else if (property == 'series' || property == 'axes') {
          // This is twice-nested options list.
          var perSeries = attrs[property];
          for (var series in perSeries) {
            if (perSeries.hasOwnProperty(series) &&
                scanFlatOptions(perSeries[series])) {
              return true;
            }
          }
        } else {
          // If this was not a series specific option list,
          // check if it's a pixel-changing property.
          if (!pixelSafeOptions[property]) return true;
        }
      }

      return false;
    }

    var Circles = {
      DEFAULT : function(g, name, ctx, canvasx, canvasy, color, radius) {
        ctx.beginPath();
        ctx.fillStyle = color;
        ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false);
        ctx.fill();
      }
      // For more shapes, include extras/shapes.js
    };

    /**
     * Determine whether |data| is delimited by CR, CRLF, LF, LFCR.
     * @param {string} data
     * @return {?string} the delimiter that was detected (or null on failure).
     */
    function detectLineDelimiter(data) {
      for (var i = 0; i < data.length; i++) {
        var code = data.charAt(i);
        if (code === '\r') {
          // Might actually be "\r\n".
          if (((i + 1) < data.length) && (data.charAt(i + 1) === '\n')) {
            return '\r\n';
          }
          return code;
        }
        if (code === '\n') {
          // Might actually be "\n\r".
          if (((i + 1) < data.length) && (data.charAt(i + 1) === '\r')) {
            return '\n\r';
          }
          return code;
        }
      }

      return null;
    }

    /**
     * Is one node contained by another?
     * @param {Node} containee The contained node.
     * @param {Node} container The container node.
     * @return {boolean} Whether containee is inside (or equal to) container.
     * @private
     */
    function isNodeContainedBy(containee, container) {
      if (container === null || containee === null) {
        return false;
      }
      var containeeNode = /** @type {Node} */ (containee);
      while (containeeNode && containeeNode !== container) {
        containeeNode = containeeNode.parentNode;
      }
      return (containeeNode === container);
    }

    // This masks some numeric issues in older versions of Firefox,
    // where 1.0/Math.pow(10,2) != Math.pow(10,-2).
    /** @type {function(number,number):number} */
    function pow(base, exp) {
      if (exp < 0) {
        return 1.0 / Math.pow(base, -exp);
      }
      return Math.pow(base, exp);
    }

    var RGBAxRE = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})?$/;
    var RGBA_RE = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([01](?:\.\d+)?))?\)$/;

    /**
     * Helper for toRGB_ which parses strings of the form:
     * #RRGGBB (hex)
     * #RRGGBBAA (hex)
     * rgb(123, 45, 67)
     * rgba(123, 45, 67, 0.5)
     * @return parsed {r,g,b,a?} tuple or null.
     */
    function parseRGBA(rgbStr) {
      var bits, r, g, b, a = null;
      if ((bits = RGBAxRE.exec(rgbStr))) {
        r = parseInt(bits[1], 16);
        g = parseInt(bits[2], 16);
        b = parseInt(bits[3], 16);
        if (bits[4])
          a = parseInt(bits[4], 16);
      } else if ((bits = RGBA_RE.exec(rgbStr))) {
        r = parseInt(bits[1], 10);
        g = parseInt(bits[2], 10);
        b = parseInt(bits[3], 10);
        if (bits[4])
          a = parseFloat(bits[4]);
      } else
        return null;
      if (a !== null)
        return { "r": r, "g": g, "b": b, "a": a };
      return { "r": r, "g": g, "b": b };
    }

    /**
     * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple.
     *
     * @param {!string} colorStr Any valid CSS color string.
     * @return {{r:number,g:number,b:number,a:number?}} Parsed RGB tuple.
     * @private
     */
    function toRGB_(colorStr) {
      // Strategy: First try to parse colorStr directly. This is fast & avoids DOM
      // manipulation.  If that fails (e.g. for named colors like 'red'), then
      // create a hidden DOM element and parse its computed color.
      var rgb = parseRGBA(colorStr);
      if (rgb) return rgb;

      var div = document.createElement('div');
      div.style.backgroundColor = colorStr;
      div.style.visibility = 'hidden';
      document.body.appendChild(div);
      var rgbStr = window.getComputedStyle(div, null).backgroundColor;
      document.body.removeChild(div);
      return parseRGBA(rgbStr);
    }

    /**
     * Checks whether the browser supports the &lt;canvas&gt; tag.
     * @param {HTMLCanvasElement=} opt_canvasElement Pass a canvas element as an
     *     optimization if you have one.
     * @return {boolean} Whether the browser supports canvas.
     */
    function isCanvasSupported(opt_canvasElement) {
      try {
        var canvas = opt_canvasElement || document.createElement("canvas");
        canvas.getContext("2d");
      } catch (e) {
        return false;
      }
      return true;
    }

    /**
     * Parses the value as a floating point number. This is like the parseFloat()
     * built-in, but with a few differences:
     * - the empty string is parsed as null, rather than NaN.
     * - if the string cannot be parsed at all, an error is logged.
     * If the string can't be parsed, this method returns null.
     * @param {string} x The string to be parsed
     * @param {number=} opt_line_no The line number from which the string comes.
     * @param {string=} opt_line The text of the line from which the string comes.
     */
    function parseFloat_(x, opt_line_no, opt_line) {
      var val = parseFloat(x);
      if (!isNaN(val)) return val;

      // Try to figure out what happeend.
      // If the value is the empty string, parse it as null.
      if (/^ *$/.test(x)) return null;

      // If it was actually "NaN", return it as NaN.
      if (/^ *nan *$/i.test(x)) return NaN;

      // Looks like a parsing error.
      var msg = "Unable to parse '" + x + "' as a number";
      if (opt_line !== undefined && opt_line_no !== undefined) {
        msg += " on line " + (1+(opt_line_no||0)) + " ('" + opt_line + "') of CSV.";
      }
      console.error(msg);

      return null;
    }

    // Label constants for the labelsKMB and labelsKMG2 options.
    // (i.e. '100000' -> '100k')
    var KMB_LABELS_LARGE = [ 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ];
    var KMB_LABELS_SMALL = [ 'm', 'µ', 'n', 'p', 'f', 'a', 'z', 'y' ];
    var KMG2_LABELS_LARGE = [ 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi' ];
    var KMG2_LABELS_SMALL = [ 'p-10', 'p-20', 'p-30', 'p-40', 'p-50', 'p-60', 'p-70', 'p-80' ];
    /* if both are given (legacy/deprecated use only) */
    var KMB2_LABELS_LARGE = [ 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' ];
    var KMB2_LABELS_SMALL = KMB_LABELS_SMALL;

    /**
     * @private
     * Return a string version of a number. This respects the digitsAfterDecimal
     * and maxNumberWidth options.
     * @param {number} x The number to be formatted
     * @param {Dygraph} opts An options view
     */
    function numberValueFormatter(x, opts) {
      var sigFigs = opts('sigFigs');

      if (sigFigs !== null) {
        // User has opted for a fixed number of significant figures.
        return floatFormat(x, sigFigs);
      }

      // shortcut 0 so later code does not need to worry about it
      if (x === 0.0)
        return '0';

      var digits = opts('digitsAfterDecimal');
      var maxNumberWidth = opts('maxNumberWidth');

      var kmb = opts('labelsKMB');
      var kmg2 = opts('labelsKMG2');

      var label;
      var absx = Math.abs(x);

      if (kmb || kmg2) {
        var k;
        var k_labels = [];
        var m_labels = [];
        if (kmb) {
          k = 1000;
          k_labels = KMB_LABELS_LARGE;
          m_labels = KMB_LABELS_SMALL;
        }
        if (kmg2) {
          k = 1024;
          k_labels = KMG2_LABELS_LARGE;
          m_labels = KMG2_LABELS_SMALL;
          if (kmb) {
            k_labels = KMB2_LABELS_LARGE;
            m_labels = KMB2_LABELS_SMALL;
          }
        }

        var n;
        var j;
        if (absx >= k) {
          j = k_labels.length;
          while (j > 0) {
            n = pow(k, j);
            --j;
            if (absx >= n) {
              // guaranteed to hit because absx >= k (pow(k, 1))
              // if immensely large still switch to scientific notation
              if ((absx / n) >= Math.pow(10, maxNumberWidth))
                label = x.toExponential(digits);
              else
                label = round_(x / n, digits) + k_labels[j];
              return label;
            }
          }
          // not reached, fall through safely though should it ever be
        } else if ((absx < 1) /* && (m_labels.length > 0) */) {
          j = 0;
          while (j < m_labels.length) {
            ++j;
            n = pow(k, j);
            if ((absx * n) >= 1)
              break;
          }
          // if _still_ too small, switch to scientific notation instead
          if ((absx * n) < Math.pow(10, -digits))
            label = x.toExponential(digits);
          else
            label = round_(x * n, digits) + m_labels[j - 1];
          return label;
        }
        // else fall through
      }

      if (absx >= Math.pow(10, maxNumberWidth) ||
          absx < Math.pow(10, -digits)) {
        // switch to scientific notation if we underflow or overflow fixed display
        label = x.toExponential(digits);
      } else {
        label = '' + round_(x, digits);
      }

      return label;
    }

    /**
     * variant for use as an axisLabelFormatter.
     * @private
     */
    function numberAxisLabelFormatter(x, granularity, opts) {
      return numberValueFormatter.call(this, x, opts);
    }

    /**
     * @type {!Array.<string>}
     * @private
     * @constant
     */
    var SHORT_MONTH_NAMES_ = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

    /**
     * Convert a JS date to a string appropriate to display on an axis that
     * is displaying values at the stated granularity. This respects the
     * labelsUTC option.
     * @param {Date} date The date to format
     * @param {number} granularity One of the Dygraph granularity constants
     * @param {Dygraph} opts An options view
     * @return {string} The date formatted as local time
     * @private
     */
    function dateAxisLabelFormatter(date, granularity, opts) {
      var utc = opts('labelsUTC');
      var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal;

      var year = accessors.getFullYear(date),
          month = accessors.getMonth(date),
          day = accessors.getDate(date),
          hours = accessors.getHours(date),
          mins = accessors.getMinutes(date),
          secs = accessors.getSeconds(date),
          millis = accessors.getMilliseconds(date);

      if (granularity >= Granularity.DECADAL) {
        return '' + year;
      } else if (granularity >= Granularity.MONTHLY) {
        return SHORT_MONTH_NAMES_[month] + '&#160;' + year;
      } else {
        var frac = hours * 3600 + mins * 60 + secs + 1e-3 * millis;
        if (frac === 0 || granularity >= Granularity.DAILY) {
          // e.g. '21 Jan' (%d%b)
          return zeropad(day) + '&#160;' + SHORT_MONTH_NAMES_[month];
        } else if (granularity < Granularity.SECONDLY) {
          // e.g. 40.310 (meaning 40 seconds and 310 milliseconds)
          var str = "" + millis;
          return zeropad(secs) + "." + ('000'+str).substring(str.length);
        } else if (granularity > Granularity.MINUTELY) {
          return hmsString_(hours, mins, secs, 0);
        } else {
          return hmsString_(hours, mins, secs, millis);
        }
      }
    }

    /**
     * Return a string version of a JS date for a value label. This respects the
     * labelsUTC option.
     * @param {Date} date The date to be formatted
     * @param {Dygraph} opts An options view
     * @private
     */
    function dateValueFormatter(d, opts) {
      return dateString_(d, opts('labelsUTC'));
    }

    // stuff for simple onDOMready implementation
    var deferDOM_callbacks = [];
    var deferDOM_handlerCalled = false;

    // onDOMready once DOM is ready
    /**
     * Simple onDOMready implementation
     * @param {function()} cb The callback to run once the DOM is ready.
     * @return {boolean} whether the DOM is currently ready
     */
    function deferDOM_ready(cb) {
      if (typeof(cb) === "function")
        cb();
      return (true);
    }

    /**
     * Setup a simple onDOMready implementation on the given objct.
     * @param {*} self the object to update .onDOMready on
     * @private
     */
    function setupDOMready_(self) {
      // only attach if there’s a DOM
      if (typeof(document) !== "undefined") {
        // called by browser
        const handler = function deferDOM_handler() {
          /* execute only once */
          if (deferDOM_handlerCalled)
            return;
          deferDOM_handlerCalled = true;
          /* subsequent calls must not enqueue */
          self.onDOMready = deferDOM_ready;
          /* clear event handlers */
          document.removeEventListener("DOMContentLoaded", handler, false);
          window.removeEventListener("load", handler, false);
          /* run user callbacks */
          for (let i = 0; i < deferDOM_callbacks.length; ++i)
            deferDOM_callbacks[i]();
          deferDOM_callbacks = null; //gc
        };

        // make callable (mutating, do not copy)
        self.onDOMready = function deferDOM_initial(cb) {
          /* if possible, skip all that */
          if (document.readyState === "complete") {
            self.onDOMready = deferDOM_ready;
            return (deferDOM_ready(cb));
          }
          // onDOMready, after setup, before DOM is ready
          const enqfn = function deferDOM_enqueue(cb) {
            if (typeof(cb) === "function")
              deferDOM_callbacks.push(cb);
            return (false);
          };
          /* subsequent calls will enqueue */
          self.onDOMready = enqfn;
          /* set up handler */
          document.addEventListener("DOMContentLoaded", handler, false);
          /* last resort: always works, but later than possible */
          window.addEventListener("load", handler, false);
          /* except if DOM got ready in the meantime */
          if (document.readyState === "complete") {
            /* undo all that attaching */
            handler();
            /* goto finish */
            self.onDOMready = deferDOM_ready;
            return (deferDOM_ready(cb));
          }
          /* just enqueue that */
          return (enqfn(cb));
        };
      }
    }

    /**
     * @license
     * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * Creates a new DygraphLayout object.
     *
     * This class contains all the data to be charted.
     * It uses data coordinates, but also records the chart range (in data
     * coordinates) and hence is able to calculate percentage positions ('In this
     * view, Point A lies 25% down the x-axis.')
     *
     * Two things that it does not do are:
     * 1. Record pixel coordinates for anything.
     * 2. (oddly) determine anything about the layout of chart elements.
     *
     * The naming is a vestige of Dygraph's original PlotKit roots.
     *
     * @constructor
     */
    var DygraphLayout = function(dygraph) {
      this.dygraph_ = dygraph;
      /**
       * Array of points for each series.
       *
       * [series index][row index in series] = |Point| structure,
       * where series index refers to visible series only, and the
       * point index is for the reduced set of points for the current
       * zoom region (including one point just outside the window).
       * All points in the same row index share the same X value.
       *
       * @type {Array.<Array.<Dygraph.PointType>>}
       */
      this.points = [];
      this.setNames = [];
      this.annotations = [];
      this.yAxes_ = null;

      // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs,
      // but xticks and yticks are outputs. Clean this up.
      this.xTicks_ = null;
      this.yTicks_ = null;
    };

    /**
     * Add points for a single series.
     *
     * @param {string} setname Name of the series.
     * @param {Array.<Dygraph.PointType>} set_xy Points for the series.
     */
    DygraphLayout.prototype.addDataset = function(setname, set_xy) {
      this.points.push(set_xy);
      this.setNames.push(setname);
    };

    /**
     * Returns the box which the chart should be drawn in. This is the canvas's
     * box, less space needed for the axis and chart labels.
     *
     * @return {{x: number, y: number, w: number, h: number}}
     */
    DygraphLayout.prototype.getPlotArea = function() {
      return this.area_;
    };

    // Compute the box which the chart should be drawn in. This is the canvas's
    // box, less space needed for axis, chart labels, and other plug-ins.
    // NOTE: This should only be called by Dygraph.predraw_().
    DygraphLayout.prototype.computePlotArea = function() {
      var area = {
        // TODO(danvk): per-axis setting.
        x: 0,
        y: 0
      };

      area.w = this.dygraph_.width_ - area.x - this.dygraph_.getOption('rightGap');
      area.h = this.dygraph_.height_;

      // Let plugins reserve space.
      var e = {
        chart_div: this.dygraph_.graphDiv,
        reserveSpaceLeft: function(px) {
          var r = {
            x: area.x,
            y: area.y,
            w: px,
            h: area.h
          };
          area.x += px;
          area.w -= px;
          return r;
        },
        reserveSpaceRight: function(px) {
          var r = {
            x: area.x + area.w - px,
            y: area.y,
            w: px,
            h: area.h
          };
          area.w -= px;
          return r;
        },
        reserveSpaceTop: function(px) {
          var r = {
            x: area.x,
            y: area.y,
            w: area.w,
            h: px
          };
          area.y += px;
          area.h -= px;
          return r;
        },
        reserveSpaceBottom: function(px) {
          var r = {
            x: area.x,
            y: area.y + area.h - px,
            w: area.w,
            h: px
          };
          area.h -= px;
          return r;
        },
        chartRect: function() {
          return {x:area.x, y:area.y, w:area.w, h:area.h};
        }
      };
      this.dygraph_.cascadeEvents_('layout', e);

      this.area_ = area;
    };

    DygraphLayout.prototype.setAnnotations = function(ann) {
      // The Dygraph object's annotations aren't parsed. We parse them here and
      // save a copy. If there is no parser, then the user must be using raw format.
      this.annotations = [];
      var parse = this.dygraph_.getOption('xValueParser') || function(x) { return x; };
      for (var i = 0; i < ann.length; i++) {
        var a = {};
        if (!ann[i].xval && ann[i].x === undefined) {
          console.error("Annotations must have an 'x' property");
          return;
        }
        if (ann[i].icon &&
            !(ann[i].hasOwnProperty('width') &&
              ann[i].hasOwnProperty('height'))) {
          console.error("Must set width and height when setting " +
                        "annotation.icon property");
          return;
        }
        update(a, ann[i]);
        if (!a.xval) a.xval = parse(a.x);
        this.annotations.push(a);
      }
    };

    DygraphLayout.prototype.setXTicks = function(xTicks) {
      this.xTicks_ = xTicks;
    };

    // TODO(danvk): add this to the Dygraph object's API or move it into Layout.
    DygraphLayout.prototype.setYAxes = function (yAxes) {
      this.yAxes_ = yAxes;
    };

    DygraphLayout.prototype.evaluate = function() {
      this._xAxis = {};
      this._evaluateLimits();
      this._evaluateLineCharts();
      this._evaluateLineTicks();
      this._evaluateAnnotations();
    };

    DygraphLayout.prototype._evaluateLimits = function() {
      var xlimits = this.dygraph_.xAxisRange();
      this._xAxis.minval = xlimits[0];
      this._xAxis.maxval = xlimits[1];
      var xrange = xlimits[1] - xlimits[0];
      this._xAxis.scale = (xrange !== 0 ? 1 / xrange : 1.0);

      if (this.dygraph_.getOptionForAxis("logscale", 'x')) {
        this._xAxis.xlogrange = log10(this._xAxis.maxval) - log10(this._xAxis.minval);
        this._xAxis.xlogscale = (this._xAxis.xlogrange !== 0 ? 1.0 / this._xAxis.xlogrange : 1.0);
      }
      for (var i = 0; i < this.yAxes_.length; i++) {
        var axis = this.yAxes_[i];
        axis.minyval = axis.computedValueRange[0];
        axis.maxyval = axis.computedValueRange[1];
        axis.yrange = axis.maxyval - axis.minyval;
        axis.yscale = (axis.yrange !== 0 ? 1.0 / axis.yrange : 1.0);

        if (this.dygraph_.getOption("logscale") || axis.logscale) {
          axis.ylogrange = log10(axis.maxyval) - log10(axis.minyval);
          axis.ylogscale = (axis.ylogrange !== 0 ? 1.0 / axis.ylogrange : 1.0);
          if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) {
            console.error('axis ' + i + ' of graph at ' + axis.g +
                          ' can\'t be displayed in log scale for range [' +
                          axis.minyval + ' - ' + axis.maxyval + ']');
          }
        }
      }
    };

    DygraphLayout.calcXNormal_ = function(value, xAxis, logscale) {
      if (logscale) {
        return ((log10(value) - log10(xAxis.minval)) * xAxis.xlogscale);
      } else {
        return (value - xAxis.minval) * xAxis.scale;
      }
    };

    /**
     * @param {DygraphAxisType} axis
     * @param {number} value
     * @param {boolean} logscale
     * @return {number}
     */
    DygraphLayout.calcYNormal_ = function(axis, value, logscale) {
      if (logscale) {
        var x = 1.0 - ((log10(value) - log10(axis.minyval)) * axis.ylogscale);
        return isFinite(x) ? x : NaN;  // shim for v8 issue; see pull request 276
      } else {
        return 1.0 - ((value - axis.minyval) * axis.yscale);
      }
    };

    DygraphLayout.prototype._evaluateLineCharts = function() {
      var isStacked = this.dygraph_.getOption("stackedGraph");
      var isLogscaleForX = this.dygraph_.getOptionForAxis("logscale", 'x');

      for (var setIdx = 0; setIdx < this.points.length; setIdx++) {
        var points = this.points[setIdx];
        var setName = this.setNames[setIdx];
        var connectSeparated = this.dygraph_.getOption('connectSeparatedPoints', setName);
        var axis = this.dygraph_.axisPropertiesForSeries(setName);
        // TODO (konigsberg): use optionsForAxis instead.
        var logscale = this.dygraph_.attributes_.getForSeries("logscale", setName);

        for (var j = 0; j < points.length; j++) {
          var point = points[j];

          // Range from 0-1 where 0 represents left and 1 represents right.
          point.x = DygraphLayout.calcXNormal_(point.xval, this._xAxis, isLogscaleForX);
          // Range from 0-1 where 0 represents top and 1 represents bottom
          var yval = point.yval;
          if (isStacked) {
            point.y_stacked = DygraphLayout.calcYNormal_(
                axis, point.yval_stacked, logscale);
            if (yval !== null && !isNaN(yval)) {
              yval = point.yval_stacked;
            }
          }
          if (yval === null) {
            yval = NaN;
            if (!connectSeparated) {
              point.yval = NaN;
            }
          }
          point.y = DygraphLayout.calcYNormal_(axis, yval, logscale);
        }

        this.dygraph_.dataHandler_.onLineEvaluated(points, axis, logscale);
      }
    };

    DygraphLayout.prototype._evaluateLineTicks = function() {
      var i, tick, label, pos, v, has_tick;
      this.xticks = [];
      for (i = 0; i < this.xTicks_.length; i++) {
        tick = this.xTicks_[i];
        label = tick.label;
        has_tick = !('label_v' in tick);
        v = has_tick ? tick.v : tick.label_v;
        pos = this.dygraph_.toPercentXCoord(v);
        if ((pos >= 0.0) && (pos < 1.0)) {
          this.xticks.push({pos, label, has_tick});
        }
      }

      this.yticks = [];
      for (i = 0; i < this.yAxes_.length; i++ ) {
        var axis = this.yAxes_[i];
        for (var j = 0; j < axis.ticks.length; j++) {
          tick = axis.ticks[j];
          label = tick.label;
          has_tick = !('label_v' in tick);
          v = has_tick ? tick.v : tick.label_v;
          pos = this.dygraph_.toPercentYCoord(v, i);
          if ((pos > 0.0) && (pos <= 1.0)) {
            this.yticks.push({axis: i, pos, label, has_tick});
          }
        }
      }
    };

    DygraphLayout.prototype._evaluateAnnotations = function() {
      // Add the annotations to the point to which they belong.
      // Make a map from (setName, xval) to annotation for quick lookups.
      var i;
      var annotations = {};
      for (i = 0; i < this.annotations.length; i++) {
        var a = this.annotations[i];
        annotations[a.xval + "," + a.series] = a;
      }

      this.annotated_points = [];

      // Exit the function early if there are no annotations.
      if (!this.annotations || !this.annotations.length) {
        return;
      }

      // TODO(antrob): loop through annotations not points.
      for (var setIdx = 0; setIdx < this.points.length; setIdx++) {
        var points = this.points[setIdx];
        for (i = 0; i < points.length; i++) {
          var p = points[i];
          var k = p.xval + "," + p.name;
          if (k in annotations) {
            p.annotation = annotations[k];
            this.annotated_points.push(p);
            //if there are multiple same x-valued points, the annotation would be rendered multiple times
            //remove already rendered annotation
            delete annotations[k];
          }
        }
      }
    };

    /**
     * Convenience function to remove all the data sets from a graph
     */
    DygraphLayout.prototype.removeAllDatasets = function() {
      delete this.points;
      delete this.setNames;
      delete this.setPointsLengths;
      delete this.setPointsOffsets;
      this.points = [];
      this.setNames = [];
      this.setPointsLengths = [];
      this.setPointsOffsets = [];
    };

    /**
     * @license
     * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * @constructor
     *
     * This gets called when there are "new points" to chart. This is generally the
     * case when the underlying data being charted has changed. It is _not_ called
     * in the common case that the user has zoomed or is panning the view.
     *
     * The chart canvas has already been created by the Dygraph object. The
     * renderer simply gets a drawing context.
     *
     * @param {Dygraph} dygraph The chart to which this renderer belongs.
     * @param {HTMLCanvasElement} element The &lt;canvas&gt; DOM element on which to draw.
     * @param {CanvasRenderingContext2D} elementContext The drawing context.
     * @param {DygraphLayout} layout The chart's DygraphLayout object.
     *
     * TODO(danvk): remove the elementContext property.
     */
    var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) {
      this.dygraph_ = dygraph;

      this.layout = layout;
      this.element = element;
      this.elementContext = elementContext;

      this.height = dygraph.height_;
      this.width = dygraph.width_;

      // --- check whether everything is ok before we return
      if (!isCanvasSupported(this.element)) {
        throw "Canvas is not supported.";
      }

      // internal state
      this.area = layout.getPlotArea();

      // Set up a clipping area for the canvas (and the interaction canvas).
      // This ensures that we don't overdraw.
      var ctx = this.dygraph_.canvas_ctx_;
      ctx.beginPath();
      ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
      ctx.clip();

      ctx = this.dygraph_.hidden_ctx_;
      ctx.beginPath();
      ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
      ctx.clip();
    };

    /**
     * Clears out all chart content and DOM elements.
     * This is called immediately before render() on every frame, including
     * during zooms and pans.
     * @private
     */
    DygraphCanvasRenderer.prototype.clear = function() {
      this.elementContext.clearRect(0, 0, this.width, this.height);
    };

    /**
     * This method is responsible for drawing everything on the chart, including
     * lines, high/low bands, fills and axes.
     * It is called immediately after clear() on every frame, including during pans
     * and zooms.
     * @private
     */
    DygraphCanvasRenderer.prototype.render = function() {
      // attaches point.canvas{x,y}
      this._updatePoints();

      // actually draws the chart.
      this._renderLineChart();
    };

    /**
     * Returns a predicate to be used with an iterator, which will
     * iterate over points appropriately, depending on whether
     * connectSeparatedPoints is true. When it's false, the predicate will
     * skip over points with missing yVals.
     */
    DygraphCanvasRenderer._getIteratorPredicate = function(connectSeparatedPoints) {
      return connectSeparatedPoints ?
          DygraphCanvasRenderer._predicateThatSkipsEmptyPoints :
          null;
    };

    DygraphCanvasRenderer._predicateThatSkipsEmptyPoints =
        function(array, idx) {
      return array[idx].yval !== null;
    };

    /**
     * Draws a line with the styles passed in and calls all the drawPointCallbacks.
     * @param {Object} e The dictionary passed to the plotter function.
     * @private
     */
    DygraphCanvasRenderer._drawStyledLine = function(e,
        color, strokeWidth, strokePattern, drawPoints,
        drawPointCallback, pointSize) {
      var g = e.dygraph;
      // TODO(konigsberg): Compute attributes outside this method call.
      var stepPlot = g.getBooleanOption("stepPlot", e.setName);

      if (!isArrayLike(strokePattern)) {
        strokePattern = null;
      }

      var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName);

      var points = e.points;
      var setName = e.setName;
      var iter = createIterator(points, 0, points.length,
          DygraphCanvasRenderer._getIteratorPredicate(
              g.getBooleanOption("connectSeparatedPoints", setName)));

      var stroking = strokePattern && (strokePattern.length >= 2);

      var ctx = e.drawingContext;
      ctx.save();
      if (stroking) {
        if (ctx.setLineDash) ctx.setLineDash(strokePattern);
      }

      var pointsOnLine = DygraphCanvasRenderer._drawSeries(
          e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);
      DygraphCanvasRenderer._drawPointsOnLine(
          e, pointsOnLine, drawPointCallback, color, pointSize);

      if (stroking) {
        if (ctx.setLineDash) ctx.setLineDash([]);
      }

      ctx.restore();
    };

    /**
     * This does the actual drawing of lines on the canvas, for just one series.
     * Returns a list of [canvasx, canvasy] pairs for points for which a
     * drawPointCallback should be fired.  These include isolated points, or all
     * points if drawPoints=true.
     * @param {Object} e The dictionary passed to the plotter function.
     * @private
     */
    DygraphCanvasRenderer._drawSeries = function(e,
        iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) {

      var prevCanvasX = null;
      var prevCanvasY = null;
      var nextCanvasY = null;
      var isIsolated; // true if this point is isolated (no line segments)
      var point; // the point being processed in the while loop
      var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
      var first = true; // the first cycle through the while loop

      var ctx = e.drawingContext;
      ctx.beginPath();
      ctx.strokeStyle = color;
      ctx.lineWidth = strokeWidth;

      // NOTE: we break the iterator's encapsulation here for about a 25% speedup.
      var arr = iter.array_;
      var limit = iter.end_;
      var predicate = iter.predicate_;

      for (var i = iter.start_; i < limit; i++) {
        point = arr[i];
        if (predicate) {
          while (i < limit && !predicate(arr, i)) {
            i++;
          }
          if (i == limit) break;
          point = arr[i];
        }

        // FIXME: The 'canvasy != canvasy' test here catches NaN values but the test
        // doesn't catch Infinity values. Could change this to
        // !isFinite(point.canvasy), but I assume it avoids isNaN for performance?
        if (point.canvasy === null || point.canvasy != point.canvasy) {
          if (stepPlot && prevCanvasX !== null) {
            // Draw a horizontal line to the start of the missing data
            ctx.moveTo(prevCanvasX, prevCanvasY);
            ctx.lineTo(point.canvasx, prevCanvasY);
          }
          prevCanvasX = prevCanvasY = null;
        } else {
          isIsolated = false;
          if (drawGapPoints || prevCanvasX === null) {
            iter.nextIdx_ = i;
            iter.next();
            nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;

            var isNextCanvasYNullOrNaN = nextCanvasY === null ||
                nextCanvasY != nextCanvasY;
            isIsolated = (prevCanvasX === null && isNextCanvasYNullOrNaN);
            if (drawGapPoints) {
              // Also consider a point to be "isolated" if it's adjacent to a
              // null point, excluding the graph edges.
              if ((!first && prevCanvasX === null) ||
                  (iter.hasNext && isNextCanvasYNullOrNaN)) {
                isIsolated = true;
              }
            }
          }

          if (prevCanvasX !== null) {
            if (strokeWidth) {
              if (stepPlot) {
                ctx.moveTo(prevCanvasX, prevCanvasY);
                ctx.lineTo(point.canvasx, prevCanvasY);
              }

              ctx.lineTo(point.canvasx, point.canvasy);
            }
          } else {
            ctx.moveTo(point.canvasx, point.canvasy);
          }
          if (drawPoints || isIsolated) {
            pointsOnLine.push([point.canvasx, point.canvasy, point.idx]);
          }
          prevCanvasX = point.canvasx;
          prevCanvasY = point.canvasy;
        }
        first = false;
      }
      ctx.stroke();
      return pointsOnLine;
    };

    /**
     * This fires the drawPointCallback functions, which draw dots on the points by
     * default. This gets used when the "drawPoints" option is set, or when there
     * are isolated points.
     * @param {Object} e The dictionary passed to the plotter function.
     * @private
     */
    DygraphCanvasRenderer._drawPointsOnLine = function(
        e, pointsOnLine, drawPointCallback, color, pointSize) {
      var ctx = e.drawingContext;
      for (var idx = 0; idx < pointsOnLine.length; idx++) {
        var cb = pointsOnLine[idx];
        ctx.save();
        drawPointCallback.call(e.dygraph,
            e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]);
        ctx.restore();
      }
    };

    /**
     * Attaches canvas coordinates to the points array.
     * @private
     */
    DygraphCanvasRenderer.prototype._updatePoints = function() {
      // Update Points
      // TODO(danvk): here
      //
      // TODO(bhs): this loop is a hot-spot for high-point-count charts. These
      // transformations can be pushed into the canvas via linear transformation
      // matrices.
      // NOTE(danvk): this is trickier than it sounds at first. The transformation
      // needs to be done before the .moveTo() and .lineTo() calls, but must be
      // undone before the .stroke() call to ensure that the stroke width is
      // unaffected.  An alternative is to reduce the stroke width in the
      // transformed coordinate space, but you can't specify different values for
      // each dimension (as you can with .scale()). The speedup here is ~12%.
      var sets = this.layout.points;
      for (var i = sets.length; i--;) {
        var points = sets[i];
        for (var j = points.length; j--;) {
          var point = points[j];
          point.canvasx = this.area.w * point.x + this.area.x;
          point.canvasy = this.area.h * point.y + this.area.y;
        }
      }
    };

    /**
     * Add canvas Actually draw the lines chart, including high/low bands.
     *
     * This function can only be called if DygraphLayout's points array has been
     * updated with canvas{x,y} attributes, i.e. by
     * DygraphCanvasRenderer._updatePoints.
     *
     * @param {string=} opt_seriesName when specified, only that series will
     *     be drawn. (This is used for expedited redrawing with highlightSeriesOpts)
     * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing
     *     context.  However, lines are typically drawn on the object's
     *     elementContext.
     * @private
     */
    DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ctx) {
      var ctx = opt_ctx || this.elementContext;
      var i;

      var sets = this.layout.points;
      var setNames = this.layout.setNames;
      var setName;

      this.colors = this.dygraph_.colorsMap_;

      // Determine which series have specialized plotters.
      var plotter_attr = this.dygraph_.getOption("plotter");
      var plotters = plotter_attr;
      if (!isArrayLike(plotters)) {
        plotters = [plotters];
      }

      var setPlotters = {};  // series name -> plotter fn.
      for (i = 0; i < setNames.length; i++) {
        setName = setNames[i];
        var setPlotter = this.dygraph_.getOption("plotter", setName);
        if (setPlotter == plotter_attr) continue;  // not specialized.

        setPlotters[setName] = setPlotter;
      }

      for (i = 0; i < plotters.length; i++) {
        var plotter = plotters[i];
        var is_last = (i == plotters.length - 1);

        for (var j = 0; j < sets.length; j++) {
          setName = setNames[j];
          if (opt_seriesName && setName != opt_seriesName) continue;

          var points = sets[j];

          // Only throw in the specialized plotters on the last iteration.
          var p = plotter;
          if (setName in setPlotters) {
            if (is_last) {
              p = setPlotters[setName];
            } else {
              // Don't use the standard plotters in this case.
              continue;
            }
          }

          var color = this.colors[setName];
          var strokeWidth = this.dygraph_.getOption("strokeWidth", setName);

          ctx.save();
          ctx.strokeStyle = color;
          ctx.lineWidth = strokeWidth;
          p({
            points: points,
            setName: setName,
            drawingContext: ctx,
            color: color,
            strokeWidth: strokeWidth,
            dygraph: this.dygraph_,
            axis: this.dygraph_.axisPropertiesForSeries(setName),
            plotArea: this.area,
            seriesIndex: j,
            seriesCount: sets.length,
            singleSeriesName: opt_seriesName,
            allSeriesPoints: sets
          });
          ctx.restore();
        }
      }
    };

    /**
     * Standard plotters. These may be used by clients via Dygraph.Plotters.
     * See comments there for more details.
     */
    DygraphCanvasRenderer._Plotters = {
      linePlotter: function(e) {
        DygraphCanvasRenderer._linePlotter(e);
      },

      fillPlotter: function(e) {
        DygraphCanvasRenderer._fillPlotter(e);
      },

      errorPlotter: function(e) {
        DygraphCanvasRenderer._errorPlotter(e);
      }
    };

    /**
     * Plotter which draws the central lines for a series.
     * @private
     */
    DygraphCanvasRenderer._linePlotter = function(e) {
      var g = e.dygraph;
      var setName = e.setName;
      var strokeWidth = e.strokeWidth;

      // TODO(danvk): Check if there's any performance impact of just calling
      // getOption() inside of _drawStyledLine. Passing in so many parameters makes
      // this code a bit nasty.
      var borderWidth = g.getNumericOption("strokeBorderWidth", setName);
      var drawPointCallback = g.getOption("drawPointCallback", setName) ||
          Circles.DEFAULT;
      var strokePattern = g.getOption("strokePattern", setName);
      var drawPoints = g.getBooleanOption("drawPoints", setName);
      var pointSize = g.getNumericOption("pointSize", setName);

      if (borderWidth && strokeWidth) {
        DygraphCanvasRenderer._drawStyledLine(e,
            g.getOption("strokeBorderColor", setName),
            strokeWidth + 2 * borderWidth,
            strokePattern,
            drawPoints,
            drawPointCallback,
            pointSize
            );
      }

      DygraphCanvasRenderer._drawStyledLine(e,
          e.color,
          strokeWidth,
          strokePattern,
          drawPoints,
          drawPointCallback,
          pointSize
      );
    };

    /**
     * Draws the shaded high/low bands (confidence intervals) for each series.
     * This happens before the center lines are drawn, since the center lines
     * need to be drawn on top of the high/low bands for all series.
     * @private
     */
    DygraphCanvasRenderer._errorPlotter = function(e) {
      var g = e.dygraph;
      var setName = e.setName;
      var errorBars = g.getBooleanOption("errorBars") ||
          g.getBooleanOption("customBars");
      if (!errorBars) return;

      var fillGraph = g.getBooleanOption("fillGraph", setName);
      if (fillGraph) {
        console.warn("Can't use fillGraph option with customBars or errorBars option");
      }

      var ctx = e.drawingContext;
      var color = e.color;
      var fillAlpha = g.getNumericOption('fillAlpha', setName);
      var stepPlot = g.getBooleanOption("stepPlot", setName);
      var points = e.points;

      var iter = createIterator(points, 0, points.length,
          DygraphCanvasRenderer._getIteratorPredicate(
              g.getBooleanOption("connectSeparatedPoints", setName)));

      var newYs;

      // setup graphics context
      var prevX = NaN;
      var prevY = NaN;
      var prevYs = [-1, -1];
      // should be same color as the lines but only 15% opaque.
      var rgb = toRGB_(color);
      var err_color =
          'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
      ctx.fillStyle = err_color;
      ctx.beginPath();

      var isNullUndefinedOrNaN = function(x) {
        return (x === null ||
                x === undefined ||
                isNaN(x));
      };

      while (iter.hasNext) {
        var point = iter.next();
        if ((!stepPlot && isNullUndefinedOrNaN(point.y)) ||
            (stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY))) {
          prevX = NaN;
          continue;
        }

        newYs = [ point.y_bottom, point.y_top ];
        if (stepPlot) {
          prevY = point.y;
        }

        // The documentation specifically disallows nulls inside the point arrays,
        // but in case it happens we should do something sensible.
        if (isNaN(newYs[0])) newYs[0] = point.y;
        if (isNaN(newYs[1])) newYs[1] = point.y;

        newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y;
        newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y;
        if (!isNaN(prevX)) {
          if (stepPlot) {
            ctx.moveTo(prevX, prevYs[0]);
            ctx.lineTo(point.canvasx, prevYs[0]);
            ctx.lineTo(point.canvasx, prevYs[1]);
          } else {
            ctx.moveTo(prevX, prevYs[0]);
            ctx.lineTo(point.canvasx, newYs[0]);
            ctx.lineTo(point.canvasx, newYs[1]);
          }
          ctx.lineTo(prevX, prevYs[1]);
          ctx.closePath();
        }
        prevYs = newYs;
        prevX = point.canvasx;
      }
      ctx.fill();
    };

    /**
     * Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which are
     * superfluous. It accumulates all movements which haven't changed the x-value
     * and only applies the two with the most extreme y-values.
     *
     * Calls to lineTo/moveTo must have non-decreasing x-values.
     */
    DygraphCanvasRenderer._fastCanvasProxy = function(context) {
      var pendingActions = [];  // array of [type, x, y] tuples
      var lastRoundedX = null;
      var lastFlushedX = null;

      var LINE_TO = 1,
          MOVE_TO = 2;

      var actionCount = 0;  // number of moveTos and lineTos passed to context.

      // Drop superfluous motions
      // Assumes all pendingActions have the same (rounded) x-value.
      var compressActions = function(opt_losslessOnly) {
        if (pendingActions.length <= 1) return;

        // Lossless compression: drop inconsequential moveTos.
        for (var i = pendingActions.length - 1; i > 0; i--) {
          var action = pendingActions[i];
          if (action[0] == MOVE_TO) {
            var prevAction = pendingActions[i - 1];
            if (prevAction[1] == action[1] && prevAction[2] == action[2]) {
              pendingActions.splice(i, 1);
            }
          }
        }

        // Lossless compression: ... drop consecutive moveTos ...
        for (var i = 0; i < pendingActions.length - 1; /* incremented internally */) {
          var action = pendingActions[i];
          if (action[0] == MOVE_TO && pendingActions[i + 1][0] == MOVE_TO) {
            pendingActions.splice(i, 1);
          } else {
            i++;
          }
        }

        // Lossy compression: ... drop all but the extreme y-values ...
        if (pendingActions.length > 2 && !opt_losslessOnly) {
          // keep an initial moveTo, but drop all others.
          var startIdx = 0;
          if (pendingActions[0][0] == MOVE_TO) startIdx++;
          var minIdx = null, maxIdx = null;
          for (var i = startIdx; i < pendingActions.length; i++) {
            var action = pendingActions[i];
            if (action[0] != LINE_TO) continue;
            if (minIdx === null && maxIdx === null) {
              minIdx = i;
              maxIdx = i;
            } else {
              var y = action[2];
              if (y < pendingActions[minIdx][2]) {
                minIdx = i;
              } else if (y > pendingActions[maxIdx][2]) {
                maxIdx = i;
              }
            }
          }
          var minAction = pendingActions[minIdx],
              maxAction = pendingActions[maxIdx];
          pendingActions.splice(startIdx, pendingActions.length - startIdx);
          if (minIdx < maxIdx) {
            pendingActions.push(minAction);
            pendingActions.push(maxAction);
          } else if (minIdx > maxIdx) {
            pendingActions.push(maxAction);
            pendingActions.push(minAction);
          } else {
            pendingActions.push(minAction);
          }
        }
      };

      var flushActions = function(opt_noLossyCompression) {
        compressActions(opt_noLossyCompression);
        for (var i = 0, len = pendingActions.length; i < len; i++) {
          var action = pendingActions[i];
          if (action[0] == LINE_TO) {
            context.lineTo(action[1], action[2]);
          } else if (action[0] == MOVE_TO) {
            context.moveTo(action[1], action[2]);
          }
        }
        if (pendingActions.length) {
          lastFlushedX = pendingActions[pendingActions.length - 1][1];
        }
        actionCount += pendingActions.length;
        pendingActions = [];
      };

      var addAction = function(action, x, y) {
        var rx = Math.round(x);
        if (lastRoundedX === null || rx != lastRoundedX) {
          // if there are large gaps on the x-axis, it's essential to keep the
          // first and last point as well.
          var hasGapOnLeft = (lastRoundedX - lastFlushedX > 1),
              hasGapOnRight = (rx - lastRoundedX > 1),
              hasGap = hasGapOnLeft || hasGapOnRight;
          flushActions(hasGap);
          lastRoundedX = rx;
        }
        pendingActions.push([action, x, y]);
      };

      return {
        moveTo: function(x, y) {
          addAction(MOVE_TO, x, y);
        },
        lineTo: function(x, y) {
          addAction(LINE_TO, x, y);
        },

        // for major operations like stroke/fill, we skip compression to ensure
        // that there are no artifacts at the right edge.
        stroke:    function() { flushActions(true); context.stroke(); },
        fill:      function() { flushActions(true); context.fill(); },
        beginPath: function() { flushActions(true); context.beginPath(); },
        closePath: function() { flushActions(true); context.closePath(); },

        _count: function() { return actionCount; }
      };
    };

    /**
     * Draws the shaded regions when "fillGraph" is set.
     * Not to be confused with high/low bands (historically misnamed errorBars).
     *
     * For stacked charts, it's more convenient to handle all the series
     * simultaneously. So this plotter plots all the points on the first series
     * it's asked to draw, then ignores all the other series.
     *
     * @private
     */
    DygraphCanvasRenderer._fillPlotter = function(e) {
      // Skip if we're drawing a single series for interactive highlight overlay.
      if (e.singleSeriesName) return;

      // We'll handle all the series at once, not one-by-one.
      if (e.seriesIndex !== 0) return;

      var g = e.dygraph;
      var setNames = g.getLabels().slice(1);  // remove x-axis

      // getLabels() includes names for invisible series, which are not included in
      // allSeriesPoints. We remove those to make the two match.
      // TODO(danvk): provide a simpler way to get this information.
      for (var i = setNames.length; i >= 0; i--) {
        if (!g.visibility()[i]) setNames.splice(i, 1);
      }

      var anySeriesFilled = (function() {
        for (var i = 0; i < setNames.length; i++) {
          if (g.getBooleanOption("fillGraph", setNames[i])) return true;
        }
        return false;
      })();

      if (!anySeriesFilled) return;

      var area = e.plotArea;
      var sets = e.allSeriesPoints;
      var setCount = sets.length;

      var stackedGraph = g.getBooleanOption("stackedGraph");
      var colors = g.getColors();

      // For stacked graphs, track the baseline for filling.
      //
      // The filled areas below graph lines are trapezoids with two
      // vertical edges. The top edge is the line segment being drawn, and
      // the baseline is the bottom edge. Each baseline corresponds to the
      // top line segment from the previous stacked line. In the case of
      // step plots, the trapezoids are rectangles.
      var baseline = {};
      var currBaseline;
      var prevStepPlot;  // for different line drawing modes (line/step) per series

      // Helper function to trace a line back along the baseline.
      var traceBackPath = function(ctx, baselineX, baselineY, pathBack) {
        ctx.lineTo(baselineX, baselineY);
        if (stackedGraph) {
          for (var i = pathBack.length - 1; i >= 0; i--) {
            var pt = pathBack[i];
            ctx.lineTo(pt[0], pt[1]);
          }
        }
      };

      // process sets in reverse order (needed for stacked graphs)
      for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
        var ctx = e.drawingContext;
        var setName = setNames[setIdx];
        if (!g.getBooleanOption('fillGraph', setName)) continue;

        var fillAlpha = g.getNumericOption('fillAlpha', setName);
        var stepPlot = g.getBooleanOption('stepPlot', setName);
        var color = colors[setIdx];
        var axis = g.axisPropertiesForSeries(setName);
        var axisY = 1.0 + axis.minyval * axis.yscale;
        if (axisY < 0.0) axisY = 0.0;
        else if (axisY > 1.0) axisY = 1.0;
        axisY = area.h * axisY + area.y;

        var points = sets[setIdx];
        var iter = createIterator(points, 0, points.length,
            DygraphCanvasRenderer._getIteratorPredicate(
                g.getBooleanOption("connectSeparatedPoints", setName)));

        // setup graphics context
        var prevX = NaN;
        var prevYs = [-1, -1];
        var newYs;
        // should be same color as the lines but only 15% opaque.
        var rgb = toRGB_(color);
        var err_color =
            'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
        ctx.fillStyle = err_color;
        ctx.beginPath();
        var last_x, is_first = true;

        // If the point density is high enough, dropping segments on their way to
        // the canvas justifies the overhead of doing so.
        if (points.length > 2 * g.width_ || Dygraph.FORCE_FAST_PROXY) {
          ctx = DygraphCanvasRenderer._fastCanvasProxy(ctx);
        }

        // For filled charts, we draw points from left to right, then back along
        // the x-axis to complete a shape for filling.
        // For stacked plots, this "back path" is a more complex shape. This array
        // stores the [x, y] values needed to trace that shape.
        var pathBack = [];

        // TODO(danvk): there are a lot of options at play in this loop.
        //     The logic would be much clearer if some (e.g. stackGraph and
        //     stepPlot) were split off into separate sub-plotters.
        var point;
        while (iter.hasNext) {
          point = iter.next();
          if (!isOK(point.y) && !stepPlot) {
            traceBackPath(ctx, prevX, prevYs[1], pathBack);
            pathBack = [];
            prevX = NaN;
            if (point.y_stacked !== null && !isNaN(point.y_stacked)) {
              baseline[point.canvasx] = area.h * point.y_stacked + area.y;
            }
            continue;
          }
          if (stackedGraph) {
            if (!is_first && last_x == point.xval) {
              continue;
            } else {
              is_first = false;
              last_x = point.xval;
            }

            currBaseline = baseline[point.canvasx];
            var lastY;
            if (currBaseline === undefined) {
              lastY = axisY;
            } else {
              if(prevStepPlot) {
                lastY = currBaseline[0];
              } else {
                lastY = currBaseline;
              }
            }
            newYs = [ point.canvasy, lastY ];

            if (stepPlot) {
              // Step plots must keep track of the top and bottom of
              // the baseline at each point.
              if (prevYs[0] === -1) {
                baseline[point.canvasx] = [ point.canvasy, axisY ];
              } else {
                baseline[point.canvasx] = [ point.canvasy, prevYs[0] ];
              }
            } else {
              baseline[point.canvasx] = point.canvasy;
            }

          } else {
            if (isNaN(point.canvasy) && stepPlot) {
              newYs = [ area.y + area.h, axisY ];
            } else {
              newYs = [ point.canvasy, axisY ];
            }
          }
          if (!isNaN(prevX)) {
            // Move to top fill point
            if (stepPlot) {
              ctx.lineTo(point.canvasx, prevYs[0]);
              ctx.lineTo(point.canvasx, newYs[0]);
            } else {
              ctx.lineTo(point.canvasx, newYs[0]);
            }

            // Record the baseline for the reverse path.
            if (stackedGraph) {
              pathBack.push([prevX, prevYs[1]]);
              if (prevStepPlot && currBaseline) {
                // Draw to the bottom of the baseline
                pathBack.push([point.canvasx, currBaseline[1]]);
              } else {
                pathBack.push([point.canvasx, newYs[1]]);
              }
            }
          } else {
            ctx.moveTo(point.canvasx, newYs[1]);
            ctx.lineTo(point.canvasx, newYs[0]);
          }
          prevYs = newYs;
          prevX = point.canvasx;
        }
        prevStepPlot = stepPlot;
        if (newYs && point) {
          traceBackPath(ctx, point.canvasx, newYs[1], pathBack);
          pathBack = [];
        }
        ctx.fill();
      }
    };

    /**
     * @license
     * Copyright 2011 Robert Konigsberg (konigsberg@google.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * You can drag this many pixels past the edge of the chart and still have it
     * be considered a zoom. This makes it easier to zoom to the exact edge of the
     * chart, a fairly common operation.
     */
    var DRAG_EDGE_MARGIN = 100;

    /**
     * A collection of functions to facilitate build custom interaction models.
     * @class
     */
    var DygraphInteraction = {};

    /**
     * Checks whether the beginning & ending of an event were close enough that it
     * should be considered a click. If it should, dispatch appropriate events.
     * Returns true if the event was treated as a click.
     *
     * @param {Event} event
     * @param {Dygraph} g
     * @param {Object} context
     */
    DygraphInteraction.maybeTreatMouseOpAsClick = function(event, g, context) {
      context.dragEndX = dragGetX_(event, context);
      context.dragEndY = dragGetY_(event, context);
      var regionWidth = Math.abs(context.dragEndX - context.dragStartX);
      var regionHeight = Math.abs(context.dragEndY - context.dragStartY);

      if (regionWidth < 2 && regionHeight < 2 &&
          g.lastx_ !== undefined && g.lastx_ !== null) {
        DygraphInteraction.treatMouseOpAsClick(g, event, context);
      }

      context.regionWidth = regionWidth;
      context.regionHeight = regionHeight;
    };

    /**
     * Called in response to an interaction model operation that
     * should start the default panning behavior.
     *
     * It's used in the default callback for "mousedown" operations.
     * Custom interaction model builders can use it to provide the default
     * panning behavior.
     *
     * @param {Event} event the event object which led to the startPan call.
     * @param {Dygraph} g The dygraph on which to act.
     * @param {Object} context The dragging context object (with
     *     dragStartX/dragStartY/etc. properties). This function modifies the
     *     context.
     */
    DygraphInteraction.startPan = function(event, g, context) {
      var i, axis;
      context.isPanning = true;
      var xRange = g.xAxisRange();

      if (g.getOptionForAxis("logscale", "x")) {
        context.initialLeftmostDate = log10(xRange[0]);
        context.dateRange = log10(xRange[1]) - log10(xRange[0]);
      } else {
        context.initialLeftmostDate = xRange[0];
        context.dateRange = xRange[1] - xRange[0];
      }
      context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1);

      if (g.getNumericOption("panEdgeFraction")) {
        var maxXPixelsToDraw = g.width_ * g.getNumericOption("panEdgeFraction");
        var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes!

        var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw;
        var boundedRightX = g.toDomXCoord(xExtremes[1]) + maxXPixelsToDraw;

        var boundedLeftDate = g.toDataXCoord(boundedLeftX);
        var boundedRightDate = g.toDataXCoord(boundedRightX);
        context.boundedDates = [boundedLeftDate, boundedRightDate];

        var boundedValues = [];
        var maxYPixelsToDraw = g.height_ * g.getNumericOption("panEdgeFraction");

        for (i = 0; i < g.axes_.length; i++) {
          axis = g.axes_[i];
          var yExtremes = axis.extremeRange;

          var boundedTopY = g.toDomYCoord(yExtremes[0], i) + maxYPixelsToDraw;
          var boundedBottomY = g.toDomYCoord(yExtremes[1], i) - maxYPixelsToDraw;

          var boundedTopValue = g.toDataYCoord(boundedTopY, i);
          var boundedBottomValue = g.toDataYCoord(boundedBottomY, i);

          boundedValues[i] = [boundedTopValue, boundedBottomValue];
        }
        context.boundedValues = boundedValues;
      } else {
        // undo effect if it was once set
        context.boundedDates = null;
        context.boundedValues = null;
      }

      // Record the range of each y-axis at the start of the drag.
      // If any axis has a valueRange, then we want a 2D pan.
      // We can't store data directly in g.axes_, because it does not belong to us
      // and could change out from under us during a pan (say if there's a data
      // update).
      context.is2DPan = false;
      context.axes = [];
      for (i = 0; i < g.axes_.length; i++) {
        axis = g.axes_[i];
        var axis_data = {};
        var yRange = g.yAxisRange(i);
        // TODO(konigsberg): These values should be in |context|.
        // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale.
        var logscale = g.attributes_.getForAxis("logscale", i);
        if (logscale) {
          axis_data.initialTopValue = log10(yRange[1]);
          axis_data.dragValueRange = log10(yRange[1]) - log10(yRange[0]);
        } else {
          axis_data.initialTopValue = yRange[1];
          axis_data.dragValueRange = yRange[1] - yRange[0];
        }
        axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1);
        context.axes.push(axis_data);

        // While calculating axes, set 2dpan.
        if (axis.valueRange) context.is2DPan = true;
      }
    };

    /**
     * Called in response to an interaction model operation that
     * responds to an event that pans the view.
     *
     * It's used in the default callback for "mousemove" operations.
     * Custom interaction model builders can use it to provide the default
     * panning behavior.
     *
     * @param {Event} event the event object which led to the movePan call.
     * @param {Dygraph} g The dygraph on which to act.
     * @param {Object} context The dragging context object (with
     *     dragStartX/dragStartY/etc. properties). This function modifies the
     *     context.
     */
    DygraphInteraction.movePan = function(event, g, context) {
      context.dragEndX = dragGetX_(event, context);
      context.dragEndY = dragGetY_(event, context);

      var minDate = context.initialLeftmostDate -
        (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel;
      if (context.boundedDates) {
        minDate = Math.max(minDate, context.boundedDates[0]);
      }
      var maxDate = minDate + context.dateRange;
      if (context.boundedDates) {
        if (maxDate > context.boundedDates[1]) {
          // Adjust minDate, and recompute maxDate.
          minDate = minDate - (maxDate - context.boundedDates[1]);
          maxDate = minDate + context.dateRange;
        }
      }

      if (g.getOptionForAxis("logscale", "x")) {
        g.dateWindow_ = [ Math.pow(LOG_SCALE, minDate),
                          Math.pow(LOG_SCALE, maxDate) ];
      } else {
        g.dateWindow_ = [minDate, maxDate];
      }

      // y-axis scaling is automatic unless this is a full 2D pan.
      if (context.is2DPan) {

        var pixelsDragged = context.dragEndY - context.dragStartY;

        // Adjust each axis appropriately.
        for (var i = 0; i < g.axes_.length; i++) {
          var axis = g.axes_[i];
          var axis_data = context.axes[i];
          var unitsDragged = pixelsDragged * axis_data.unitsPerPixel;

          var boundedValue = context.boundedValues ? context.boundedValues[i] : null;

          // In log scale, maxValue and minValue are the logs of those values.
          var maxValue = axis_data.initialTopValue + unitsDragged;
          if (boundedValue) {
            maxValue = Math.min(maxValue, boundedValue[1]);
          }
          var minValue = maxValue - axis_data.dragValueRange;
          if (boundedValue) {
            if (minValue < boundedValue[0]) {
              // Adjust maxValue, and recompute minValue.
              maxValue = maxValue - (minValue - boundedValue[0]);
              minValue = maxValue - axis_data.dragValueRange;
            }
          }
          if (g.attributes_.getForAxis("logscale", i)) {
            axis.valueRange = [ Math.pow(LOG_SCALE, minValue),
                                Math.pow(LOG_SCALE, maxValue) ];
          } else {
            axis.valueRange = [ minValue, maxValue ];
          }
        }
      }

      g.drawGraph_(false);
    };

    /**
     * Called in response to an interaction model operation that
     * responds to an event that ends panning.
     *
     * It's used in the default callback for "mouseup" operations.
     * Custom interaction model builders can use it to provide the default
     * panning behavior.
     *
     * @param {Event} event the event object which led to the endPan call.
     * @param {Dygraph} g The dygraph on which to act.
     * @param {Object} context The dragging context object (with
     *     dragStartX/dragStartY/etc. properties). This function modifies the
     *     context.
     */
    DygraphInteraction.endPan = DygraphInteraction.maybeTreatMouseOpAsClick;

    /**
     * Called in response to an interaction model operation that
     * responds to an event that starts zooming.
     *
     * It's used in the default callback for "mousedown" operations.
     * Custom interaction model builders can use it to provide the default
     * zooming behavior.
     *
     * @param {Event} event the event object which led to the startZoom call.
     * @param {Dygraph} g The dygraph on which to act.
     * @param {Object} context The dragging context object (with
     *     dragStartX/dragStartY/etc. properties). This function modifies the
     *     context.
     */
    DygraphInteraction.startZoom = function(event, g, context) {
      context.isZooming = true;
      context.zoomMoved = false;
    };

    /**
     * Called in response to an interaction model operation that
     * responds to an event that defines zoom boundaries.
     *
     * It's used in the default callback for "mousemove" operations.
     * Custom interaction model builders can use it to provide the default
     * zooming behavior.
     *
     * @param {Event} event the event object which led to the moveZoom call.
     * @param {Dygraph} g The dygraph on which to act.
     * @param {Object} context The dragging context object (with
     *     dragStartX/dragStartY/etc. properties). This function modifies the
     *     context.
     */
    DygraphInteraction.moveZoom = function(event, g, context) {
      context.zoomMoved = true;
      context.dragEndX = dragGetX_(event, context);
      context.dragEndY = dragGetY_(event, context);

      var xDelta = Math.abs(context.dragStartX - context.dragEndX);
      var yDelta = Math.abs(context.dragStartY - context.dragEndY);

      // drag direction threshold for y axis is twice as large as x axis
      context.dragDirection = (xDelta < yDelta / 2) ? VERTICAL : HORIZONTAL;

      g.drawZoomRect_(
          context.dragDirection,
          context.dragStartX,
          context.dragEndX,
          context.dragStartY,
          context.dragEndY,
          context.prevDragDirection,
          context.prevEndX,
          context.prevEndY);

      context.prevEndX = context.dragEndX;
      context.prevEndY = context.dragEndY;
      context.prevDragDirection = context.dragDirection;
    };

    /**
     * TODO(danvk): move this logic into dygraph.js
     * @param {Dygraph} g
     * @param {Event} event
     * @param {Object} context
     */
    DygraphInteraction.treatMouseOpAsClick = function(g, event, context) {
      var clickCallback = g.getFunctionOption('clickCallback');
      var pointClickCallback = g.getFunctionOption('pointClickCallback');

      var selectedPoint = null;

      // Find out if the click occurs on a point.
      var closestIdx = -1;
      var closestDistance = Number.MAX_VALUE;

      // check if the click was on a particular point.
      for (var i = 0; i < g.selPoints_.length; i++) {
        var p = g.selPoints_[i];
        var distance = Math.pow(p.canvasx - context.dragEndX, 2) +
                       Math.pow(p.canvasy - context.dragEndY, 2);
        if (!isNaN(distance) &&
            (closestIdx == -1 || distance < closestDistance)) {
          closestDistance = distance;
          closestIdx = i;
        }
      }

      // Allow any click within two pixels of the dot.
      var radius = g.getNumericOption('highlightCircleSize') + 2;
      if (closestDistance <= radius * radius) {
        selectedPoint = g.selPoints_[closestIdx];
      }

      if (selectedPoint) {
        var e = {
          cancelable: true,
          point: selectedPoint,
          canvasx: context.dragEndX,
          canvasy: context.dragEndY
        };
        var defaultPrevented = g.cascadeEvents_('pointClick', e);
        if (defaultPrevented) {
          // Note: this also prevents click / clickCallback from firing.
          return;
        }
        if (pointClickCallback) {
          pointClickCallback.call(g, event, selectedPoint);
        }
      }

      var e = {
        cancelable: true,
        xval: g.lastx_,  // closest point by x value
        pts: g.selPoints_,
        canvasx: context.dragEndX,
        canvasy: context.dragEndY
      };
      if (!g.cascadeEvents_('click', e)) {
        if (clickCallback) {
          // TODO(danvk): pass along more info about the points, e.g. 'x'
          clickCallback.call(g, event, g.lastx_, g.selPoints_);
        }
      }
    };

    /**
     * Called in response to an interaction model operation that
     * responds to an event that performs a zoom based on previously defined
     * bounds..
     *
     * It's used in the default callback for "mouseup" operations.
     * Custom interaction model builders can use it to provide the default
     * zooming behavior.
     *
     * @param {Event} event the event object which led to the endZoom call.
     * @param {Dygraph} g The dygraph on which to end the zoom.
     * @param {Object} context The dragging context object (with
     *     dragStartX/dragStartY/etc. properties). This function modifies the
     *     context.
     */
    DygraphInteraction.endZoom = function(event, g, context) {
      g.clearZoomRect_();
      context.isZooming = false;
      DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);

      // The zoom rectangle is visibly clipped to the plot area, so its behavior
      // should be as well.
      // See http://code.google.com/p/dygraphs/issues/detail?id=280
      var plotArea = g.getArea();
      if (context.regionWidth >= 10 &&
          context.dragDirection == HORIZONTAL) {
        var left = Math.min(context.dragStartX, context.dragEndX),
            right = Math.max(context.dragStartX, context.dragEndX);
        left = Math.max(left, plotArea.x);
        right = Math.min(right, plotArea.x + plotArea.w);
        if (left < right) {
          g.doZoomX_(left, right);
        }
        context.cancelNextDblclick = true;
      } else if (context.regionHeight >= 10 &&
                 context.dragDirection == VERTICAL) {
        var top = Math.min(context.dragStartY, context.dragEndY),
            bottom = Math.max(context.dragStartY, context.dragEndY);
        top = Math.max(top, plotArea.y);
        bottom = Math.min(bottom, plotArea.y + plotArea.h);
        if (top < bottom) {
          g.doZoomY_(top, bottom);
        }
        context.cancelNextDblclick = true;
      }
      context.dragStartX = null;
      context.dragStartY = null;
    };

    /**
     * @private
     */
    DygraphInteraction.startTouch = function(event, g, context) {
      event.preventDefault();  // touch browsers are all nice.
      if (event.touches.length > 1) {
        // If the user ever puts two fingers down, it's not a double tap.
        context.startTimeForDoubleTapMs = null;
      }

      var touches = [];
      for (var i = 0; i < event.touches.length; i++) {
        var t = event.touches[i];
        var rect = t.target.getBoundingClientRect();
        // we dispense with 'dragGetX_' because all touchBrowsers support pageX
        touches.push({
          pageX: t.pageX,
          pageY: t.pageY,
          dataX: g.toDataXCoord(t.clientX - rect.left),
          dataY: g.toDataYCoord(t.clientY - rect.top)
          // identifier: t.identifier
        });
      }
      context.initialTouches = touches;

      if (touches.length == 1) {
        // This is just a swipe.
        context.initialPinchCenter = touches[0];
        context.touchDirections = { x: true, y: true };
      } else if (touches.length >= 2) {
        // It's become a pinch!
        // In case there are 3+ touches, we ignore all but the "first" two.

        // only screen coordinates can be averaged (data coords could be log scale).
        context.initialPinchCenter = {
          pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
          pageY: 0.5 * (touches[0].pageY + touches[1].pageY),

          // TODO(danvk): remove
          dataX: 0.5 * (touches[0].dataX + touches[1].dataX),
          dataY: 0.5 * (touches[0].dataY + touches[1].dataY)
        };

        // Make pinches in a 45-degree swath around either axis 1-dimensional zooms.
        var initialAngle = 180 / Math.PI * Math.atan2(
            context.initialPinchCenter.pageY - touches[0].pageY,
            touches[0].pageX - context.initialPinchCenter.pageX);

        // use symmetry to get it into the first quadrant.
        initialAngle = Math.abs(initialAngle);
        if (initialAngle > 90) initialAngle = 90 - initialAngle;

        context.touchDirections = {
          x: (initialAngle < (90 - 45/2)),
          y: (initialAngle > 45/2)
        };
      }

      // save the full x & y ranges.
      context.initialRange = {
        x: g.xAxisRange(),
        y: g.yAxisRange()
      };
    };

    /**
     * @private
     */
    DygraphInteraction.moveTouch = function(event, g, context) {
      // If the tap moves, then it's definitely not part of a double-tap.
      context.startTimeForDoubleTapMs = null;

      var i, touches = [];
      for (i = 0; i < event.touches.length; i++) {
        var t = event.touches[i];
        touches.push({
          pageX: t.pageX,
          pageY: t.pageY
        });
      }
      var initialTouches = context.initialTouches;

      var c_now;

      // old and new centers.
      var c_init = context.initialPinchCenter;
      if (touches.length == 1) {
        c_now = touches[0];
      } else {
        c_now = {
          pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
          pageY: 0.5 * (touches[0].pageY + touches[1].pageY)
        };
      }

      // this is the "swipe" component
      // we toss it out for now, but could use it in the future.
      var swipe = {
        pageX: c_now.pageX - c_init.pageX,
        pageY: c_now.pageY - c_init.pageY
      };
      var dataWidth = context.initialRange.x[1] - context.initialRange.x[0];
      var dataHeight = context.initialRange.y[0] - context.initialRange.y[1];
      swipe.dataX = (swipe.pageX / g.plotter_.area.w) * dataWidth;
      swipe.dataY = (swipe.pageY / g.plotter_.area.h) * dataHeight;
      var xScale, yScale;

      // The residual bits are usually split into scale & rotate bits, but we split
      // them into x-scale and y-scale bits.
      if (touches.length == 1) {
        xScale = 1.0;
        yScale = 1.0;
      } else if (touches.length >= 2) {
        var initHalfWidth = (initialTouches[1].pageX - c_init.pageX);
        xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth;

        var initHalfHeight = (initialTouches[1].pageY - c_init.pageY);
        yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight;
      }

      // Clip scaling to [1/8, 8] to prevent too much blowup.
      xScale = Math.min(8, Math.max(0.125, xScale));
      yScale = Math.min(8, Math.max(0.125, yScale));

      var didZoom = false;
      if (context.touchDirections.x) {
        var cFactor = c_init.dataX - swipe.dataX / xScale;
        g.dateWindow_ = [
          cFactor + (context.initialRange.x[0] - c_init.dataX) / xScale,
          cFactor + (context.initialRange.x[1] - c_init.dataX) / xScale
        ];
        didZoom = true;
      }

      if (context.touchDirections.y) {
        for (i = 0; i < 1  /*g.axes_.length*/; i++) {
          var axis = g.axes_[i];
          var logscale = g.attributes_.getForAxis("logscale", i);
          if (logscale) ; else {
            var cFactor = c_init.dataY - swipe.dataY / yScale;
            axis.valueRange = [
              cFactor + (context.initialRange.y[0] - c_init.dataY) / yScale,
              cFactor + (context.initialRange.y[1] - c_init.dataY) / yScale
            ];
            didZoom = true;
          }
        }
      }

      g.drawGraph_(false);

      // We only call zoomCallback on zooms, not pans, to mirror desktop behavior.
      if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) {
        var viewWindow = g.xAxisRange();
        g.getFunctionOption("zoomCallback").call(g, viewWindow[0], viewWindow[1], g.yAxisRanges());
      }
    };

    /**
     * @private
     */
    DygraphInteraction.endTouch = function(event, g, context) {
      if (event.touches.length !== 0) {
        // this is effectively a "reset"
        DygraphInteraction.startTouch(event, g, context);
      } else if (event.changedTouches.length == 1) {
        // Could be part of a "double tap"
        // The heuristic here is that it's a double-tap if the two touchend events
        // occur within 500ms and within a 50x50 pixel box.
        var now = new Date().getTime();
        var t = event.changedTouches[0];
        if (context.startTimeForDoubleTapMs &&
            now - context.startTimeForDoubleTapMs < 500 &&
            context.doubleTapX && Math.abs(context.doubleTapX - t.screenX) < 50 &&
            context.doubleTapY && Math.abs(context.doubleTapY - t.screenY) < 50) {
          g.resetZoom();
        } else {
          context.startTimeForDoubleTapMs = now;
          context.doubleTapX = t.screenX;
          context.doubleTapY = t.screenY;
        }
      }
    };

    // Determine the distance from x to [left, right].
    var distanceFromInterval = function(x, left, right) {
      if (x < left) {
        return left - x;
      } else if (x > right) {
        return x - right;
      } else {
        return 0;
      }
    };

    /**
     * Returns the number of pixels by which the event happens from the nearest
     * edge of the chart. For events in the interior of the chart, this returns zero.
     */
    var distanceFromChart = function(event, g) {
      var chartPos = findPos(g.canvas_);
      var box = {
        left: chartPos.x,
        right: chartPos.x + g.canvas_.offsetWidth,
        top: chartPos.y,
        bottom: chartPos.y + g.canvas_.offsetHeight
      };

      var pt = {
        x: pageX(event),
        y: pageY(event)
      };

      var dx = distanceFromInterval(pt.x, box.left, box.right),
          dy = distanceFromInterval(pt.y, box.top, box.bottom);
      return Math.max(dx, dy);
    };

    /**
     * Default interation model for dygraphs. You can refer to specific elements of
     * this when constructing your own interaction model, e.g.:
     * g.updateOptions( {
     *   interactionModel: {
     *     mousedown: DygraphInteraction.defaultInteractionModel.mousedown
     *   }
     * } );
     */
    DygraphInteraction.defaultModel = {
      // Track the beginning of drag events
      mousedown: function(event, g, context) {
        // Right-click should not initiate a zoom.
        if (event.button && event.button == 2) return;

        context.initializeMouseDown(event, g, context);

        if (event.altKey || event.shiftKey) {
          DygraphInteraction.startPan(event, g, context);
        } else {
          DygraphInteraction.startZoom(event, g, context);
        }

        // Note: we register mousemove/mouseup on document to allow some leeway for
        // events to move outside of the chart. Interaction model events get
        // registered on the canvas, which is too small to allow this.
        var mousemove = function(event) {
          if (context.isZooming) {
            // When the mouse moves >200px from the chart edge, cancel the zoom.
            var d = distanceFromChart(event, g);
            if (d < DRAG_EDGE_MARGIN) {
              DygraphInteraction.moveZoom(event, g, context);
            } else {
              if (context.dragEndX !== null) {
                context.dragEndX = null;
                context.dragEndY = null;
                g.clearZoomRect_();
              }
            }
          } else if (context.isPanning) {
            DygraphInteraction.movePan(event, g, context);
          }
        };
        var mouseup = function(event) {
          if (context.isZooming) {
            if (context.dragEndX !== null) {
              DygraphInteraction.endZoom(event, g, context);
            } else {
              DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);
            }
          } else if (context.isPanning) {
            DygraphInteraction.endPan(event, g, context);
          }

          removeEvent(document, 'mousemove', mousemove);
          removeEvent(document, 'mouseup', mouseup);
          context.destroy();
        };

        g.addAndTrackEvent(document, 'mousemove', mousemove);
        g.addAndTrackEvent(document, 'mouseup', mouseup);
      },
      willDestroyContextMyself: true,

      touchstart: function(event, g, context) {
        DygraphInteraction.startTouch(event, g, context);
      },
      touchmove: function(event, g, context) {
        DygraphInteraction.moveTouch(event, g, context);
      },
      touchend: function(event, g, context) {
        DygraphInteraction.endTouch(event, g, context);
      },

      // Disable zooming out if panning.
      dblclick: function(event, g, context) {
        if (context.cancelNextDblclick) {
          context.cancelNextDblclick = false;
          return;
        }

        // Give plugins a chance to grab this event.
        var e = {
          canvasx: context.dragEndX,
          canvasy: context.dragEndY,
          cancelable: true,
        };
        if (g.cascadeEvents_('dblclick', e)) {
          return;
        }

        if (event.altKey || event.shiftKey) {
          return;
        }
        g.resetZoom();
      }
    };

    /*
    Dygraph.DEFAULT_ATTRS.interactionModel = DygraphInteraction.defaultModel;

    // old ways of accessing these methods/properties
    Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel;
    Dygraph.endZoom = DygraphInteraction.endZoom;
    Dygraph.moveZoom = DygraphInteraction.moveZoom;
    Dygraph.startZoom = DygraphInteraction.startZoom;
    Dygraph.endPan = DygraphInteraction.endPan;
    Dygraph.movePan = DygraphInteraction.movePan;
    Dygraph.startPan = DygraphInteraction.startPan;
    */

    DygraphInteraction.nonInteractiveModel_ = {
      mousedown: function(event, g, context) {
        context.initializeMouseDown(event, g, context);
      },
      mouseup: DygraphInteraction.maybeTreatMouseOpAsClick
    };

    // Default interaction model when using the range selector.
    DygraphInteraction.dragIsPanInteractionModel = {
      mousedown: function(event, g, context) {
        context.initializeMouseDown(event, g, context);
        DygraphInteraction.startPan(event, g, context);
      },
      mousemove: function(event, g, context) {
        if (context.isPanning) {
          DygraphInteraction.movePan(event, g, context);
        }
      },
      mouseup: function(event, g, context) {
        if (context.isPanning) {
          DygraphInteraction.endPan(event, g, context);
        }
      }
    };

    // Default attribute values.
    var DEFAULT_ATTRS = {
      highlightCircleSize: 3,
      highlightSeriesOpts: null,
      highlightSeriesBackgroundAlpha: 0.5,
      highlightSeriesBackgroundColor: 'rgb(255, 255, 255)',

      labelsSeparateLines: false,
      labelsShowZeroValues: true,
      labelsKMB: false,
      labelsKMG2: false,
      showLabelsOnHighlight: true,

      digitsAfterDecimal: 2,
      maxNumberWidth: 6,
      sigFigs: null,

      strokeWidth: 1.0,
      strokeBorderWidth: 0,
      strokeBorderColor: "white",

      axisTickSize: 3,
      axisLabelFontSize: 14,
      rightGap: 5,

      showRoller: false,
      xValueParser: undefined,

      delimiter: ',',

      sigma: 2.0,
      errorBars: false,
      fractions: false,
      wilsonInterval: true,  // only relevant if fractions is true
      customBars: false,
      fillGraph: false,
      fillAlpha: 0.15,
      connectSeparatedPoints: false,

      stackedGraph: false,
      stackedGraphNaNFill: 'all',
      hideOverlayOnMouseOut: true,
      resizable: 'no',

      legend: 'onmouseover',
      legendFollowOffsetX: 50,
      legendFollowOffsetY: -50,
      stepPlot: false,
      xRangePad: 0,
      yRangePad: null,
      drawAxesAtZero: false,

      // Sizes of the various chart labels.
      titleHeight: 28,
      xLabelHeight: 18,
      yLabelWidth: 18,

      axisLineColor: "black",
      axisLineWidth: 0.3,
      gridLineWidth: 0.3,
      axisLabelWidth: 50,
      gridLineColor: "rgb(128,128,128)",

      interactionModel: DygraphInteraction.defaultModel,
      animatedZooms: false,  // (for now)
      animateBackgroundFade: true,

      // Range selector options
      showRangeSelector: false,
      rangeSelectorHeight: 40,
      rangeSelectorPlotStrokeColor: "#808FAB",
      rangeSelectorPlotFillGradientColor: "white",
      rangeSelectorPlotFillColor: "#A7B1C4",
      rangeSelectorBackgroundStrokeColor: "gray",
      rangeSelectorBackgroundLineWidth: 1,
      rangeSelectorPlotLineWidth:1.5,
      rangeSelectorForegroundStrokeColor: "black",
      rangeSelectorForegroundLineWidth: 1,
      rangeSelectorAlpha: 0.6,
      showInRangeSelector: null,

      // The ordering here ensures that central lines always appear above any
      // fill bars/error bars.
      plotter: [
        DygraphCanvasRenderer._fillPlotter,
        DygraphCanvasRenderer._errorPlotter,
        DygraphCanvasRenderer._linePlotter
      ],

      plugins: [ ],

      // per-axis options
      axes: {
        x: {
          pixelsPerLabel: 70,
          axisLabelWidth: 60,
          axisLabelFormatter: dateAxisLabelFormatter,
          valueFormatter: dateValueFormatter,
          drawGrid: true,
          drawAxis: true,
          independentTicks: true,
          ticker: dateTicker
        },
        y: {
          axisLabelWidth: 50,
          pixelsPerLabel: 30,
          valueFormatter: numberValueFormatter,
          axisLabelFormatter: numberAxisLabelFormatter,
          drawGrid: true,
          drawAxis: true,
          independentTicks: true,
          ticker: numericTicks
        },
        y2: {
          axisLabelWidth: 50,
          pixelsPerLabel: 30,
          valueFormatter: numberValueFormatter,
          axisLabelFormatter: numberAxisLabelFormatter,
          drawAxis: true,  // only applies when there are two axes of data.
          drawGrid: false,
          independentTicks: false,
          ticker: numericTicks
        }
      }
    };

    /**
     * @license
     * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    var OPTIONS_REFERENCE = null;

    if (typeof process !== 'undefined' && "production" != 'production') {
    // For "production" code, this gets removed by uglifyjs.

    // NOTE: in addition to parsing as JS, this snippet is expected to be valid
    // JSON. This assumption cannot be checked in JS, but it will be checked when
    // documentation is generated by the generate-documentation.py script. For the
    // most part, this just means that you should always use double quotes.
    OPTIONS_REFERENCE =  // <JSON>
    {
      "animateBackgroundFade": {
        "default": "true",
        "labels": ["Overall display"],
        "type": "boolean",
        "description": "Activate an animation effect for a gradual fade."
      },
      "xValueParser": {
        "default": "parseFloat() or Date.parse()*",
        "labels": ["CSV parsing"],
        "type": "function(str) -> number",
        "description": "A function which parses x-values (i.e. the dependent series). Must return a number, even when the values are dates. In this case, millis since epoch are used. This is used primarily for parsing CSV data. *=Dygraphs is slightly more accepting in the dates which it will parse. See code for details."
      },
      "stackedGraph": {
        "default": "false",
        "labels": ["Data Line display"],
        "type": "boolean",
        "description": "If set, stack series on top of one another rather than drawing them independently. The first series specified in the input data will wind up on top of the chart and the last will be on bottom. NaN values are drawn as white areas without a line on top, see stackedGraphNaNFill for details."
      },
      "stackedGraphNaNFill": {
        "default": "all",
        "labels": ["Data Line display"],
        "type": "string",
        "description": "Controls handling of NaN values inside a stacked graph. NaN values are interpolated/extended for stacking purposes, but the actual point value remains NaN in the legend display. Valid option values are \"all\" (interpolate internally, repeat leftmost and rightmost value as needed), \"inside\" (interpolate internally only, use zero outside leftmost and rightmost value), and \"none\" (treat NaN as zero everywhere)."
      },
      "pointSize": {
        "default": "1",
        "labels": ["Data Line display"],
        "type": "integer",
        "description": "The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is \"isolated\", i.e. there is a missing point on either side of it. This also controls the size of those dots."
      },
      "drawPoints": {
        "default": "false",
        "labels": ["Data Line display"],
        "type": "boolean",
        "description": "Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a <a href='#drawPointCallback'>drawPointCallback</a>."
      },
      "drawGapEdgePoints": {
        "default": "false",
        "labels": ["Data Line display"],
        "type": "boolean",
        "description": "Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities."
      },
      "drawPointCallback": {
        "default": "null",
        "labels": ["Data Line display"],
        "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize, idx)",
        "parameters": [
          ["g", "the reference graph"],
          ["seriesName", "the name of the series"],
          ["canvasContext", "the canvas to draw on"],
          ["cx", "center x coordinate"],
          ["cy", "center y coordinate"],
          ["color", "series color"],
          ["pointSize", "the radius of the image."],
          ["idx", "the row-index of the point in the data."]
        ],
        "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy).  Also see <a href='#drawHighlightPointCallback'>drawHighlightPointCallback</a>"
      },
      "height": {
        "default": "320",
        "labels": ["Overall display"],
        "type": "integer",
        "description": "Height, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored."
      },
      "resizable": {
        "default": "no",
        "labels": ["Overall display"],
        "type": "string",
        "description": "Whether to add a ResizeObserver to the container div (\"passive\") and additionally make it resizable (\"horizontal\", \"vertical\", \"both\"). In any case, if the container div has CSS \"overflow:visible;\" it will be changed to \"overflow:hidden;\" to make CSS resizing possible. Note that this is distinct from resizing the graph when the window size changes, which is always active; this feature adds user-resizable “handles” to the container div."
      },
      "zoomCallback": {
        "default": "null",
        "labels": ["Callbacks"],
        "type": "function(minDate, maxDate, yRanges)",
        "parameters": [
          ["minDate", "milliseconds since epoch"],
          ["maxDate", "milliseconds since epoch."],
          ["yRanges", "is an array of [bottom, top] pairs, one for each y-axis."]
        ],
        "description": "A function to call when the zoom window is changed (either by zooming in or out). When animatedZooms is set, zoomCallback is called once at the end of the transition (it will not be called for intermediate frames)."
      },
      "pointClickCallback": {
        "snippet": "function(e, point){<br>  alert(point);<br>}",
        "default": "null",
        "labels": ["Callbacks", "Interactive Elements"],
        "type": "function(e, point)",
        "parameters": [
          ["e", "the event object for the click"],
          ["point", "the point that was clicked See <a href='#point_properties'>Point properties</a> for details"]
        ],
        "description": "A function to call when a data point is clicked. and the point that was clicked."
      },
      "color": {
        "default": "(see description)",
        "labels": ["Data Series Colors"],
        "type": "string",
        "example": "red",
        "description": "A per-series color definition. Used in conjunction with, and overrides, the colors option."
      },
      "colors": {
        "default": "(see description)",
        "labels": ["Data Series Colors"],
        "type": "Array of strings",
        "example": "['red', '#00FF00']",
        "description": "List of colors for the data series. These can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\", etc. If not specified, equally-spaced points around a color wheel are used. Overridden by the “color” option."
      },
      "connectSeparatedPoints": {
        "default": "false",
        "labels": ["Data Line display"],
        "type": "boolean",
        "description": "Usually, when Dygraphs encounters a missing value in a data series, it interprets this as a gap and draws it as such. If, instead, the missing values represents an x-value for which only a different series has data, then you’ll want to connect the dots by setting this to true. To explicitly include a gap with this option set, use a value of NaN."
      },
      "highlightCallback": {
        "default": "null",
        "labels": ["Callbacks"],
        "type": "function(event, x, points, row, seriesName)",
        "description": "When set, this callback gets called every time a new point is highlighted.",
        "parameters": [
          ["event", "the JavaScript mousemove event"],
          ["x", "the x-coordinate of the highlighted points"],
          ["points", "an array of highlighted points: <code>[ {name: 'series', yval: y-value}, … ]</code>"],
          ["row", "integer index of the highlighted row in the data table, starting from 0"],
          ["seriesName", "name of the highlighted series, only present if highlightSeriesOpts is set."]
        ]
      },
      "drawHighlightPointCallback": {
        "default": "null",
        "labels": ["Data Line display"],
        "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize, idx)",
        "parameters": [
          ["g", "the reference graph"],
          ["seriesName", "the name of the series"],
          ["canvasContext", "the canvas to draw on"],
          ["cx", "center x coordinate"],
          ["cy", "center y coordinate"],
          ["color", "series color"],
          ["pointSize", "the radius of the image."],
          ["idx", "the row-index of the point in the data."]
        ],
        "description": "Draw a custom item when a point is highlighted.  Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see <a href='#drawPointCallback'>drawPointCallback</a>"
      },
      "highlightSeriesOpts": {
        "default": "null",
        "labels": ["Interactive Elements"],
        "type": "Object",
        "description": "When set, the options from this object are applied to the timeseries closest to the mouse pointer for interactive highlighting. See also “highlightCallback”. Example: highlightSeriesOpts: { strokeWidth: 3 }."
      },
      "highlightSeriesBackgroundAlpha": {
        "default": "0.5",
        "labels": ["Interactive Elements"],
        "type": "float",
        "description": "Fade the background while highlighting series. 1=fully visible background (disable fading), 0=hiddden background (show highlighted series only)."
      },
      "highlightSeriesBackgroundColor": {
        "default": "rgb(255, 255, 255)",
        "labels": ["Interactive Elements"],
        "type": "string",
        "description": "Sets the background color used to fade out the series in conjunction with “highlightSeriesBackgroundAlpha”."
      },
      "includeZero": {
        "default": "false",
        "labels": ["Axis display"],
        "type": "boolean",
        "description": "Usually, dygraphs will use the range of the data plus some padding to set the range of the y-axis. If this option is set, the y-axis will always include zero, typically as the lowest value. This can be used to avoid exaggerating the variance in the data"
      },
      "rollPeriod": {
        "default": "1",
        "labels": ["Error Bars", "Rolling Averages"],
        "type": "integer &gt;= 1",
        "description": "Number of days over which to average data. Discussed extensively above."
      },
      "unhighlightCallback": {
        "default": "null",
        "labels": ["Callbacks"],
        "type": "function(event)",
        "parameters": [
          ["event", "the mouse event"]
        ],
        "description": "When set, this callback gets called every time the user stops highlighting any point by mousing out of the graph."
      },
      "axisTickSize": {
        "default": "3.0",
        "labels": ["Axis display"],
        "type": "number",
        "description": "The size of the line to display next to each tick mark on x- or y-axes."
      },
      "labelsSeparateLines": {
        "default": "false",
        "labels": ["Legend"],
        "type": "boolean",
        "description": "Put <code>&lt;br/&gt;</code> between lines in the label string. Often used in conjunction with <strong>labelsDiv</strong>."
      },
      "valueFormatter": {
        "default": "Depends on the type of your data.",
        "labels": ["Legend", "Value display/formatting"],
        "type": "function(num_or_millis, opts, seriesName, dygraph, row, col)",
        "description": "Function to provide a custom display format for the values displayed on mouseover. This does not affect the values that appear on tick marks next to the axes. To format those, see axisLabelFormatter. This is usually set on a <a href='per-axis.html'>per-axis</a> basis. .",
        "parameters": [
          ["num_or_millis", "The value to be formatted. This is always a number. For date axes, it’s millis since epoch. You can call new Date(millis) to get a Date object."],
          ["opts", "This is a function you can call to access various options (e.g. opts('labelsKMB')). It returns per-axis values for the option when available."],
          ["seriesName", "The name of the series from which the point came, e.g. 'X', 'Y', 'A', etc."],
          ["dygraph", "The dygraph object for which the formatting is being done"],
          ["row", "The row of the data from which this point comes. g.getValue(row, 0) will return the x-value for this point."],
          ["col", "The column of the data from which this point comes. g.getValue(row, col) will return the original y-value for this point. This can be used to get the full confidence interval for the point, or access un-rolled values for the point."]
        ]
      },
      "annotationMouseOverHandler": {
        "default": "null",
        "labels": ["Annotations"],
        "type": "function(annotation, point, dygraph, event)",
        "description": "If provided, this function is called whenever the user mouses over an annotation."
      },
      "annotationMouseOutHandler": {
        "default": "null",
        "labels": ["Annotations"],
        "type": "function(annotation, point, dygraph, event)",
        "parameters": [
          ["annotation", "the annotation left"],
          ["point", "the point associated with the annotation"],
          ["dygraph", "the reference graph"],
          ["event", "the mouse event"]
        ],
        "description": "If provided, this function is called whenever the user mouses out of an annotation."
      },
      "annotationClickHandler": {
        "default": "null",
        "labels": ["Annotations"],
        "type": "function(annotation, point, dygraph, event)",
        "parameters": [
          ["annotation", "the annotation left"],
          ["point", "the point associated with the annotation"],
          ["dygraph", "the reference graph"],
          ["event", "the mouse event"]
        ],
        "description": "If provided, this function is called whenever the user clicks on an annotation."
      },
      "annotationDblClickHandler": {
        "default": "null",
        "labels": ["Annotations"],
        "type": "function(annotation, point, dygraph, event)",
        "parameters": [
          ["annotation", "the annotation left"],
          ["point", "the point associated with the annotation"],
          ["dygraph", "the reference graph"],
          ["event", "the mouse event"]
        ],
        "description": "If provided, this function is called whenever the user double-clicks on an annotation."
      },
      "drawCallback": {
        "default": "null",
        "labels": ["Callbacks"],
        "type": "function(dygraph, is_initial)",
        "parameters": [
          ["dygraph", "The graph being drawn"],
          ["is_initial", "True if this is the initial draw, false for subsequent draws."]
        ],
        "description": "When set, this callback gets called every time the dygraph is drawn. This includes the initial draw, after zooming and repeatedly while panning."
      },
      "labelsKMG2": {
        "default": "false",
        "labels": ["Value display/formatting"],
        "type": "boolean",
        "description": "Show Ki/Mi/Gi for powers of 1024 on y-axis. If used together with <code>labelsKMB</code> (deprecated), K/M/G are used instead."
      },
      "delimiter": {
        "default": ",",
        "labels": ["CSV parsing"],
        "type": "string",
        "description": "The delimiter to look for when separating fields of a CSV file. Setting this to a tab is not usually necessary, since tab-delimited data is auto-detected."
      },
      "axisLabelFontSize": {
        "default": "14",
        "labels": ["Axis display"],
        "type": "integer",
        "description": "Size of the font (in pixels) to use in the axis labels, both x- and y-axis."
      },
      "underlayCallback": {
        "default": "null",
        "labels": ["Callbacks"],
        "type": "function(context, area, dygraph)",
        "parameters": [
          ["context", "the canvas drawing context on which to draw"],
          ["area", "An object with {x,y,w,h} properties describing the drawing area."],
          ["dygraph", "the reference graph"]
        ],
        "description": "When set, this callback gets called before the chart is drawn. It details on how to use this."
      },
      "width": {
        "default": "480",
        "labels": ["Overall display"],
        "type": "integer",
        "description": "Width, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored."
      },
      "pixelRatio": {
        "default": "(devicePixelRatio / context.backingStoreRatio)",
        "labels": ["Overall display"],
        "type": "float",
        "description": "Overrides the pixel ratio scaling factor for the canvas’ 2d context. Ordinarily, this is set to the devicePixelRatio / (context.backingStoreRatio || 1), so on mobile devices, where the devicePixelRatio can be somewhere around 3, performance can be improved by overriding this value to something less precise, like 1, at the expense of resolution."
      },
      "interactionModel": {
        "default": "...",
        "labels": ["Interactive Elements"],
        "type": "Object",
        "description": "TODO(konigsberg): document this"
      },
      "ticker": {
        "default": "Dygraph.dateTicker or Dygraph.numericTicks",
        "labels": ["Axis display"],
        "type": "function(min, max, pixels, opts, dygraph, vals) → [{v: …, label: …}, …]",
        "parameters": [
          ["min", ""],
          ["max", ""],
          ["pixels", ""],
          ["opts", ""],
          ["dygraph", "the reference graph"],
          ["vals", ""]
        ],
        "description": "This lets you specify an arbitrary function to generate tick marks on an axis. The tick marks are an array of (value, label) pairs. The built-in functions go to great lengths to choose good tick marks so, if you set this option, you’ll most likely want to call one of them and modify the result. See dygraph-tickers.js for an extensive discussion. This is set on a <a href='per-axis.html'>per-axis</a> basis."
      },
      "xAxisHeight": {
        "default": "(null)",
        "labels": ["Axis display"],
        "type": "integer",
        "description": "Height, in pixels, of the x-axis. If not set explicitly, this is computed based on axisLabelFontSize and axisTickSize."
      },
      "showLabelsOnHighlight": {
        "default": "true",
        "labels": ["Interactive Elements", "Legend"],
        "type": "boolean",
        "description": "Whether to show the legend upon mouseover."
      },
      "axis": {
        "default": "(none)",
        "labels": ["Axis display"],
        "type": "string",
        "description": "Set to either 'y1' or 'y2' to assign a series to a y-axis (primary or secondary). Must be set per-series."
      },
      "pixelsPerLabel": {
        "default": "70 (x-axis) or 30 (y-axes)",
        "labels": ["Axis display", "Grid"],
        "type": "integer",
        "description": "Number of pixels to require between each x- and y-label. Larger values will yield a sparser axis with fewer ticks. This is set on a <a href='per-axis.html'>per-axis</a> basis."
      },
      "labelsDiv": {
        "default": "null",
        "labels": ["Legend"],
        "type": "DOM element or string",
        "example": "<code style='font-size: small'>document.getElementById('foo')</code> or <code>'foo'</code>",
        "description": "Show data labels in an external div, rather than on the graph.  This value can either be a div element or a div id."
      },
      "fractions": {
        "default": "false",
        "labels": ["CSV parsing", "Error Bars"],
        "type": "boolean",
        "description": "When set, attempt to parse each cell in the CSV file as \"a/b\", where a and b are integers. The ratio will be plotted. This allows computation of Wilson confidence intervals (see below)."
      },
      "logscale": {
        "default": "false",
        "labels": ["Axis display"],
        "type": "boolean",
        "description": "When set for the y-axis or x-axis, the graph shows that axis in log scale. Any values less than or equal to zero are not displayed. Showing log scale with ranges that go below zero will result in an unviewable graph.\n\n Not compatible with showZero. connectSeparatedPoints is ignored. This is ignored for date-based x-axes."
      },
      "strokeWidth": {
        "default": "1.0",
        "labels": ["Data Line display"],
        "type": "float",
        "example": "0.5, 2.0",
        "description": "The width of the lines connecting data points. This can be used to increase the contrast or some graphs."
      },
      "strokePattern": {
        "default": "null",
        "labels": ["Data Line display"],
        "type": "Array of integers",
        "example": "[10, 2, 5, 2]",
        "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed lines."
      },
      "strokeBorderWidth": {
        "default": "null",
        "labels": ["Data Line display"],
        "type": "float",
        "example": "1.0",
        "description": "Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines."
      },
      "strokeBorderColor": {
        "default": "white",
        "labels": ["Data Line display"],
        "type": "string",
        "example": "red, #ccffdd",
        "description": "Color for the line border used if strokeBorderWidth is set."
      },
      "wilsonInterval": {
        "default": "true",
        "labels": ["Error Bars"],
        "type": "boolean",
        "description": "Use in conjunction with the \"fractions\" option. Instead of plotting +/- N standard deviations, dygraphs will compute a Wilson confidence interval and plot that. This has more reasonable behavior for ratios close to 0 or 1."
      },
      "fillGraph": {
        "default": "false",
        "labels": ["Data Line display"],
        "type": "boolean",
        "description": "Should the area underneath the graph be filled? This option is not compatible with <tt>customBars</tt> nor <tt>errorBars</tt>. This may be set on a <a href='per-axis.html'>per-series</a> basis."
      },
      "highlightCircleSize": {
        "default": "3",
        "labels": ["Interactive Elements"],
        "type": "integer",
        "description": "The size in pixels of the dot drawn over highlighted points."
      },
      "gridLineColor": {
        "default": "rgb(128,128,128)",
        "labels": ["Grid"],
        "type": "red, blue",
        "description": "The color of the gridlines. This may be set on a per-axis basis to define each axis’ grid separately."
      },
      "gridLinePattern": {
        "default": "null",
        "labels": ["Grid"],
        "type": "Array of integers",
        "example": "[10, 2, 5, 2]",
        "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed gridlines."
      },
      "visibility": {
        "default": "[true, true, ...]",
        "labels": ["Data Line display"],
        "type": "Array of booleans",
        "description": "Which series should initially be visible? Once the Dygraph has been constructed, you can access and modify the visibility of each series using the <code>visibility</code> and <code>setVisibility</code> methods."
      },
      "valueRange": {
        "default": "Full range of the input is shown",
        "labels": ["Axis display"],
        "type": "Array of two numbers",
        "example": "[10, 110]",
        "description": "Explicitly set the vertical range of the graph to [low, high]. This may be set on a per-axis basis to define each y-axis separately. If either limit is unspecified, it will be calculated automatically (e.g. [null, 30] to automatically calculate just the lower bound)"
      },
      "colorSaturation": {
        "default": "1.0",
        "labels": ["Data Series Colors"],
        "type": "float (0.0 - 1.0)",
        "description": "If <strong>colors</strong> is not specified, saturation of the automatically-generated data series colors."
      },
      "hideOverlayOnMouseOut": {
        "default": "true",
        "labels": ["Interactive Elements", "Legend"],
        "type": "boolean",
        "description": "Whether to hide the legend when the mouse leaves the chart area."
      },
      "legend": {
        "default": "onmouseover",
        "labels": ["Legend"],
        "type": "string",
        "description": "When to display the legend. By default, it only appears when a user mouses over the chart. Set it to \"always\" to always display a legend of some sort, \"never\" to hide it. When set to \"follow\", legend follows highlighted points."
      },
      "legendFollowOffsetX": {
        "default": "50",
        "labels": ["Legend"],
        "type": "integer",
        "description": "Number of pixels to use as horizontal offset from the point for a “floating” legend (\"follow\" mode). This should be positive (to the right) because the legend flips over to the left side if it’s too wide."
      },
      "legendFollowOffsetY": {
        "default": "-50",
        "labels": ["Legend"],
        "type": "integer",
        "description": "Number of pixels to use as vertical offset from the point for a “floating” legend (\"follow\" mode)."
      },
      "legendFormatter": {
        "default": "null",
        "labels": ["Legend"],
        "type": "function(data): string or DocumentFragment node",
        "params": [
          ["data", "An object containing information about the selection (or lack of a selection). This includes formatted values and series information. See <a href=\"legend-formatter.md\"><tt>docs/legend-formatter.md</tt></a> (<a href=\"https://github.com/danvk/dygraphs/blob/master/docs/legend-formatter.md\">online</a>) for sample values."]
        ],
        "description": "Set this to supply a custom formatter for the legend. See <a href=\"legend-formatter.md\"><tt>docs/legend-formatter.md</tt></a> (<a href=\"https://github.com/danvk/dygraphs/blob/master/docs/legend-formatter.md\">online</a>) and the <a href=\"tests/legend-formatter.html\">legendFormatter demo</a> for usage."
      },
      "labelsShowZeroValues": {
        "default": "true",
        "labels": ["Legend"],
        "type": "boolean",
        "description": "Show zero value labels in the labelsDiv."
      },
      "stepPlot": {
        "default": "false",
        "labels": ["Data Line display"],
        "type": "boolean",
        "description": "When set, display the graph as a step plot instead of a line plot. This option may either be set for the whole graph or for single series."
      },
      "labelsUTC": {
        "default": "false",
        "labels": ["Value display/formatting", "Axis display"],
        "type": "boolean",
        "description": "Show date/time labels according to UTC (instead of local time)."
      },
      "labelsKMB": {
        "default": "false",
        "labels": ["Value display/formatting"],
        "type": "boolean",
        "description": "Show k/M/B for thousands/millions/billions on y-axis."
      },
      "rightGap": {
        "default": "5",
        "labels": ["Overall display"],
        "type": "integer",
        "description": "Number of pixels to leave blank at the right edge of the Dygraph. This makes it easier to highlight the right-most data point."
      },
      "drawAxesAtZero": {
        "default": "false",
        "labels": ["Axis display"],
        "type": "boolean",
        "description": "When set, draw the X axis at the Y=0 position and the Y axis at the X=0 position if those positions are inside the graph’s visible area. Otherwise, draw the axes at the bottom or left graph edge as usual."
      },
      "xRangePad": {
        "default": "0",
        "labels": ["Axis display"],
        "type": "float",
        "description": "Add the specified amount of extra space (in pixels) around the X-axis value range to ensure points at the edges remain visible."
      },
      "yRangePad": {
        "default": "null",
        "labels": ["Axis display"],
        "type": "float",
        "description": "If set, add the specified amount of extra space (in pixels) around the Y-axis value range to ensure points at the edges remain visible. If unset, use the traditional Y padding algorithm."
      },
      "axisLabelFormatter": {
        "default": "Depends on the data type",
        "labels": ["Axis display"],
        "type": "function(number_or_Date, granularity, opts, dygraph)",
        "parameters": [
          ["number_or_Date", "Either a number (for a numeric axis) or a Date object (for a date axis)"],
          ["granularity", "specifies how fine-grained the axis is. For date axes, this is a reference to the time granularity enumeration, defined in dygraph-tickers.js, e.g. Dygraph.WEEKLY."],
          ["opts", "a function which provides access to various options on the dygraph, e.g. opts('labelsKMB')."],
          ["dygraph", "the referenced graph"]
        ],
        "description": "Function to call to format the tick values that appear along an axis. This is usually set on a <a href='per-axis.html'>per-axis</a> basis."
      },
      "clickCallback": {
        "snippet": "function(e, date_millis){<br>  alert(new Date(date_millis));<br>}",
        "default": "null",
        "labels": ["Callbacks"],
        "type": "function(e, x, points)",
        "parameters": [
          ["e", "The event object for the click"],
          ["x", "The x value that was clicked (for dates, this is milliseconds since epoch)"],
          ["points", "The closest points along that date. See <a href='#point_properties'>Point properties</a> for details."]
        ],
        "description": "A function to call when the canvas is clicked."
      },
      "labels": {
        "default": "[\"X\", \"Y1\", \"Y2\", ...]*",
        "labels": ["Legend"],
        "type": "Array of strings",
        "description": "A distinct name for each data series, including the independent (X) series. For CSV files and DataTable objects, this is determined by context. For raw data, this must be specified. If it is not, default values are supplied and a warning is logged. Make sure no two names are the same!"
      },
      "dateWindow": {
        "default": "Full range of the input is shown",
        "labels": ["Axis display"],
        "type": "Array of two numbers",
        "example": "[<br>  Date.parse('2006-01-01'),<br>  (new Date()).valueOf()<br>]",
        "description": "Initially zoom in on a section of the graph. Is of the form [earliest, latest], where earliest/latest are milliseconds since epoch. If the data for the x-axis is numeric, the values in dateWindow must also be numbers."
      },
      "showRoller": {
        "default": "false",
        "labels": ["Interactive Elements", "Rolling Averages"],
        "type": "boolean",
        "description": "If the rolling average period text box should be shown."
      },
      "sigma": {
        "default": "2.0",
        "labels": ["Error Bars"],
        "type": "float",
        "description": "When errorBars is set, shade this many standard deviations above/below each point."
      },
      "customBars": {
        "default": "false",
        "labels": ["CSV parsing", "Error Bars"],
        "type": "boolean",
        "description": "When set, parse each CSV cell as \"low;middle;high\". Custom high/low bands will be drawn for each point between low and high, with the series itself going through middle."
      },
      "colorValue": {
        "default": "1.0",
        "labels": ["Data Series Colors"],
        "type": "float (0.0 - 1.0)",
        "description": "If colors is not specified, value of the data series colors, as in hue/saturation/value. (0.0-1.0, default 0.5)"
      },
      "errorBars": {
        "default": "false",
        "labels": ["CSV parsing", "Error Bars"],
        "type": "boolean",
        "description": "Does the data contain standard deviations? Setting this to true alters the input format (see above)."
      },
      "displayAnnotations": {
        "default": "false",
        "labels": ["Annotations"],
        "type": "boolean",
        "description": "Only applies when Dygraphs is used as a GViz chart. Causes string columns following a data series to be interpreted as annotations on points in that series. This is the same format used by Google’s AnnotatedTimeLine chart."
      },
      "panEdgeFraction": {
        "default": "null",
        "labels": ["Axis display", "Interactive Elements"],
        "type": "float",
        "description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% passed the edges of the displayed values. null means no bounds."
      },
      "title": {
        "labels": ["Chart labels"],
        "type": "string",
        "default": "null",
        "description": "Text to display above the chart. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the “dygraph-label” or “dygraph-title” classes."
      },
      "titleHeight": {
        "default": "18",
        "labels": ["Chart labels"],
        "type": "integer",
        "description": "Height of the chart title, in pixels. This also controls the default font size of the title. If you style the title on your own, this controls how much space is set aside above the chart for the title’s div."
      },
      "xlabel": {
        "labels": ["Chart labels"],
        "type": "string",
        "default": "null",
        "description": "Text to display below the chart’s x-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the “dygraph-label” or “dygraph-xlabel” classes."
      },
      "xLabelHeight": {
        "labels": ["Chart labels"],
        "type": "integer",
        "default": "18",
        "description": "Height of the x-axis label, in pixels. This also controls the default font size of the x-axis label. If you style the label on your own, this controls how much space is set aside below the chart for the x-axis label’s div."
      },
      "ylabel": {
        "labels": ["Chart labels"],
        "type": "string",
        "default": "null",
        "description": "Text to display to the left of the chart’s y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the “dygraph-label” or “dygraph-ylabel” classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the per-axis <tt>axisLabelWidth</tt> option on the y axis. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it’s rotated, so width is controlled by the “height” property) or set the yLabelWidth option."
      },
      "y2label": {
        "labels": ["Chart labels"],
        "type": "string",
        "default": "null",
        "description": "Text to display to the right of the chart’s secondary y-axis. This label is only displayed if a secondary y-axis is present. See <a href='tests/two-axes.html'>this test</a> for an example of how to do this. The comments for the “ylabel” option generally apply here as well. This label gets a “dygraph-y2label” instead of a “dygraph-ylabel” class."
      },
      "yLabelWidth": {
        "labels": ["Chart labels"],
        "type": "integer",
        "default": "18",
        "description": "Width of the div which contains the y-axis label. Since the y-axis label appears rotated 90 degrees, this actually affects the height of its div."
      },
      "drawGrid": {
        "default": "true for x and y, false for y2",
        "labels": ["Grid"],
        "type": "boolean",
        "description": "Whether to display gridlines in the chart. This may be set on a per-axis basis to define the visibility of each axis’ grid separately."
      },
      "independentTicks": {
        "default": "true for y, false for y2",
        "labels": ["Axis display", "Grid"],
        "type": "boolean",
        "description": "Only valid for y and y2, has no effect on x: This option defines whether the y axes should align their ticks or if they should be independent. Possible combinations: [1.] y=true, y2=false (default): y is the primary axis and the y2 ticks are aligned to the the ones of y. (only 1 grid) [2.] y=false, y2=true: y2 is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 grid) [3.] y=true, y2=true: Both axis are independent and have their own ticks. (2 grids) [4.] y=false, y2=false: Invalid configuration causes an error."
      },
      "drawAxis": {
        "default": "true for x and y, false for y2",
        "labels": ["Axis display"],
        "type": "boolean",
        "description": "Whether to draw the specified axis. This may be set on a per-axis basis to define the visibility of each axis separately. Setting this to false also prevents axis ticks from being drawn and reclaims the space for the chart grid/lines."
      },
      "gridLineWidth": {
        "default": "0.3",
        "labels": ["Grid"],
        "type": "float",
        "description": "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawGrid option. This may be set on a per-axis basis to define each axis’ grid separately."
      },
      "axisLineWidth": {
        "default": "0.3",
        "labels": ["Axis display"],
        "type": "float",
        "description": "Thickness (in pixels) of the x- and y-axis lines."
      },
      "axisLineColor": {
        "default": "black",
        "labels": ["Axis display"],
        "type": "string",
        "description": "Color of the x- and y-axis lines. Accepts any value which the HTML canvas strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'."
      },
      "fillAlpha": {
        "default": "0.15",
        "labels": ["Error Bars", "Data Series Colors"],
        "type": "float (0.0 - 1.0)",
        "description": "Custom or sigma-based high/low bands for each series are drawn in the same colour as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the bands will not be drawn, whereas a value of 1.0 means that the bands will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point."
      },
      "axisLabelWidth": {
        "default": "50 (y-axis), 60 (x-axis)",
        "labels": ["Axis display", "Chart labels"],
        "type": "integer",
        "description": "Width (in pixels) of the containing divs for x- and y-axis labels. For the y-axis, this also controls the width of the y-axis. Note that for the x-axis, this is independent from pixelsPerLabel, which controls the spacing between labels."
      },
      "sigFigs": {
        "default": "null",
        "labels": ["Value display/formatting"],
        "type": "integer",
        "description": "By default, dygraphs displays numbers with a fixed number of digits after the decimal point. If you’d prefer to have a fixed number of significant figures, set this option to that number of sig figs. A value of 2, for instance, would cause 1 to be display as 1.0 and 1234 to be displayed as 1.23e+3."
      },
      "digitsAfterDecimal": {
        "default": "2",
        "labels": ["Value display/formatting"],
        "type": "integer",
        "description": "Unless it’s run in scientific mode (see the <code>sigFigs</code> option), dygraphs displays numbers with <code>digitsAfterDecimal</code> digits after the decimal point. Trailing zeros are not displayed, so with a value of 2 you’ll get '0', '0.1', '0.12', '123.45' but not '123.456' (it will be rounded to '123.46'). Numbers with absolute value less than 0.1^digitsAfterDecimal (i.e. those which would show up as '0.00') will be displayed in scientific notation."
      },
      "maxNumberWidth": {
        "default": "6",
        "labels": ["Value display/formatting"],
        "type": "integer",
        "description": "When displaying numbers in normal (not scientific) mode, large numbers will be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This can lead to unwieldy y-axis labels. If there are more than <code>maxNumberWidth</code> digits to the left of the decimal in a number, dygraphs will switch to scientific notation, even when not operating in scientific mode. If you’d like to see all those digits, set this to something large, like 20 or 30."
      },
      "file": {
        "default": "(set when constructed)",
        "labels": ["Data"],
        "type": "string (URL of CSV or CSV), GViz DataTable or 2D Array",
        "description": "Sets the data being displayed in the chart. This can only be set when calling updateOptions; it cannot be set from the constructor. For a full description of valid data formats, see the <a href='data.html'>Data Formats</a> page."
      },
      "timingName": {
        "default": "null",
        "labels": ["Debugging", "Deprecated"],
        "type": "string",
        "description": "Set this option to log timing information. The value of the option will be logged along with the timimg, so that you can distinguish multiple dygraphs on the same page."
      },
      "showRangeSelector": {
        "default": "false",
        "labels": ["Range Selector"],
        "type": "boolean",
        "description": "Show or hide the range selector widget."
      },
      "rangeSelectorHeight": {
        "default": "40",
        "labels": ["Range Selector"],
        "type": "integer",
        "description": "Height, in pixels, of the range selector widget. This option can only be specified at Dygraph creation time."
      },
      "rangeSelectorPlotStrokeColor": {
        "default": "#808FAB",
        "labels": ["Range Selector"],
        "type": "string",
        "description": "The range selector mini plot stroke color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off stroke."
      },
      "rangeSelectorPlotFillColor": {
        "default": "#A7B1C4",
        "labels": ["Range Selector"],
        "type": "string",
        "description": "The range selector mini plot fill color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off fill."
      },
      "rangeSelectorPlotFillGradientColor": {
        "default": "white",
        "labels": ["Range Selector"],
        "type": "string",
        "description": "The top color for the range selector mini plot fill color gradient. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"rgba(255,100,200,42)\" or \"yellow\". You can also specify null or \"\" to disable the gradient and fill with one single color."
      },
      "rangeSelectorBackgroundStrokeColor": {
        "default": "gray",
        "labels": ["Range Selector"],
        "type": "string",
        "description": "The color of the lines below and on both sides of the range selector mini plot. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"."
      },
      "rangeSelectorBackgroundLineWidth": {
        "default": "1",
        "labels": ["Range Selector"],
        "type": "float",
        "description": "The width of the lines below and on both sides of the range selector mini plot."
      },
      "rangeSelectorPlotLineWidth": {
        "default": "1.5",
        "labels": ["Range Selector"],
        "type": "float",
        "description": "The width of the range selector mini plot line."
      },
      "rangeSelectorForegroundStrokeColor": {
        "default": "black",
        "labels": ["Range Selector"],
        "type": "string",
        "description": "The color of the lines in the interactive layer of the range selector. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"."
      },
      "rangeSelectorForegroundLineWidth": {
        "default": "1",
        "labels": ["Range Selector"],
        "type": "float",
        "description": "The width the lines in the interactive layer of the range selector."
      },
      "rangeSelectorAlpha": {
        "default": "0.6",
        "labels": ["Range Selector"],
        "type": "float (0.0 - 1.0)",
        "description": "The transparency of the veil that is drawn over the unselected portions of the range selector mini plot. A value of 0 represents full transparency and the unselected portions of the mini plot will appear as normal. A value of 1 represents full opacity and the unselected portions of the mini plot will be hidden."
      },
      "rangeSelectorVeilColour": {
        "default": "null",
        "labels": ["Range Selector"],
        "type": "string",
        "description": "The fillStyle for the veil of the range selector (e.g. \"rgba(240, 240, 240, 0.6)\"); if set, the rangeSelectorAlpha option is ignored."
      },
      "showInRangeSelector": {
        "default": "null",
        "labels": ["Range Selector"],
        "type": "boolean",
        "description": "Mark this series for inclusion in the range selector. The mini plot curve will be an average of all such series. If this is not specified for any series, the default behavior is to average all the visible series. Setting it for one series will result in that series being charted alone in the range selector. Once it’s set for a single series, it needs to be set for all series which should be included (regardless of visibility)."
      },
      "animatedZooms": {
        "default": "false",
        "labels": ["Interactive Elements"],
        "type": "boolean",
        "description": "Set this option to animate the transition between zoom windows. Applies to programmatic and interactive zooms. Note that if you also set a drawCallback, it will be called several times on each zoom. If you set a zoomCallback, it will only be called after the animation is complete."
      },
      "plotter": {
        "default": "[DygraphCanvasRenderer.Plotters.fillPlotter, DygraphCanvasRenderer.Plotters.errorPlotter, DygraphCanvasRenderer.Plotters.linePlotter]",
        "labels": ["Data Line display"],
        "type": "array or function",
        "description": "A function (or array of functions) which plot each data series on the chart. TODO(danvk): more details! May be set per-series."
      },
      "axes": {
        "default": "null",
        "labels": ["Configuration"],
        "type": "Object",
        "description": "Defines per-axis options. Valid keys are 'x', 'y' and 'y2'. Only some options may be set on a per-axis basis. If an option may be set in this way, it will be noted on this page. See also documentation on <a href='per-axis.html'>per-series and per-axis options</a>."
      },
      "series": {
        "default": "null",
        "labels": ["Series"],
        "type": "Object",
        "description": "Defines per-series options. Its keys match the y-axis label names, and the values are dictionaries themselves that contain options specific to that series."
      },
      "plugins": {
        "default": "[]",
        "labels": ["Configuration"],
        "type": "Array of plugins",
        "description": "Defines per-graph plugins. Useful for per-graph customization"
      },
      "dataHandler": {
        "default": "(depends on data)",
        "labels": ["Data"],
        "type": "Dygraph.DataHandler",
        "description": "Custom DataHandler. This is an advanced customisation. See <a href='datahandler-proposal.pdf'><tt>docs/datahandler-proposal.pdf</tt></a>."
      }
    }
    ;  // </JSON>
    // NOTE: in addition to parsing as JS, this snippet is expected to be valid
    // JSON. This assumption cannot be checked in JS, but it will be checked when
    // documentation is generated by the generate-documentation.py script. For the
    // most part, this just means that you should always use double quotes.

    // Do a quick sanity check on the options reference.
    const warn = function warn(msg) {
      if (window.console)
        window.console.warn(msg);
    };
    const flds = ['type', 'default', 'description'];
    const valid_cats =  // <JSON>
    {
      "Annotations": "",
      "Axis display": "",
      "CSV parsing": "",
      "Callbacks": "",
      "Chart labels": "",
      "Configuration": "",
      "Data Line display": "",
      "Data Series Colors": "",
      "Data": "",
      "Debugging": "",
      "Deprecated": "",
      "Error Bars": "These are actually high/low bands, not error bars; the misnomer is historic.",
      "Grid": "",
      "Interactive Elements": "",
      "Legend": "",
      "Overall display": "",
      "Range Selector": "",
      "Rolling Averages": "",
      "Series": "",
      "Value display/formatting": ""
    }
    ;  // </JSON>
    for (let k in OPTIONS_REFERENCE) {
      if (!OPTIONS_REFERENCE.hasOwnProperty(k)) continue;
      var op = OPTIONS_REFERENCE[k];
      for (let i = 0; i < flds.length; i++) {
        if (!op.hasOwnProperty(flds[i])) {
          warn('Option ' + k + ' missing "' + flds[i] + '" property');
        } else if (typeof(op[flds[i]]) != 'string') {
          warn(k + '.' + flds[i] + ' must be of type string');
        }
      }
      let labels = op.labels;
      if (!Array.isArray(labels)) {
        warn('Option "' + k + '" is missing a "labels": [...] option');
      } else {
        for (let i = 0; i < labels.length; i++) {
          if (!valid_cats.hasOwnProperty(labels[i])) {
            warn('Option "' + k + '" has label "' + labels[i] +
                 '", which is invalid.');
          }
        }
      }
    }

    }

    var OPTIONS_REFERENCE$1 = OPTIONS_REFERENCE;

    /**
     * @license
     * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /*
     * Interesting member variables: (REMOVING THIS LIST AS I CLOSURIZE)
     * global_ - global attributes (common among all graphs, AIUI)
     * user - attributes set by the user
     * series_ - { seriesName -> { idx, yAxis, options }}
     */

    /**
     * This parses attributes into an object that can be easily queried.
     *
     * It doesn't necessarily mean that all options are available, specifically
     * if labels are not yet available, since those drive details of the per-series
     * and per-axis options.
     *
     * @param {Dygraph} dygraph The chart to which these options belong.
     * @constructor
     */
    var DygraphOptions = function(dygraph) {
      /**
       * The dygraph.
       * @type {!Dygraph}
       */
      this.dygraph_ = dygraph;

      /**
       * Array of axis index to { series : [ series names ] , options : { axis-specific options. } }
       * @type {Array.<{series : Array.<string>, options : Object}>} @private
       */
      this.yAxes_ = [];

      /**
       * Contains x-axis specific options, which are stored in the options key.
       * This matches the yAxes_ object structure (by being a dictionary with an
       * options element) allowing for shared code.
       * @type {options: Object} @private
       */
      this.xAxis_ = {};
      this.series_ = {};

      // Once these two objects are initialized, you can call get();
      this.global_ = this.dygraph_.attrs_;
      this.user_ = this.dygraph_.user_attrs_ || {};

      /**
       * A list of series in columnar order.
       * @type {Array.<string>}
       */
      this.labels_ = [];

      this.highlightSeries_ = this.get("highlightSeriesOpts") || {};
      this.reparseSeries();
    };

    /**
     * Not optimal, but does the trick when you're only using two axes.
     * If we move to more axes, this can just become a function.
     *
     * @type {Object.<number>}
     * @private
     */
    DygraphOptions.AXIS_STRING_MAPPINGS_ = {
      'y' : 0,
      'Y' : 0,
      'y1' : 0,
      'Y1' : 0,
      'y2' : 1,
      'Y2' : 1
    };

    /**
     * @param {string|number} axis
     * @private
     */
    DygraphOptions.axisToIndex_ = function(axis) {
      if (typeof(axis) == "string") {
        if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) {
          return DygraphOptions.AXIS_STRING_MAPPINGS_[axis];
        }
        throw "Unknown axis : " + axis;
      }
      if (typeof(axis) == "number") {
        if (axis === 0 || axis === 1) {
          return axis;
        }
        throw "Dygraphs only supports two y-axes, indexed from 0-1.";
      }
      if (axis) {
        throw "Unknown axis : " + axis;
      }
      // No axis specification means axis 0.
      return 0;
    };

    /**
     * Reparses options that are all related to series. This typically occurs when
     * options are either updated, or source data has been made available.
     *
     * TODO(konigsberg): The method name is kind of weak; fix.
     */
    DygraphOptions.prototype.reparseSeries = function() {
      var labels = this.get("labels");
      if (!labels) {
        return; // -- can't do more for now, will parse after getting the labels.
      }

      this.labels_ = labels.slice(1);

      this.yAxes_ = [ { series : [], options : {}} ]; // Always one axis at least.
      this.xAxis_ = { options : {} };
      this.series_ = {};

      // Series are specified in the series element:
      //
      // {
      //   labels: [ "X", "foo", "bar" ],
      //   pointSize: 3,
      //   series : {
      //     foo : {}, // options for foo
      //     bar : {} // options for bar
      //   }
      // }
      //
      // So, if series is found, it's expected to contain per-series data,
      // otherwise set a default.
      var seriesDict = this.user_.series || {};
      for (var idx = 0; idx < this.labels_.length; idx++) {
        var seriesName = this.labels_[idx];
        var optionsForSeries = seriesDict[seriesName] || {};
        var yAxis = DygraphOptions.axisToIndex_(optionsForSeries["axis"]);

        this.series_[seriesName] = {
          idx: idx,
          yAxis: yAxis,
          options : optionsForSeries };

        if (!this.yAxes_[yAxis]) {
          this.yAxes_[yAxis] =  { series : [ seriesName ], options : {} };
        } else {
          this.yAxes_[yAxis].series.push(seriesName);
        }
      }

      var axis_opts = this.user_["axes"] || {};
      update(this.yAxes_[0].options, axis_opts["y"] || {});
      if (this.yAxes_.length > 1) {
        update(this.yAxes_[1].options, axis_opts["y2"] || {});
      }
      update(this.xAxis_.options, axis_opts["x"] || {});

      if (typeof process !== 'undefined' && "production" != 'production') {
      // For "production" code, this gets removed by uglifyjs.
        this.validateOptions_();
      }
    };

    /**
     * Get a global value.
     *
     * @param {string} name the name of the option.
     */
    DygraphOptions.prototype.get = function(name) {
      var result = this.getGlobalUser_(name);
      if (result !== null) {
        return result;
      }
      return this.getGlobalDefault_(name);
    };

    DygraphOptions.prototype.getGlobalUser_ = function(name) {
      if (this.user_.hasOwnProperty(name)) {
        return this.user_[name];
      }
      return null;
    };

    DygraphOptions.prototype.getGlobalDefault_ = function(name) {
      if (this.global_.hasOwnProperty(name)) {
        return this.global_[name];
      }
      if (DEFAULT_ATTRS.hasOwnProperty(name)) {
        return DEFAULT_ATTRS[name];
      }
      return null;
    };

    /**
     * Get a value for a specific axis. If there is no specific value for the axis,
     * the global value is returned.
     *
     * @param {string} name the name of the option.
     * @param {string|number} axis the axis to search. Can be the string representation
     * ("y", "y2") or the axis number (0, 1).
     */
    DygraphOptions.prototype.getForAxis = function(name, axis) {
      var axisIdx;
      var axisString;

      // Since axis can be a number or a string, straighten everything out here.
      if (typeof(axis) == 'number') {
        axisIdx = axis;
        axisString = axisIdx === 0 ? "y" : "y2";
      } else {
        if (axis == "y1") { axis = "y"; } // Standardize on 'y'. Is this bad? I think so.
        if (axis == "y") {
          axisIdx = 0;
        } else if (axis == "y2") {
          axisIdx = 1;
        } else if (axis == "x") {
          axisIdx = -1; // simply a placeholder for below.
        } else {
          throw "Unknown axis " + axis;
        }
        axisString = axis;
      }

      var userAxis = (axisIdx == -1) ? this.xAxis_ : this.yAxes_[axisIdx];

      // Search the user-specified axis option first.
      if (userAxis) { // This condition could be removed if we always set up this.yAxes_ for y2.
        var axisOptions = userAxis.options;
        if (axisOptions.hasOwnProperty(name)) {
          return axisOptions[name];
        }
      }

      // User-specified global options second.
      // But, hack, ignore globally-specified 'logscale' for 'x' axis declaration.
      if (!(axis === 'x' && name === 'logscale')) {
        var result = this.getGlobalUser_(name);
        if (result !== null) {
          return result;
        }
      }
      // Default axis options third.
      var defaultAxisOptions = DEFAULT_ATTRS.axes[axisString];
      if (defaultAxisOptions.hasOwnProperty(name)) {
        return defaultAxisOptions[name];
      }

      // Default global options last.
      return this.getGlobalDefault_(name);
    };

    /**
     * Get a value for a specific series. If there is no specific value for the series,
     * the value for the axis is returned (and afterwards, the global value.)
     *
     * @param {string} name the name of the option.
     * @param {string} series the series to search.
     */
    DygraphOptions.prototype.getForSeries = function(name, series) {
      // Honors indexes as series.
      if (series === this.dygraph_.getHighlightSeries()) {
        if (this.highlightSeries_.hasOwnProperty(name)) {
          return this.highlightSeries_[name];
        }
      }

      if (!this.series_.hasOwnProperty(series)) {
        throw "Unknown series: " + series;
      }

      var seriesObj = this.series_[series];
      var seriesOptions = seriesObj["options"];
      if (seriesOptions.hasOwnProperty(name)) {
        return seriesOptions[name];
      }

      return this.getForAxis(name, seriesObj["yAxis"]);
    };

    /**
     * Returns the number of y-axes on the chart.
     * @return {number} the number of axes.
     */
    DygraphOptions.prototype.numAxes = function() {
      return this.yAxes_.length;
    };

    /**
     * Return the y-axis for a given series, specified by name.
     */
    DygraphOptions.prototype.axisForSeries = function(series) {
      return this.series_[series].yAxis;
    };

    /**
     * Returns the options for the specified axis.
     */
    // TODO(konigsberg): this is y-axis specific. Support the x axis.
    DygraphOptions.prototype.axisOptions = function(yAxis) {
      return this.yAxes_[yAxis].options;
    };

    /**
     * Return the series associated with an axis.
     */
    DygraphOptions.prototype.seriesForAxis = function(yAxis) {
      return this.yAxes_[yAxis].series;
    };

    /**
     * Return the list of all series, in their columnar order.
     */
    DygraphOptions.prototype.seriesNames = function() {
      return this.labels_;
    };

    if (typeof process !== 'undefined' && "production" != 'production') {
    // For "production" code, this gets removed by uglifyjs.

    /**
     * Validate all options.
     * This requires OPTIONS_REFERENCE, which is only available in debug builds.
     * @private
     */
    DygraphOptions.prototype.validateOptions_ = function() {
      if (typeof OPTIONS_REFERENCE$1 === 'undefined') {
        throw 'Called validateOptions_ in prod build.';
      }

      var that = this;
      var validateOption = function(optionName) {
        if (!OPTIONS_REFERENCE$1[optionName]) {
          that.warnInvalidOption_(optionName);
        }
      };

      var optionsDicts = [this.xAxis_.options,
                          this.yAxes_[0].options,
                          this.yAxes_[1] && this.yAxes_[1].options,
                          this.global_,
                          this.user_,
                          this.highlightSeries_];
      var names = this.seriesNames();
      for (var i = 0; i < names.length; i++) {
        var name = names[i];
        if (this.series_.hasOwnProperty(name)) {
          optionsDicts.push(this.series_[name].options);
        }
      }
      for (var i = 0; i < optionsDicts.length; i++) {
        var dict = optionsDicts[i];
        if (!dict) continue;
        for (var optionName in dict) {
          if (dict.hasOwnProperty(optionName)) {
            validateOption(optionName);
          }
        }
      }
    };

    var WARNINGS = {};  // Only show any particular warning once.

    /**
     * Logs a warning about invalid options.
     * TODO: make this throw for testing
     * @private
     */
    DygraphOptions.prototype.warnInvalidOption_ = function(optionName) {
      if (!WARNINGS[optionName]) {
        WARNINGS[optionName] = true;
        var isSeries = (this.labels_.indexOf(optionName) >= 0);
        if (isSeries) {
          console.warn('Use new-style per-series options (saw ' + optionName + ' as top-level options key). See http://blog.dygraphs.com/2012/12/the-new-and-better-way-to-specify.html (The New and Better Way to Specify Series and Axis Options).');
        } else {
          console.warn('Unknown option ' + optionName + ' (see https://dygraphs.com/options.html for the full list of options)');
        }
        throw "invalid option " + optionName;
      }
    };

    // Reset list of previously-shown warnings. Used for testing.
    DygraphOptions.resetWarnings_ = function() {
      WARNINGS = {};
    };

    }

    /**
     * To create a "drag" interaction, you typically register a mousedown event
     * handler on the element where the drag begins. In that handler, you register a
     * mouseup handler on the window to determine when the mouse is released,
     * wherever that release happens. This works well, except when the user releases
     * the mouse over an off-domain iframe. In that case, the mouseup event is
     * handled by the iframe and never bubbles up to the window handler.
     *
     * To deal with this issue, we cover iframes with high z-index divs to make sure
     * they don't capture mouseup.
     *
     * Usage:
     * element.addEventListener('mousedown', function() {
     *   var tarper = new IFrameTarp();
     *   tarper.cover();
     *   var mouseUpHandler = function() {
     *     ...
     *     window.removeEventListener(mouseUpHandler);
     *     tarper.uncover();
     *   };
     *   window.addEventListener('mouseup', mouseUpHandler);
     * });
     *
     * @constructor
     */

    function IFrameTarp() {
      /** @type {Array.<!HTMLDivElement>} */
      this.tarps = [];
    }

    /**
     * Find all the iframes in the document and cover them with high z-index
     * transparent divs.
     */
    IFrameTarp.prototype.cover = function() {
      var iframes = document.getElementsByTagName("iframe");
      for (var i = 0; i < iframes.length; i++) {
        var iframe = iframes[i];
        var pos = findPos(iframe),
            x = pos.x,
            y = pos.y,
            width = iframe.offsetWidth,
            height = iframe.offsetHeight;

        var div = document.createElement("div");
        div.style.position = "absolute";
        div.style.left = x + 'px';
        div.style.top = y + 'px';
        div.style.width = width + 'px';
        div.style.height = height + 'px';
        div.style.zIndex = 999;
        document.body.appendChild(div);
        this.tarps.push(div);
      }
    };

    /**
     * Remove all the iframe covers. You should call this in a mouseup handler.
     */
    IFrameTarp.prototype.uncover = function() {
      for (var i = 0; i < this.tarps.length; i++) {
        this.tarps[i].parentNode.removeChild(this.tarps[i]);
      }
      this.tarps = [];
    };

    /**
     * @license
     * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     *
     * The data handler is responsible for all data specific operations. All of the
     * series data it receives and returns is always in the unified data format.
     * Initially the unified data is created by the extractSeries method
     * @constructor
     */
    var DygraphDataHandler = function () {
    };

    var handler = DygraphDataHandler;

    /**
     * X-value array index constant for unified data samples.
     * @const
     * @type {number}
     */
    handler.X = 0;

    /**
     * Y-value array index constant for unified data samples.
     * @const
     * @type {number}
     */
    handler.Y = 1;

    /**
     * Extras-value array index constant for unified data samples.
     * @const
     * @type {number}
     */
    handler.EXTRAS = 2;

    /**
     * Extracts one series from the raw data (a 2D array) into an array of the
     * unified data format.
     * This is where undesirable points (i.e. negative values on log scales and
     * missing values through which we wish to connect lines) are dropped.
     * TODO(danvk): the "missing values" bit above doesn't seem right.
     *
     * @param {!Array.<Array>} rawData The raw data passed into dygraphs where
     *     rawData[i] = [x,ySeries1,...,ySeriesN].
     * @param {!number} seriesIndex Index of the series to extract. All other
     *     series should be ignored.
     * @param {!DygraphOptions} options Dygraph options.
     * @return {Array.<[!number,?number,?]>} The series in the unified data format
     *     where series[i] = [x,y,{extras}].
     */
    handler.prototype.extractSeries = function(rawData, seriesIndex, options) {
    };

    /**
     * Converts a series to a Point array.  The resulting point array must be
     * returned in increasing order of idx property.
     *
     * @param {!Array.<[!number,?number,?]>} series The series in the unified
     *          data format where series[i] = [x,y,{extras}].
     * @param {!string} setName Name of the series.
     * @param {!number} boundaryIdStart Index offset of the first point, equal to the
     *          number of skipped points left of the date window minimum (if any).
     * @return {!Array.<Dygraph.PointType>} List of points for this series.
     */
    handler.prototype.seriesToPoints = function(series, setName, boundaryIdStart) {
      // TODO(bhs): these loops are a hot-spot for high-point-count charts. In
      // fact,
      // on chrome+linux, they are 6 times more expensive than iterating through
      // the
      // points and drawing the lines. The brunt of the cost comes from allocating
      // the |point| structures.
      var points = [];
      for ( var i = 0; i < series.length; ++i) {
        var item = series[i];
        var yraw = item[1];
        var yval = yraw === null ? null : handler.parseFloat(yraw);
        var point = {
          x : NaN,
          y : NaN,
          xval : handler.parseFloat(item[0]),
          yval : yval,
          name : setName, // TODO(danvk): is this really necessary?
          idx : i + boundaryIdStart,
          canvasx: NaN, // add these so we do not alter the structure later, which slows Chrome
          canvasy: NaN,
        };
        points.push(point);
      }
      this.onPointsCreated_(series, points);
      return points;
    };

    /**
     * Callback called for each series after the series points have been generated
     * which will later be used by the plotters to draw the graph.
     * Here data may be added to the seriesPoints which is needed by the plotters.
     * The indexes of series and points are in sync meaning the original data
     * sample for series[i] is points[i].
     *
     * @param {!Array.<[!number,?number,?]>} series The series in the unified
     *     data format where series[i] = [x,y,{extras}].
     * @param {!Array.<Dygraph.PointType>} points The corresponding points passed
     *     to the plotter.
     * @protected
     */
    handler.prototype.onPointsCreated_ = function(series, points) {
    };

    /**
     * Calculates the rolling average of a data set.
     *
     * @param {!Array.<[!number,?number,?]>} series The series in the unified
     *          data format where series[i] = [x,y,{extras}].
     * @param {!number} rollPeriod The number of points over which to average the data
     * @param {!DygraphOptions} options The dygraph options.
     * @param {!number} seriesIndex Index of the series this was extracted from.
     * @return {!Array.<[!number,?number,?]>} the rolled series.
     */
    handler.prototype.rollingAverage = function(series, rollPeriod, options, seriesIndex) {
    };

    /**
     * Computes the range of the data series (including confidence intervals).
     *
     * @param {!Array.<[!number,?number,?]>} series The series in the unified
     *     data format where series[i] = [x, y, {extras}].
     * @param {!Array.<number>} dateWindow The x-value range to display with
     *     the format: [min, max].
     * @param {boolean} stepPlot Whether the stepPlot option is set.
     * @return {Array.<number>} The low and high extremes of the series in the
     *     given window with the format: [low, high].
     */
    handler.prototype.getExtremeYValues = function(series, dateWindow, stepPlot) {
    };

    /**
     * Callback called for each series after the layouting data has been
     * calculated before the series is drawn. Here normalized positioning data
     * should be calculated for the extras of each point.
     *
     * @param {!Array.<Dygraph.PointType>} points The points passed to
     *          the plotter.
     * @param {!Object} axis The axis on which the series will be plotted.
     * @param {!boolean} logscale Whether or not to use a logscale.
     */
    handler.prototype.onLineEvaluated = function(points, axis, logscale) {
    };

    /**
     * Optimized replacement for parseFloat, which was way too slow when almost
     * all values were type number, with few edge cases, none of which were strings.
     * @param {?number} val
     * @return {number}
     * @protected
     */
    handler.parseFloat = function(val) {
      // parseFloat(null) is NaN
      if (val === null) {
        return NaN;
      }

      // Assume it's a number or NaN. If it's something else, I'll be shocked.
      return val;
    };

    /**
     * @license
     * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * @constructor
     * @extends Dygraph.DataHandler
     */
    var DefaultHandler = function() {
    };

    DefaultHandler.prototype = new DygraphDataHandler();

    /** @inheritDoc */
    DefaultHandler.prototype.extractSeries = function(rawData, i, options) {
      // TODO(danvk): pre-allocate series here.
      var series = [];
      const seriesLabel = options.get("labels")[i];
      const logScale = options.getForSeries("logscale", seriesLabel);
      for ( var j = 0; j < rawData.length; j++) {
        var x = rawData[j][0];
        var point = rawData[j][i];
        if (logScale) {
          // On the log scale, points less than zero do not exist.
          // This will create a gap in the chart.
          if (point <= 0) {
            point = null;
          }
        }
        series.push([ x, point ]);
      }
      return series;
    };

    /** @inheritDoc */
    DefaultHandler.prototype.rollingAverage = function(originalData, rollPeriod,
        options, i) {
      rollPeriod = Math.min(rollPeriod, originalData.length);
      var rollingData = [];

      var i, j, y, sum, num_ok;
      // Calculate the rolling average for the first rollPeriod - 1 points
      // where
      // there is not enough data to roll over the full number of points
      if (rollPeriod == 1) {
        return originalData;
      }
      for (i = 0; i < originalData.length; i++) {
        sum = 0;
        num_ok = 0;
        for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
          y = originalData[j][1];
          if (y === null || isNaN(y))
            continue;
          num_ok++;
          sum += originalData[j][1];
        }
        if (num_ok) {
          rollingData[i] = [ originalData[i][0], sum / num_ok ];
        } else {
          rollingData[i] = [ originalData[i][0], null ];
        }
      }

      return rollingData;
    };

    /** @inheritDoc */
    DefaultHandler.prototype.getExtremeYValues = function getExtremeYValues(series, dateWindow, stepPlot) {
      var minY = null, maxY = null, y;
      var firstIdx = 0, lastIdx = series.length - 1;

      for ( var j = firstIdx; j <= lastIdx; j++) {
        y = series[j][1];
        if (y === null || isNaN(y))
          continue;
        if (maxY === null || y > maxY) {
          maxY = y;
        }
        if (minY === null || y < minY) {
          minY = y;
        }
      }
      return [ minY, maxY ];
    };

    /**
     * @license
     * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * @constructor
     * @extends {Dygraph.DataHandler}
     */
    var BarsHandler = function() {
      DygraphDataHandler.call(this);
    };
    BarsHandler.prototype = new DygraphDataHandler();

    // TODO(danvk): figure out why the jsdoc has to be copy/pasted from superclass.
    //   (I get closure compiler errors if this isn't here.)
    /**
     * @override
     * @param {!Array.<Array>} rawData The raw data passed into dygraphs where
     *     rawData[i] = [x,ySeries1,...,ySeriesN].
     * @param {!number} seriesIndex Index of the series to extract. All other
     *     series should be ignored.
     * @param {!DygraphOptions} options Dygraph options.
     * @return {Array.<[!number,?number,?]>} The series in the unified data format
     *     where series[i] = [x,y,{extras}].
     */
    BarsHandler.prototype.extractSeries = function(rawData, seriesIndex, options) {
      // Not implemented here must be extended
    };

    /**
     * @override
     * @param {!Array.<[!number,?number,?]>} series The series in the unified
     *          data format where series[i] = [x,y,{extras}].
     * @param {!number} rollPeriod The number of points over which to average the data
     * @param {!DygraphOptions} options The dygraph options.
     * @param {!number} seriesIndex Index of the series this was extracted from.
     * TODO(danvk): be more specific than "Array" here.
     * @return {!Array.<[!number,?number,?]>} the rolled series.
     */
    BarsHandler.prototype.rollingAverage =
        function(series, rollPeriod, options, seriesIndex) {
      // Not implemented here, must be extended.
    };

    /** @inheritDoc */
    BarsHandler.prototype.onPointsCreated_ = function(series, points) {
      for (var i = 0; i < series.length; ++i) {
        var item = series[i];
        var point = points[i];
        point.y_top = NaN;
        point.y_bottom = NaN;
        point.yval_minus = DygraphDataHandler.parseFloat(item[2][0]);
        point.yval_plus = DygraphDataHandler.parseFloat(item[2][1]);
      }
    };

    /** @inheritDoc */
    BarsHandler.prototype.getExtremeYValues = function(series, dateWindow, stepPlot) {
      var minY = null, maxY = null, y;

      var firstIdx = 0;
      var lastIdx = series.length - 1;

      for ( var j = firstIdx; j <= lastIdx; j++) {
        y = series[j][1];
        if (y === null || isNaN(y)) continue;

        var low = series[j][2][0];
        var high = series[j][2][1];

        if (low > y) low = y; // this can happen with custom bars,
        if (high < y) high = y; // e.g. in tests/custom-bars.html

        if (maxY === null || high > maxY) maxY = high;
        if (minY === null || low < minY) minY = low;
      }

      return [ minY, maxY ];
    };

    /** @inheritDoc */
    BarsHandler.prototype.onLineEvaluated = function(points, axis, logscale) {
      var point;
      for (var j = 0; j < points.length; j++) {
        // Copy over the error terms
        point = points[j];
        point.y_top = DygraphLayout.calcYNormal_(axis, point.yval_minus, logscale);
        point.y_bottom = DygraphLayout.calcYNormal_(axis, point.yval_plus, logscale);
      }
    };

    /**
     * @license
     * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * @constructor
     * @extends BarsHandler
     */
    var ErrorBarsHandler = function() {
    };

    ErrorBarsHandler.prototype = new BarsHandler();

    /** @inheritDoc */
    ErrorBarsHandler.prototype.extractSeries = function(rawData, i, options) {
      // TODO(danvk): pre-allocate series here.
      var series = [];
      var x, y, variance, point;
      const seriesLabel = options.get("labels")[i];
      const logScale = options.getForSeries("logscale", seriesLabel);
      const sigma = options.getForSeries("sigma", seriesLabel);
      for ( var j = 0; j < rawData.length; j++) {
        x = rawData[j][0];
        point = rawData[j][i];
        if (logScale && point !== null) {
          // On the log scale, points less than zero do not exist.
          // This will create a gap in the chart.
          if (point[0] <= 0 || point[0] - sigma * point[1] <= 0) {
            point = null;
          }
        }
        // Extract to the unified data format.
        if (point !== null) {
          y = point[0];
          if (y !== null && !isNaN(y)) {
            variance = sigma * point[1];
            // preserve original error value in extras for further
            // filtering
            series.push([ x, y, [ y - variance, y + variance, point[1] ] ]);
          } else {
            series.push([ x, y, [ y, y, y ] ]);
          }
        } else {
          series.push([ x, null, [ null, null, null ] ]);
        }
      }
      return series;
    };

    /** @inheritDoc */
    ErrorBarsHandler.prototype.rollingAverage =
        function(originalData, rollPeriod, options, i) {
      rollPeriod = Math.min(rollPeriod, originalData.length);
      var rollingData = [];
      const seriesLabel = options.get("labels")[i];
      const sigma = options.getForSeries("sigma", seriesLabel);

      var i, j, y, v, sum, num_ok, stddev, variance, value;

      // Calculate the rolling average for the first rollPeriod - 1 points
      // where there is not enough data to roll over the full number of points
      for (i = 0; i < originalData.length; i++) {
        sum = 0;
        variance = 0;
        num_ok = 0;
        for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
          y = originalData[j][1];
          if (y === null || isNaN(y))
            continue;
          num_ok++;
          sum += y;
          variance += Math.pow(originalData[j][2][2], 2);
        }
        if (num_ok) {
          stddev = Math.sqrt(variance) / num_ok;
          value = sum / num_ok;
          rollingData[i] = [ originalData[i][0], value,
              [value - sigma * stddev, value + sigma * stddev] ];
        } else {
          // This explicitly preserves NaNs to aid with "independent
          // series".
          // See testRollingAveragePreservesNaNs.
          v = (rollPeriod == 1) ? originalData[i][1] : null;
          rollingData[i] = [ originalData[i][0], v, [ v, v ] ];
        }
      }

      return rollingData;
    };

    /**
     * @license
     * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * @constructor
     * @extends Dygraph.DataHandlers.BarsHandler
     */
    var CustomBarsHandler = function() {
    };

    CustomBarsHandler.prototype = new BarsHandler();

    /** @inheritDoc */
    CustomBarsHandler.prototype.extractSeries = function(rawData, i, options) {
      // TODO(danvk): pre-allocate series here.
      var series = [];
      var x, y, point;
      const seriesLabel = options.get("labels")[i];
      const logScale = options.getForSeries("logscale", seriesLabel);
      for ( var j = 0; j < rawData.length; j++) {
        x = rawData[j][0];
        point = rawData[j][i];
        if (logScale && point !== null) {
          // On the log scale, points less than zero do not exist.
          // This will create a gap in the chart.
          if (point[0] <= 0 || point[1] <= 0 || point[2] <= 0) {
            point = null;
          }
        }
        // Extract to the unified data format.
        if (point !== null) {
          y = point[1];
          if (y !== null && !isNaN(y)) {
            series.push([ x, y, [ point[0], point[2] ] ]);
          } else {
            series.push([ x, y, [ y, y ] ]);
          }
        } else {
          series.push([ x, null, [ null, null ] ]);
        }
      }
      return series;
    };

    /** @inheritDoc */
    CustomBarsHandler.prototype.rollingAverage =
        function(originalData, rollPeriod, options, i) {
      rollPeriod = Math.min(rollPeriod, originalData.length);
      var rollingData = [];
      var y, low, high, mid,count, i, extremes;

      low = 0;
      mid = 0;
      high = 0;
      count = 0;
      for (i = 0; i < originalData.length; i++) {
        y = originalData[i][1];
        extremes = originalData[i][2];
        rollingData[i] = originalData[i];

        if (y !== null && !isNaN(y)) {
          low += extremes[0];
          mid += y;
          high += extremes[1];
          count += 1;
        }
        if (i - rollPeriod >= 0) {
          var prev = originalData[i - rollPeriod];
          if (prev[1] !== null && !isNaN(prev[1])) {
            low -= prev[2][0];
            mid -= prev[1];
            high -= prev[2][1];
            count -= 1;
          }
        }
        if (count) {
          rollingData[i] = [
              originalData[i][0],
              1.0 * mid / count,
              [ 1.0 * low / count,
                1.0 * high / count ] ];
        } else {
          rollingData[i] = [ originalData[i][0], null, [ null, null ] ];
        }
      }

      return rollingData;
    };

    /**
     * @license
     * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * @extends DefaultHandler
     * @constructor
     */
    var DefaultFractionHandler = function() {
    };

    DefaultFractionHandler.prototype = new DefaultHandler();

    DefaultFractionHandler.prototype.extractSeries = function(rawData, i, options) {
      // TODO(danvk): pre-allocate series here.
      var series = [];
      var x, y, point, num, den, value;
      var mult = 100.0;
      const seriesLabel = options.get("labels")[i];
      const logScale = options.getForSeries("logscale", seriesLabel);
      for ( var j = 0; j < rawData.length; j++) {
        x = rawData[j][0];
        point = rawData[j][i];
        if (logScale && point !== null) {
          // On the log scale, points less than zero do not exist.
          // This will create a gap in the chart.
          if (point[0] <= 0 || point[1] <= 0) {
            point = null;
          }
        }
        // Extract to the unified data format.
        if (point !== null) {
          num = point[0];
          den = point[1];
          if (num !== null && !isNaN(num)) {
            value = den ? num / den : 0.0;
            y = mult * value;
            // preserve original values in extras for further filtering
            series.push([ x, y, [ num, den ] ]);
          } else {
            series.push([ x, num, [ num, den ] ]);
          }
        } else {
          series.push([ x, null, [ null, null ] ]);
        }
      }
      return series;
    };

    DefaultFractionHandler.prototype.rollingAverage = function(originalData, rollPeriod,
        options, i) {
      rollPeriod = Math.min(rollPeriod, originalData.length);
      var rollingData = [];

      var i;
      var num = 0;
      var den = 0; // numerator/denominator
      var mult = 100.0;
      for (i = 0; i < originalData.length; i++) {
        num += originalData[i][2][0];
        den += originalData[i][2][1];
        if (i - rollPeriod >= 0) {
          num -= originalData[i - rollPeriod][2][0];
          den -= originalData[i - rollPeriod][2][1];
        }

        var date = originalData[i][0];
        var value = den ? num / den : 0.0;
        rollingData[i] = [ date, mult * value ];
      }

      return rollingData;
    };

    /**
     * @license
     * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * @constructor
     * @extends Dygraph.DataHandlers.BarsHandler
     */
    var FractionsBarsHandler = function() {
    };

    FractionsBarsHandler.prototype = new BarsHandler();

    /** @inheritDoc */
    FractionsBarsHandler.prototype.extractSeries = function(rawData, i, options) {
      // TODO(danvk): pre-allocate series here.
      var series = [];
      var x, y, point, num, den, value, stddev, variance;
      var mult = 100.0;
      const seriesLabel = options.get("labels")[i];
      const logScale = options.getForSeries("logscale", seriesLabel);
      const sigma = options.getForSeries("sigma", seriesLabel);
      for ( var j = 0; j < rawData.length; j++) {
        x = rawData[j][0];
        point = rawData[j][i];
        if (logScale && point !== null) {
          // On the log scale, points less than zero do not exist.
          // This will create a gap in the chart.
          if (point[0] <= 0 || point[1] <= 0) {
            point = null;
          }
        }
        // Extract to the unified data format.
        if (point !== null) {
          num = point[0];
          den = point[1];
          if (num !== null && !isNaN(num)) {
            value = den ? num / den : 0.0;
            stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
            variance = mult * stddev;
            y = mult * value;
            // preserve original values in extras for further filtering
            series.push([ x, y, [ y - variance, y + variance, num, den ] ]);
          } else {
            series.push([ x, num, [ num, num, num, den ] ]);
          }
        } else {
          series.push([ x, null, [ null, null, null, null ] ]);
        }
      }
      return series;
    };

    /** @inheritDoc */
    FractionsBarsHandler.prototype.rollingAverage =
        function(originalData, rollPeriod, options, i) {
      rollPeriod = Math.min(rollPeriod, originalData.length);
      var rollingData = [];
      const seriesLabel = options.get("labels")[i];
      const sigma = options.getForSeries("sigma", seriesLabel);
      const wilsonInterval = options.getForSeries("wilsonInterval", seriesLabel);

      var low, high, i, stddev;
      var num = 0;
      var den = 0; // numerator/denominator
      var mult = 100.0;
      for (i = 0; i < originalData.length; i++) {
        num += originalData[i][2][2];
        den += originalData[i][2][3];
        if (i - rollPeriod >= 0) {
          num -= originalData[i - rollPeriod][2][2];
          den -= originalData[i - rollPeriod][2][3];
        }

        var date = originalData[i][0];
        var value = den ? num / den : 0.0;
        if (wilsonInterval) {
          // For more details on this confidence interval, see:
          // https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval
          if (den) {
            var p = value < 0 ? 0 : value, n = den;
            var pm = sigma * Math.sqrt(p * (1 - p) / n + sigma * sigma / (4 * n * n));
            var denom = 1 + sigma * sigma / den;
            low = (p + sigma * sigma / (2 * den) - pm) / denom;
            high = (p + sigma * sigma / (2 * den) + pm) / denom;
            rollingData[i] = [ date, p * mult,
                [ low * mult, high * mult ] ];
          } else {
            rollingData[i] = [ date, 0, [ 0, 0 ] ];
          }
        } else {
          stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
          rollingData[i] = [ date, mult * value,
                             [ mult * (value - stddev), mult * (value + stddev) ] ];
        }
      }

      return rollingData;
    };

    /**
     * @license
     * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
    Current bits of jankiness:
    - Uses dygraph.layout_ to get the parsed annotations.
    - Uses dygraph.plotter_.area

    It would be nice if the plugin didn't require so much special support inside
    the core dygraphs classes, but annotations involve quite a bit of parsing and
    layout.

    TODO(danvk): cache DOM elements.
    */

    var annotations = function() {
      this.annotations_ = [];
    };

    annotations.prototype.toString = function() {
      return "Annotations Plugin";
    };

    annotations.prototype.activate = function(g) {
      return {
        clearChart: this.clearChart,
        didDrawChart: this.didDrawChart
      };
    };

    annotations.prototype.detachLabels = function() {
      for (var i = 0; i < this.annotations_.length; i++) {
        var a = this.annotations_[i];
        if (a.parentNode) a.parentNode.removeChild(a);
        this.annotations_[i] = null;
      }
      this.annotations_ = [];
    };

    annotations.prototype.clearChart = function(e) {
      this.detachLabels();
    };

    annotations.prototype.didDrawChart = function(e) {
      var g = e.dygraph;

      // Early out in the (common) case of zero annotations.
      var points = g.layout_.annotated_points;
      if (!points || points.length === 0) return;

      var containerDiv = e.canvas.parentNode;

      var bindEvt = function(eventName, classEventName, pt) {
        return function(annotation_event) {
          var a = pt.annotation;
          if (a.hasOwnProperty(eventName)) {
            a[eventName](a, pt, g, annotation_event);
          } else if (g.getOption(classEventName)) {
            g.getOption(classEventName)(a, pt, g, annotation_event );
          }
        };
      };

      // Add the annotations one-by-one.
      var area = e.dygraph.getArea();

      // x-coord to sum of previous annotation's heights (used for stacking).
      var xToUsedHeight = {};

      for (var i = 0; i < points.length; i++) {
        var p = points[i];
        if (p.canvasx < area.x || p.canvasx > area.x + area.w ||
            p.canvasy < area.y || p.canvasy > area.y + area.h) {
          continue;
        }

        var a = p.annotation;
        var tick_height = 6;
        if (a.hasOwnProperty("tickHeight")) {
          tick_height = a.tickHeight;
        }

        // TODO: deprecate axisLabelFontSize in favor of CSS
        var div = document.createElement("div");
        div.style['fontSize'] = g.getOption('axisLabelFontSize') + "px";
        var className = 'dygraph-annotation';
        if (!a.hasOwnProperty('icon')) {
          // camelCase class names are deprecated.
          className += ' dygraphDefaultAnnotation dygraph-default-annotation';
        }
        if (a.hasOwnProperty('cssClass')) {
          className += " " + a.cssClass;
        }
        div.className = className;

        var width = a.hasOwnProperty('width') ? a.width : 16;
        var height = a.hasOwnProperty('height') ? a.height : 16;
        if (a.hasOwnProperty('icon')) {
          var img = document.createElement("img");
          img.src = a.icon;
          img.width = width;
          img.height = height;
          div.appendChild(img);
        } else if (p.annotation.hasOwnProperty('shortText')) {
          div.appendChild(document.createTextNode(p.annotation.shortText));
        }
        var left = p.canvasx - width / 2;
        div.style.left = left + "px";
        var divTop = 0;
        if (a.attachAtBottom) {
          var y = (area.y + area.h - height - tick_height);
          if (xToUsedHeight[left]) {
            y -= xToUsedHeight[left];
          } else {
            xToUsedHeight[left] = 0;
          }
          xToUsedHeight[left] += (tick_height + height);
          divTop = y;
        } else {
          divTop = p.canvasy - height - tick_height;
        }
        div.style.top = divTop + "px";
        div.style.width = width + "px";
        div.style.height = height + "px";
        div.title = p.annotation.text;
        div.style.color = g.colorsMap_[p.name];
        div.style.borderColor = g.colorsMap_[p.name];
        a.div = div;

        g.addAndTrackEvent(div, 'click',
            bindEvt('clickHandler', 'annotationClickHandler', p));
        g.addAndTrackEvent(div, 'mouseover',
            bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p));
        g.addAndTrackEvent(div, 'mouseout',
            bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p));
        g.addAndTrackEvent(div, 'dblclick',
            bindEvt('dblClickHandler', 'annotationDblClickHandler', p));

        containerDiv.appendChild(div);
        this.annotations_.push(div);

        var ctx = e.drawingContext;
        ctx.save();
        ctx.strokeStyle = a.hasOwnProperty('tickColor') ? a.tickColor : g.colorsMap_[p.name];
        ctx.lineWidth = a.hasOwnProperty('tickWidth') ? a.tickWidth : g.getOption('strokeWidth');
        ctx.beginPath();
        if (!a.attachAtBottom) {
          ctx.moveTo(p.canvasx, p.canvasy);
          ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height);
        } else {
          var y = divTop + height;
          ctx.moveTo(p.canvasx, y);
          ctx.lineTo(p.canvasx, y + tick_height);
        }
        ctx.closePath();
        ctx.stroke();
        ctx.restore();
      }
    };

    annotations.prototype.destroy = function() {
      this.detachLabels();
    };

    /**
     * @license
     * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * Draws the axes. This includes the labels on the x- and y-axes, as well
     * as the tick marks on the axes.
     * It does _not_ draw the grid lines which span the entire chart.
     */
    var axes = function() {
      this.xlabels_ = [];
      this.ylabels_ = [];
    };

    axes.prototype.toString = function() {
      return 'Axes Plugin';
    };

    axes.prototype.activate = function(g) {
      return {
        layout: this.layout,
        clearChart: this.clearChart,
        willDrawChart: this.willDrawChart
      };
    };

    axes.prototype.layout = function(e) {
      var g = e.dygraph;

      if (g.getOptionForAxis('drawAxis', 'y')) {
        var w = g.getOptionForAxis('axisLabelWidth', 'y') + 2 * g.getOptionForAxis('axisTickSize', 'y');
        e.reserveSpaceLeft(w);
      }

      if (g.getOptionForAxis('drawAxis', 'x')) {
        var h;
        // NOTE: I think this is probably broken now, since g.getOption() now
        // hits the dictionary. (That is, g.getOption('xAxisHeight') now always
        // has a value.)
        if (g.getOption('xAxisHeight')) {
          h = g.getOption('xAxisHeight');
        } else {
          h = g.getOptionForAxis('axisLabelFontSize', 'x') + 2 * g.getOptionForAxis('axisTickSize', 'x');
        }
        e.reserveSpaceBottom(h);
      }

      if (g.numAxes() == 2) {
        if (g.getOptionForAxis('drawAxis', 'y2')) {
          var w = g.getOptionForAxis('axisLabelWidth', 'y2') + 2 * g.getOptionForAxis('axisTickSize', 'y2');
          e.reserveSpaceRight(w);
        }
      } else if (g.numAxes() > 2) {
        g.error('Only two y-axes are supported at this time. (Trying ' +
                'to use ' + g.numAxes() + ')');
      }
    };

    axes.prototype.detachLabels = function() {
      function removeArray(ary) {
        for (var i = 0; i < ary.length; i++) {
          var el = ary[i];
          if (el.parentNode) el.parentNode.removeChild(el);
        }
      }

      removeArray(this.xlabels_);
      removeArray(this.ylabels_);
      this.xlabels_ = [];
      this.ylabels_ = [];
    };

    axes.prototype.clearChart = function(e) {
      this.detachLabels();
    };

    axes.prototype.willDrawChart = function(e) {
      var g = e.dygraph;

      if (!g.getOptionForAxis('drawAxis', 'x') &&
          !g.getOptionForAxis('drawAxis', 'y') &&
          !g.getOptionForAxis('drawAxis', 'y2')) {
        return;
      }

      // Round pixels to half-integer boundaries for crisper drawing.
      function halfUp(x)  { return Math.round(x) + 0.5; }
      function halfDown(y){ return Math.round(y) - 0.5; }

      var context = e.drawingContext;
      var containerDiv = e.canvas.parentNode;
      var canvasWidth = g.width_;  // e.canvas.width is affected by pixel ratio.
      var canvasHeight = g.height_;

      var label, x, y;

      var makeLabelStyle = function(axis) {
        return {
          position: 'absolute',
          fontSize: g.getOptionForAxis('axisLabelFontSize', axis) + 'px',
          width: g.getOptionForAxis('axisLabelWidth', axis) + 'px',
        };
      };

      var labelStyles = {
        x: makeLabelStyle('x'),
        y: makeLabelStyle('y'),
        y2: makeLabelStyle('y2')
      };

      var makeDiv = function(txt, axis, prec_axis) {
        /*
         * This seems to be called with the following three sets of axis/prec_axis:
         * x: undefined
         * y: y1
         * y: y2
         */
        var div = document.createElement('div');
        var labelStyle = labelStyles[prec_axis == 'y2' ? 'y2' : axis];
        update(div.style, labelStyle);
        // TODO: combine outer & inner divs
        var inner_div = document.createElement('div');
        inner_div.className = 'dygraph-axis-label' +
                              ' dygraph-axis-label-' + axis +
                              (prec_axis ? ' dygraph-axis-label-' + prec_axis : '');
        inner_div.innerHTML = txt;
        div.appendChild(inner_div);
        return div;
      };

      // axis lines
      context.save();

      var layout = g.layout_;
      var area = e.dygraph.plotter_.area;

      // Helper for repeated axis-option accesses.
      var makeOptionGetter = function(axis) {
        return function(option) {
          return g.getOptionForAxis(option, axis);
        };
      };

      const that = this;

      if (g.getOptionForAxis('drawAxis', 'y') ||
          (g.numAxes() == 2 && g.getOptionForAxis('drawAxis', 'y2'))) {
        if (layout.yticks && layout.yticks.length > 0) {
          var num_axes = g.numAxes();
          var getOptions = [makeOptionGetter('y'), makeOptionGetter('y2')];
          layout.yticks.forEach(function (tick) {
            if (tick.label === undefined) return;  // this tick only has a grid line.
            x = area.x;
            var prec_axis = 'y1';
            var getAxisOption = getOptions[0];
            if (tick.axis == 1) {  // right-side y-axis
              x = area.x + area.w;
              prec_axis = 'y2';
              getAxisOption = getOptions[1];
            }
            if (!getAxisOption('drawAxis')) return;
            var fontSize = getAxisOption('axisLabelFontSize');
            y = area.y + tick.pos * area.h;

            /* Tick marks are currently clipped, so don't bother drawing them.
            context.beginPath();
            context.moveTo(halfUp(x), halfDown(y));
            context.lineTo(halfUp(x - sgn * that.attr_('axisTickSize')), halfDown(y));
            context.closePath();
            context.stroke();
            */

            label = makeDiv(tick.label, 'y', num_axes == 2 ? prec_axis : null);
            var top = (y - fontSize / 2);
            if (top < 0) top = 0;

            if (top + fontSize + 3 > canvasHeight) {
              label.style.bottom = '0';
            } else {
              // The lowest tick on the y-axis often overlaps with the leftmost
              // tick on the x-axis. Shift the bottom tick up a little bit to
              // compensate if necessary.
              label.style.top = Math.min(top, canvasHeight - (2 * fontSize)) + 'px';
            }
            // TODO: replace these with css classes?
            if (tick.axis === 0) {
              label.style.left = (area.x - getAxisOption('axisLabelWidth') - getAxisOption('axisTickSize')) + 'px';
              label.style.textAlign = 'right';
            } else if (tick.axis == 1) {
              label.style.left = (area.x + area.w +
                                  getAxisOption('axisTickSize')) + 'px';
              label.style.textAlign = 'left';
            }
            label.style.width = getAxisOption('axisLabelWidth') + 'px';
            containerDiv.appendChild(label);
            that.ylabels_.push(label);
          });
        }

        // draw a vertical line on the left to separate the chart from the labels.
        var axisX;
        if (g.getOption('drawAxesAtZero')) {
          var r = g.toPercentXCoord(0);
          if (r > 1 || r < 0 || isNaN(r)) r = 0;
          axisX = halfUp(area.x + r * area.w);
        } else {
          axisX = halfUp(area.x);
        }

        context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y');
        context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y');

        context.beginPath();
        context.moveTo(axisX, halfDown(area.y));
        context.lineTo(axisX, halfDown(area.y + area.h));
        context.closePath();
        context.stroke();

        // if there's a secondary y-axis, draw a vertical line for that, too.
        if (g.numAxes() == 2 && g.getOptionForAxis('drawAxis', 'y2')) {
          context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y2');
          context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y2');
          context.beginPath();
          context.moveTo(halfDown(area.x + area.w), halfDown(area.y));
          context.lineTo(halfDown(area.x + area.w), halfDown(area.y + area.h));
          context.closePath();
          context.stroke();
        }
      }

      if (g.getOptionForAxis('drawAxis', 'x')) {
        if (layout.xticks) {
          var getAxisOption = makeOptionGetter('x');
          layout.xticks.forEach(function (tick) {
            if (tick.label === undefined) return;  // this tick only has a grid line.
            x = area.x + tick.pos * area.w;
            y = area.y + area.h;

            /* Tick marks are currently clipped, so don't bother drawing them.
            context.beginPath();
            context.moveTo(halfUp(x), halfDown(y));
            context.lineTo(halfUp(x), halfDown(y + that.attr_('axisTickSize')));
            context.closePath();
            context.stroke();
            */

            label = makeDiv(tick.label, 'x');
            label.style.textAlign = 'center';
            label.style.top = (y + getAxisOption('axisTickSize')) + 'px';

            var left = (x - getAxisOption('axisLabelWidth')/2);
            if (left + getAxisOption('axisLabelWidth') > canvasWidth) {
              left = canvasWidth - getAxisOption('axisLabelWidth');
              label.style.textAlign = 'right';
            }
            if (left < 0) {
              left = 0;
              label.style.textAlign = 'left';
            }

            label.style.left = left + 'px';
            label.style.width = getAxisOption('axisLabelWidth') + 'px';
            containerDiv.appendChild(label);
            that.xlabels_.push(label);
          });
        }

        context.strokeStyle = g.getOptionForAxis('axisLineColor', 'x');
        context.lineWidth = g.getOptionForAxis('axisLineWidth', 'x');
        context.beginPath();
        var axisY;
        if (g.getOption('drawAxesAtZero')) {
          var r = g.toPercentYCoord(0, 0);
          if (r > 1 || r < 0) r = 1;
          axisY = halfDown(area.y + r * area.h);
        } else {
          axisY = halfDown(area.y + area.h);
        }
        context.moveTo(halfUp(area.x), axisY);
        context.lineTo(halfUp(area.x + area.w), axisY);
        context.closePath();
        context.stroke();
      }

      context.restore();
    };

    /**
     * @license
     * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    // TODO(danvk): move chart label options out of dygraphs and into the plugin.
    // TODO(danvk): only tear down & rebuild the DIVs when it's necessary.

    var chart_labels = function() {
      this.title_div_ = null;
      this.xlabel_div_ = null;
      this.ylabel_div_ = null;
      this.y2label_div_ = null;
    };

    chart_labels.prototype.toString = function() {
      return "ChartLabels Plugin";
    };

    chart_labels.prototype.activate = function(g) {
      return {
        layout: this.layout,
        // clearChart: this.clearChart,
        didDrawChart: this.didDrawChart
      };
    };

    // QUESTION: should there be a plugin-utils.js?
    var createDivInRect = function(r) {
      var div = document.createElement('div');
      div.style.position = 'absolute';
      div.style.left = r.x + 'px';
      div.style.top = r.y + 'px';
      div.style.width = r.w + 'px';
      div.style.height = r.h + 'px';
      return div;
    };

    // Detach and null out any existing nodes.
    chart_labels.prototype.detachLabels_ = function() {
      var els = [ this.title_div_,
                  this.xlabel_div_,
                  this.ylabel_div_,
                  this.y2label_div_ ];
      for (var i = 0; i < els.length; i++) {
        var el = els[i];
        if (!el) continue;
        if (el.parentNode) el.parentNode.removeChild(el);
      }

      this.title_div_ = null;
      this.xlabel_div_ = null;
      this.ylabel_div_ = null;
      this.y2label_div_ = null;
    };

    var createRotatedDiv = function(g, box, axis, classes, html) {
      // TODO(danvk): is this outer div actually necessary?
      var div = document.createElement("div");
      div.style.position = 'absolute';
      if (axis == 1) {
        // NOTE: this is cheating. Should be positioned relative to the box.
        div.style.left = '0px';
      } else {
        div.style.left = box.x + 'px';
      }
      div.style.top = box.y + 'px';
      div.style.width = box.w + 'px';
      div.style.height = box.h + 'px';
      div.style.fontSize = (g.getOption('yLabelWidth') - 2) + 'px';

      var inner_div = document.createElement("div");
      inner_div.style.position = 'absolute';
      inner_div.style.width = box.h + 'px';
      inner_div.style.height = box.w + 'px';
      inner_div.style.top = (box.h / 2 - box.w / 2) + 'px';
      inner_div.style.left = (box.w / 2 - box.h / 2) + 'px';
      // TODO: combine inner_div and class_div.
      inner_div.className = 'dygraph-label-rotate-' + (axis == 1 ? 'right' : 'left');

      var class_div = document.createElement("div");
      class_div.className = classes;
      class_div.innerHTML = html;

      inner_div.appendChild(class_div);
      div.appendChild(inner_div);
      return div;
    };

    chart_labels.prototype.layout = function(e) {
      this.detachLabels_();

      var g = e.dygraph;
      var div = e.chart_div;
      if (g.getOption('title')) {
        // QUESTION: should this return an absolutely-positioned div instead?
        var title_rect = e.reserveSpaceTop(g.getOption('titleHeight'));
        this.title_div_ = createDivInRect(title_rect);
        this.title_div_.style.fontSize = (g.getOption('titleHeight') - 8) + 'px';

        var class_div = document.createElement("div");
        class_div.className = 'dygraph-label dygraph-title';
        class_div.innerHTML = g.getOption('title');
        this.title_div_.appendChild(class_div);
        div.appendChild(this.title_div_);
      }

      if (g.getOption('xlabel')) {
        var x_rect = e.reserveSpaceBottom(g.getOption('xLabelHeight'));
        this.xlabel_div_ = createDivInRect(x_rect);
        this.xlabel_div_.style.fontSize = (g.getOption('xLabelHeight') - 2) + 'px';

        var class_div = document.createElement("div");
        class_div.className = 'dygraph-label dygraph-xlabel';
        class_div.innerHTML = g.getOption('xlabel');
        this.xlabel_div_.appendChild(class_div);
        div.appendChild(this.xlabel_div_);
      }

      if (g.getOption('ylabel')) {
        // It would make sense to shift the chart here to make room for the y-axis
        // label, but the default yAxisLabelWidth is large enough that this results
        // in overly-padded charts. The y-axis label should fit fine. If it
        // doesn't, the yAxisLabelWidth option can be increased.
        var y_rect = e.reserveSpaceLeft(0);

        this.ylabel_div_ = createRotatedDiv(
            g, y_rect,
            1,  // primary (left) y-axis
            'dygraph-label dygraph-ylabel',
            g.getOption('ylabel'));
        div.appendChild(this.ylabel_div_);
      }

      if (g.getOption('y2label') && g.numAxes() == 2) {
        // same logic applies here as for ylabel.
        var y2_rect = e.reserveSpaceRight(0);
        this.y2label_div_ = createRotatedDiv(
            g, y2_rect,
            2,  // secondary (right) y-axis
            'dygraph-label dygraph-y2label',
            g.getOption('y2label'));
        div.appendChild(this.y2label_div_);
      }
    };

    chart_labels.prototype.didDrawChart = function(e) {
      var g = e.dygraph;
      if (this.title_div_) {
        this.title_div_.children[0].innerHTML = g.getOption('title');
      }
      if (this.xlabel_div_) {
        this.xlabel_div_.children[0].innerHTML = g.getOption('xlabel');
      }
      if (this.ylabel_div_) {
        this.ylabel_div_.children[0].children[0].innerHTML = g.getOption('ylabel');
      }
      if (this.y2label_div_) {
        this.y2label_div_.children[0].children[0].innerHTML = g.getOption('y2label');
      }
    };

    chart_labels.prototype.clearChart = function() {
    };

    chart_labels.prototype.destroy = function() {
      this.detachLabels_();
    };

    /**
     * @license
     * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * Draws the gridlines, i.e. the gray horizontal & vertical lines running the
     * length of the chart.
     *
     * @constructor
     */
    var grid = function() {
    };

    grid.prototype.toString = function() {
      return "Gridline Plugin";
    };

    grid.prototype.activate = function(g) {
      return {
        willDrawChart: this.willDrawChart
      };
    };

    grid.prototype.willDrawChart = function(e) {
      // Draw the new X/Y grid. Lines appear crisper when pixels are rounded to
      // half-integers. This prevents them from drawing in two rows/cols.
      var g = e.dygraph;
      var ctx = e.drawingContext;
      var layout = g.layout_;
      var area = e.dygraph.plotter_.area;

      function halfUp(x)  { return Math.round(x) + 0.5; }
      function halfDown(y){ return Math.round(y) - 0.5; }

      var x, y, i, ticks;
      if (g.getOptionForAxis('drawGrid', 'y')) {
        var axes = ["y", "y2"];
        var strokeStyles = [], lineWidths = [], drawGrid = [], stroking = [], strokePattern = [];
        for (var i = 0; i < axes.length; i++) {
          drawGrid[i] = g.getOptionForAxis('drawGrid', axes[i]);
          if (drawGrid[i]) {
            strokeStyles[i] = g.getOptionForAxis('gridLineColor', axes[i]);
            lineWidths[i] = g.getOptionForAxis('gridLineWidth', axes[i]);
            strokePattern[i] = g.getOptionForAxis('gridLinePattern', axes[i]);
            stroking[i] = strokePattern[i] && (strokePattern[i].length >= 2);
          }
        }
        ticks = layout.yticks;
        ctx.save();
        // draw grids for the different y axes
        ticks.forEach(tick => {
          if (!tick.has_tick) return;
          var axis = tick.axis;
          if (drawGrid[axis]) {
            ctx.save();
            if (stroking[axis]) {
              if (ctx.setLineDash) ctx.setLineDash(strokePattern[axis]);
            }
            ctx.strokeStyle = strokeStyles[axis];
            ctx.lineWidth = lineWidths[axis];

            x = halfUp(area.x);
            y = halfDown(area.y + tick.pos * area.h);
            ctx.beginPath();
            ctx.moveTo(x, y);
            ctx.lineTo(x + area.w, y);
            ctx.stroke();

            ctx.restore();
          }
        });
        ctx.restore();
      }

      // draw grid for x axis
      if (g.getOptionForAxis('drawGrid', 'x')) {
        ticks = layout.xticks;
        ctx.save();
        var strokePattern = g.getOptionForAxis('gridLinePattern', 'x');
        var stroking = strokePattern && (strokePattern.length >= 2);
        if (stroking) {
          if (ctx.setLineDash) ctx.setLineDash(strokePattern);
        }
        ctx.strokeStyle = g.getOptionForAxis('gridLineColor', 'x');
        ctx.lineWidth = g.getOptionForAxis('gridLineWidth', 'x');
        ticks.forEach(tick => {
          if (!tick.has_tick) return;
          x = halfUp(area.x + tick.pos * area.w);
          y = halfDown(area.y + area.h);
          ctx.beginPath();
          ctx.moveTo(x, y);
          ctx.lineTo(x, area.y);
          ctx.stroke();
        });
        if (stroking) {
          if (ctx.setLineDash) ctx.setLineDash([]);
        }
        ctx.restore();
      }
    };

    grid.prototype.destroy = function() {
    };

    /**
     * @license
     * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * Creates the legend, which appears when the user hovers over the chart.
     * The legend can be either a user-specified or generated div.
     *
     * @constructor
     */
    var Legend = function() {
      this.legend_div_ = null;
      this.is_generated_div_ = false;  // do we own this div, or was it user-specified?
    };

    Legend.prototype.toString = function() {
      return "Legend Plugin";
    };

    /**
     * This is called during the dygraph constructor, after options have been set
     * but before the data is available.
     *
     * Proper tasks to do here include:
     * - Reading your own options
     * - DOM manipulation
     * - Registering event listeners
     *
     * @param {Dygraph} g Graph instance.
     * @return {object.<string, function(ev)>} Mapping of event names to callbacks.
     */
    Legend.prototype.activate = function(g) {
      var div;

      var userLabelsDiv = g.getOption('labelsDiv');
      if (userLabelsDiv && null !== userLabelsDiv) {
        if (typeof(userLabelsDiv) == "string" || userLabelsDiv instanceof String) {
          div = document.getElementById(userLabelsDiv);
        } else {
          div = userLabelsDiv;
        }
      } else {
        div = document.createElement("div");
        div.className = "dygraph-legend";
        // TODO(danvk): come up with a cleaner way to expose this.
        g.graphDiv.appendChild(div);
        this.is_generated_div_ = true;
      }

      this.legend_div_ = div;
      this.one_em_width_ = 10;  // just a guess, will be updated.

      return {
        select: this.select,
        deselect: this.deselect,
        // TODO(danvk): rethink the name "predraw" before we commit to it in any API.
        predraw: this.predraw,
        didDrawChart: this.didDrawChart
      };
    };

    // Needed for dashed lines.
    var calculateEmWidthInDiv = function(div) {
      var sizeSpan = document.createElement('span');
      sizeSpan.setAttribute('style', 'margin: 0; padding: 0 0 0 1em; border: 0;');
      div.appendChild(sizeSpan);
      var oneEmWidth=sizeSpan.offsetWidth;
      div.removeChild(sizeSpan);
      return oneEmWidth;
    };

    var escapeHTML = function(str) {
      return str.replace(/&/g, "&amp;").replace(/"/g, "&#34;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    };

    Legend.prototype.select = function(e) {
      var xValue = e.selectedX;
      var points = e.selectedPoints;
      var row = e.selectedRow;

      var legendMode = e.dygraph.getOption('legend');
      if (legendMode === 'never') {
        this.legend_div_.style.display = 'none';
        return;
      }

      var html = Legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_, row);
      if (html instanceof Node && html.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
        this.legend_div_.innerHTML = '';
        this.legend_div_.appendChild(html);
      } else
        this.legend_div_.innerHTML = html;
      // must be done now so offsetWidth isn’t 0…
      this.legend_div_.style.display = '';

      if (legendMode === 'follow') {
        // create floating legend div
        var area = e.dygraph.plotter_.area;
        var labelsDivWidth = this.legend_div_.offsetWidth;
        var yAxisLabelWidth = e.dygraph.getOptionForAxis('axisLabelWidth', 'y');
        // find the closest data point by checking the currently highlighted series,
        // or fall back to using the first data point available
        var highlightSeries = e.dygraph.getHighlightSeries();
        var point;
        if (highlightSeries) {
          point = points.find(p => p.name === highlightSeries);
          if (!point)
            point = points[0];
        } else
          point = points[0];
        // determine floating [left, top] coordinates of the legend div
        // within the plotter_ area
        // offset 50 px to the right and down from the first selection point
        // 50 px is guess based on mouse cursor size
        const followOffsetX = e.dygraph.getNumericOption('legendFollowOffsetX');
        const followOffsetY = e.dygraph.getNumericOption('legendFollowOffsetY');
        var leftLegend = point.x * area.w + followOffsetX;
        var topLegend  = point.y * area.h + followOffsetY;

        // if legend floats to end of the chart area, it flips to the other
        // side of the selection point
        if ((leftLegend + labelsDivWidth + 1) > area.w) {
          leftLegend = leftLegend - 2 * followOffsetX - labelsDivWidth - (yAxisLabelWidth - area.x);
        }

        this.legend_div_.style.left = yAxisLabelWidth + leftLegend + "px";
        this.legend_div_.style.top = topLegend + "px";
      } else if (legendMode === 'onmouseover' && this.is_generated_div_) {
        // synchronise this with Legend.prototype.predraw below
        var area = e.dygraph.plotter_.area;
        var labelsDivWidth = this.legend_div_.offsetWidth;
        this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px";
        this.legend_div_.style.top = area.y + "px";
      }
    };

    Legend.prototype.deselect = function(e) {
      var legendMode = e.dygraph.getOption('legend');
      if (legendMode !== 'always') {
        this.legend_div_.style.display = "none";
      }

      // Have to do this every time, since styles might have changed.
      var oneEmWidth = calculateEmWidthInDiv(this.legend_div_);
      this.one_em_width_ = oneEmWidth;

      var html = Legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth, null);
      if (html instanceof Node && html.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
        this.legend_div_.innerHTML = '';
        this.legend_div_.appendChild(html);
      } else
        this.legend_div_.innerHTML = html;
    };

    Legend.prototype.didDrawChart = function(e) {
      this.deselect(e);
    };

    // Right edge should be flush with the right edge of the charting area (which
    // may not be the same as the right edge of the div, if we have two y-axes).
    // TODO(danvk): is any of this really necessary? Could just set "right" in "activate".
    /**
     * Position the labels div so that:
     * - its right edge is flush with the right edge of the charting area
     * - its top edge is flush with the top edge of the charting area
     * @private
     */
    Legend.prototype.predraw = function(e) {
      // Don't touch a user-specified labelsDiv.
      if (!this.is_generated_div_) return;

      // TODO(danvk): only use real APIs for this.
      e.dygraph.graphDiv.appendChild(this.legend_div_);
      // synchronise this with Legend.prototype.select above
      var area = e.dygraph.plotter_.area;
      var labelsDivWidth = this.legend_div_.offsetWidth;
      this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px";
      this.legend_div_.style.top = area.y + "px";
    };

    /**
     * Called when dygraph.destroy() is called.
     * You should null out any references and detach any DOM elements.
     */
    Legend.prototype.destroy = function() {
      this.legend_div_ = null;
    };

    /**
     * Generates HTML for the legend which is displayed when hovering over the
     * chart. If no selected points are specified, a default legend is returned
     * (this may just be the empty string).
     * @param {number} x The x-value of the selected points.
     * @param {Object} sel_points List of selected points for the given
     *   x-value. Should have properties like 'name', 'yval' and 'canvasy'.
     * @param {number} oneEmWidth The pixel width for 1em in the legend. Only
     *   relevant when displaying a legend with no selection (i.e. {legend:
     *   'always'}) and with dashed lines.
     * @param {number} row The selected row index.
     * @private
     */
    Legend.generateLegendHTML = function(g, x, sel_points, oneEmWidth, row) {
      // Data about the selection to pass to legendFormatter
      var data = {
        dygraph: g,
        x: x,
        i: row,
        series: []
      };

      var labelToSeries = {};
      var labels = g.getLabels();
      if (labels) {
        for (var i = 1; i < labels.length; i++) {
          var series = g.getPropertiesForSeries(labels[i]);
          var strokePattern = g.getOption('strokePattern', labels[i]);
          var seriesData = {
            dashHTML: generateLegendDashHTML(strokePattern, series.color, oneEmWidth),
            label: labels[i],
            labelHTML: escapeHTML(labels[i]),
            isVisible: series.visible,
            color: series.color
          };

          data.series.push(seriesData);
          labelToSeries[labels[i]] = seriesData;
        }
      }

      if (typeof(x) !== 'undefined') {
        var xOptView = g.optionsViewForAxis_('x');
        var xvf = xOptView('valueFormatter');
        data.xHTML = xvf.call(g, x, xOptView, labels[0], g, row, 0);

        var yOptViews = [];
        var num_axes = g.numAxes();
        for (var i = 0; i < num_axes; i++) {
          // TODO(danvk): remove this use of a private API
          yOptViews[i] = g.optionsViewForAxis_('y' + (i ? 1 + i : ''));
        }

        var showZeros = g.getOption('labelsShowZeroValues');
        var highlightSeries = g.getHighlightSeries();
        for (i = 0; i < sel_points.length; i++) {
          var pt = sel_points[i];
          var seriesData = labelToSeries[pt.name];
          seriesData.y = pt.yval;

          if ((pt.yval === 0 && !showZeros) || isNaN(pt.canvasy)) {
            seriesData.isVisible = false;
            continue;
          }

          var series = g.getPropertiesForSeries(pt.name);
          var yOptView = yOptViews[series.axis - 1];
          var fmtFunc = yOptView('valueFormatter');
          var yHTML = fmtFunc.call(g, pt.yval, yOptView, pt.name, g, row, labels.indexOf(pt.name));

          update(seriesData, {yHTML});

          if (pt.name == highlightSeries) {
            seriesData.isHighlighted = true;
          }
        }
      }

      var formatter = (g.getOption('legendFormatter') || Legend.defaultFormatter);
      return formatter.call(g, data);
    };

    Legend.defaultFormatter = function(data) {
      var g = data.dygraph;

      // TODO(danvk): deprecate this option in place of {legend: 'never'}
      // XXX should this logic be in the formatter?
      if (g.getOption('showLabelsOnHighlight') !== true) return '';

      var sepLines = g.getOption('labelsSeparateLines');
      var html;

      if (typeof(data.x) === 'undefined') {
        // TODO: this check is duplicated in generateLegendHTML. Put it in one place.
        if (g.getOption('legend') != 'always') {
          return '';
        }

        html = '';
        for (var i = 0; i < data.series.length; i++) {
          var series = data.series[i];
          if (!series.isVisible) continue;

          if (html !== '') html += (sepLines ? '<br />' : ' ');
          html += `<span style='font-weight: bold; color: ${series.color};'>${series.dashHTML} ${series.labelHTML}</span>`;
        }
        return html;
      }

      html = data.xHTML + ':';
      for (var i = 0; i < data.series.length; i++) {
        var series = data.series[i];
        if (!series.y && !series.yHTML) continue;
        if (!series.isVisible) continue;
        if (sepLines) html += '<br>';
        var cls = series.isHighlighted ? ' class="highlight"' : '';
        html += `<span${cls}> <b><span style='color: ${series.color};'>${series.labelHTML}</span></b>:&#160;${series.yHTML}</span>`;
      }
      return html;
    };

    /**
     * Generates html for the "dash" displayed on the legend when using "legend: always".
     * In particular, this works for dashed lines with any stroke pattern. It will
     * try to scale the pattern to fit in 1em width. Or if small enough repeat the
     * pattern for 1em width.
     *
     * @param strokePattern The pattern
     * @param color The color of the series.
     * @param oneEmWidth The width in pixels of 1em in the legend.
     * @private
     */
    // TODO(danvk): cache the results of this
    function generateLegendDashHTML(strokePattern, color, oneEmWidth) {
      // Easy, common case: a solid line
      if (!strokePattern || strokePattern.length <= 1) {
        return `<div class="dygraph-legend-line" style="border-bottom-color: ${color};"></div>`;
      }

      var i, j, paddingLeft, marginRight;
      var strokePixelLength = 0, segmentLoop = 0;
      var normalizedPattern = [];
      var loop;

      // Compute the length of the pixels including the first segment twice,
      // since we repeat it.
      for (i = 0; i <= strokePattern.length; i++) {
        strokePixelLength += strokePattern[i%strokePattern.length];
      }

      // See if we can loop the pattern by itself at least twice.
      loop = Math.floor(oneEmWidth/(strokePixelLength-strokePattern[0]));
      if (loop > 1) {
        // This pattern fits at least two times, no scaling just convert to em;
        for (i = 0; i < strokePattern.length; i++) {
          normalizedPattern[i] = strokePattern[i]/oneEmWidth;
        }
        // Since we are repeating the pattern, we don't worry about repeating the
        // first segment in one draw.
        segmentLoop = normalizedPattern.length;
      } else {
        // If the pattern doesn't fit in the legend we scale it to fit.
        loop = 1;
        for (i = 0; i < strokePattern.length; i++) {
          normalizedPattern[i] = strokePattern[i]/strokePixelLength;
        }
        // For the scaled patterns we do redraw the first segment.
        segmentLoop = normalizedPattern.length+1;
      }

      // Now make the pattern.
      var dash = "";
      for (j = 0; j < loop; j++) {
        for (i = 0; i < segmentLoop; i+=2) {
          // The padding is the drawn segment.
          paddingLeft = normalizedPattern[i%normalizedPattern.length];
          if (i < strokePattern.length) {
            // The margin is the space segment.
            marginRight = normalizedPattern[(i+1)%normalizedPattern.length];
          } else {
            // The repeated first segment has no right margin.
            marginRight = 0;
          }
          dash += `<div class="dygraph-legend-dash" style="margin-right: ${marginRight}em; padding-left: ${paddingLeft}em;"></div>`;
        }
      }
      return dash;
    }

    /**
     * @license
     * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    var rangeSelector = function() {
      this.hasTouchInterface_ = typeof(TouchEvent) != 'undefined';
      this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion);
      this.interfaceCreated_ = false;
    };

    rangeSelector.prototype.toString = function() {
      return "RangeSelector Plugin";
    };

    rangeSelector.prototype.activate = function(dygraph) {
      this.dygraph_ = dygraph;
      if (this.getOption_('showRangeSelector')) {
        this.createInterface_();
      }
      return {
        layout: this.reserveSpace_,
        predraw: this.renderStaticLayer_,
        didDrawChart: this.renderInteractiveLayer_
      };
    };

    rangeSelector.prototype.destroy = function() {
      this.bgcanvas_ = null;
      this.fgcanvas_ = null;
      this.leftZoomHandle_ = null;
      this.rightZoomHandle_ = null;
    };

    //------------------------------------------------------------------
    // Private methods
    //------------------------------------------------------------------

    rangeSelector.prototype.getOption_ = function(name, opt_series) {
      return this.dygraph_.getOption(name, opt_series);
    };

    rangeSelector.prototype.setDefaultOption_ = function(name, value) {
      this.dygraph_.attrs_[name] = value;
    };

    /**
     * @private
     * Creates the range selector elements and adds them to the graph.
     */
    rangeSelector.prototype.createInterface_ = function() {
      this.createCanvases_();
      this.createZoomHandles_();
      this.initInteraction_();

      // Range selector and animatedZooms have a bad interaction. See issue 359.
      if (this.getOption_('animatedZooms')) {
        console.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.');
        this.dygraph_.updateOptions({animatedZooms: false}, true);
      }

      this.interfaceCreated_ = true;
      this.addToGraph_();
    };

    /**
     * @private
     * Adds the range selector to the graph.
     */
    rangeSelector.prototype.addToGraph_ = function() {
      var graphDiv = this.graphDiv_ = this.dygraph_.graphDiv;
      graphDiv.appendChild(this.bgcanvas_);
      graphDiv.appendChild(this.fgcanvas_);
      graphDiv.appendChild(this.leftZoomHandle_);
      graphDiv.appendChild(this.rightZoomHandle_);
    };

    /**
     * @private
     * Removes the range selector from the graph.
     */
    rangeSelector.prototype.removeFromGraph_ = function() {
      var graphDiv = this.graphDiv_;
      graphDiv.removeChild(this.bgcanvas_);
      graphDiv.removeChild(this.fgcanvas_);
      graphDiv.removeChild(this.leftZoomHandle_);
      graphDiv.removeChild(this.rightZoomHandle_);
      this.graphDiv_ = null;
    };

    /**
     * @private
     * Called by Layout to allow range selector to reserve its space.
     */
    rangeSelector.prototype.reserveSpace_ = function(e) {
      if (this.getOption_('showRangeSelector')) {
        e.reserveSpaceBottom(this.getOption_('rangeSelectorHeight') + 4);
      }
    };

    /**
     * @private
     * Renders the static portion of the range selector at the predraw stage.
     */
    rangeSelector.prototype.renderStaticLayer_ = function() {
      if (!this.updateVisibility_()) {
        return;
      }
      this.resize_();
      this.drawStaticLayer_();
    };

    /**
     * @private
     * Renders the interactive portion of the range selector after the chart has been drawn.
     */
    rangeSelector.prototype.renderInteractiveLayer_ = function() {
      if (!this.updateVisibility_() || this.isChangingRange_) {
        return;
      }
      this.placeZoomHandles_();
      this.drawInteractiveLayer_();
    };

    /**
     * @private
     * Check to see if the range selector is enabled/disabled and update visibility accordingly.
     */
    rangeSelector.prototype.updateVisibility_ = function() {
      var enabled = this.getOption_('showRangeSelector');
      if (enabled) {
        if (!this.interfaceCreated_) {
          this.createInterface_();
        } else if (!this.graphDiv_ || !this.graphDiv_.parentNode) {
          this.addToGraph_();
        }
      } else if (this.graphDiv_) {
        this.removeFromGraph_();
        var dygraph = this.dygraph_;
        setTimeout(function() { dygraph.width_ = 0; dygraph.resize(); }, 1);
      }
      return enabled;
    };

    /**
     * @private
     * Resizes the range selector.
     */
    rangeSelector.prototype.resize_ = function() {
      function setElementRect(canvas, context, rect, pixelRatioOption) {
        var canvasScale = pixelRatioOption || getContextPixelRatio(context);

        canvas.style.top = rect.y + 'px';
        canvas.style.left = rect.x + 'px';
        canvas.width = rect.w * canvasScale;
        canvas.height = rect.h * canvasScale;
        canvas.style.width = rect.w + 'px';
        canvas.style.height = rect.h + 'px';

        if(canvasScale != 1) {
          context.scale(canvasScale, canvasScale);
        }
      }

      var plotArea = this.dygraph_.layout_.getPlotArea();

      var xAxisLabelHeight = 0;
      if (this.dygraph_.getOptionForAxis('drawAxis', 'x')) {
        xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'));
      }
      this.canvasRect_ = {
        x: plotArea.x,
        y: plotArea.y + plotArea.h + xAxisLabelHeight + 4,
        w: plotArea.w,
        h: this.getOption_('rangeSelectorHeight')
      };

      var pixelRatioOption = this.dygraph_.getNumericOption('pixelRatio');
      setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_, pixelRatioOption);
      setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_, pixelRatioOption);
    };

    /**
     * @private
     * Creates the background and foreground canvases.
     */
    rangeSelector.prototype.createCanvases_ = function() {
      this.bgcanvas_ = createCanvas();
      this.bgcanvas_.className = 'dygraph-rangesel-bgcanvas';
      this.bgcanvas_.style.position = 'absolute';
      this.bgcanvas_.style.zIndex = 9;
      this.bgcanvas_ctx_ = getContext(this.bgcanvas_);

      this.fgcanvas_ = createCanvas();
      this.fgcanvas_.className = 'dygraph-rangesel-fgcanvas';
      this.fgcanvas_.style.position = 'absolute';
      this.fgcanvas_.style.zIndex = 9;
      this.fgcanvas_.style.cursor = 'default';
      this.fgcanvas_ctx_ = getContext(this.fgcanvas_);
    };

    /**
     * @private
     * Creates the zoom handle elements.
     */
    rangeSelector.prototype.createZoomHandles_ = function() {
      var img = new Image();
      img.className = 'dygraph-rangesel-zoomhandle';
      img.style.position = 'absolute';
      img.style.zIndex = 10;
      img.style.visibility = 'hidden'; // Initially hidden so they don't show up in the wrong place.
      img.style.cursor = 'col-resize';
      // TODO: change image to more options
      img.width = 9;
      img.height = 16;
      img.src = 'data:image/png;base64,' +
    'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAA' +
    'zwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENv' +
    'bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl' +
    '6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7s' +
    'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII=';

      if (this.isMobileDevice_) {
        img.width *= 2;
        img.height *= 2;
      }

      this.leftZoomHandle_ = img;
      this.rightZoomHandle_ = img.cloneNode(false);
    };

    /**
     * @private
     * Sets up the interaction for the range selector.
     */
    rangeSelector.prototype.initInteraction_ = function() {
      var self = this;
      var topElem = document;
      var clientXLast = 0;
      var handle = null;
      var isZooming = false;
      var isPanning = false;
      var dynamic = !this.isMobileDevice_;

      // We cover iframes during mouse interactions. See comments in
      // dygraph-utils.js for more info on why this is a good idea.
      var tarp = new IFrameTarp();

      // functions, defined below.  Defining them this way (rather than with
      // "function foo() {...}") makes JSHint happy.
      var toXDataWindow, onZoomStart, onZoom, onZoomEnd, doZoom, isMouseInPanZone,
          onPanStart, onPan, onPanEnd, doPan, onCanvasHover;

      // Touch event functions
      var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents;

      toXDataWindow = function(zoomHandleStatus) {
        var xDataLimits = self.dygraph_.xAxisExtremes();
        var fact = (xDataLimits[1] - xDataLimits[0])/self.canvasRect_.w;
        var xDataMin = xDataLimits[0] + (zoomHandleStatus.leftHandlePos - self.canvasRect_.x)*fact;
        var xDataMax = xDataLimits[0] + (zoomHandleStatus.rightHandlePos - self.canvasRect_.x)*fact;
        return [xDataMin, xDataMax];
      };

      onZoomStart = function(e) {
        cancelEvent(e);
        isZooming = true;
        clientXLast = e.clientX;
        handle = e.target ? e.target : e.srcElement;
        if (e.type === 'mousedown' || e.type === 'dragstart') {
          // These events are removed manually.
          addEvent(topElem, 'mousemove', onZoom);
          addEvent(topElem, 'mouseup', onZoomEnd);
        }
        self.fgcanvas_.style.cursor = 'col-resize';
        tarp.cover();
        return true;
      };

      onZoom = function(e) {
        if (!isZooming) {
          return false;
        }
        cancelEvent(e);

        var delX = e.clientX - clientXLast;
        if (Math.abs(delX) < 4) {
          return true;
        }
        clientXLast = e.clientX;

        // Move handle.
        var zoomHandleStatus = self.getZoomHandleStatus_();
        var newPos;
        if (handle == self.leftZoomHandle_) {
          newPos = zoomHandleStatus.leftHandlePos + delX;
          newPos = Math.min(newPos, zoomHandleStatus.rightHandlePos - handle.width - 3);
          newPos = Math.max(newPos, self.canvasRect_.x);
        } else {
          newPos = zoomHandleStatus.rightHandlePos + delX;
          newPos = Math.min(newPos, self.canvasRect_.x + self.canvasRect_.w);
          newPos = Math.max(newPos, zoomHandleStatus.leftHandlePos + handle.width + 3);
        }
        var halfHandleWidth = handle.width/2;
        handle.style.left = (newPos - halfHandleWidth) + 'px';
        self.drawInteractiveLayer_();

        // Zoom on the fly.
        if (dynamic) {
          doZoom();
        }
        return true;
      };

      onZoomEnd = function(e) {
        if (!isZooming) {
          return false;
        }
        isZooming = false;
        tarp.uncover();
        removeEvent(topElem, 'mousemove', onZoom);
        removeEvent(topElem, 'mouseup', onZoomEnd);
        self.fgcanvas_.style.cursor = 'default';

        // If on a slower device, zoom now.
        if (!dynamic) {
          doZoom();
        }
        return true;
      };

      doZoom = function() {
        try {
          var zoomHandleStatus = self.getZoomHandleStatus_();
          self.isChangingRange_ = true;
          if (!zoomHandleStatus.isZoomed) {
            self.dygraph_.resetZoom();
          } else {
            var xDataWindow = toXDataWindow(zoomHandleStatus);
            self.dygraph_.doZoomXDates_(xDataWindow[0], xDataWindow[1]);
          }
        } finally {
          self.isChangingRange_ = false;
        }
      };

      isMouseInPanZone = function(e) {
        var rect = self.leftZoomHandle_.getBoundingClientRect();
        var leftHandleClientX = rect.left + rect.width/2;
        rect = self.rightZoomHandle_.getBoundingClientRect();
        var rightHandleClientX = rect.left + rect.width/2;
        return (e.clientX > leftHandleClientX && e.clientX < rightHandleClientX);
      };

      onPanStart = function(e) {
        if (!isPanning && isMouseInPanZone(e) && self.getZoomHandleStatus_().isZoomed) {
          cancelEvent(e);
          isPanning = true;
          clientXLast = e.clientX;
          if (e.type === 'mousedown') {
            // These events are removed manually.
            addEvent(topElem, 'mousemove', onPan);
            addEvent(topElem, 'mouseup', onPanEnd);
          }
          return true;
        }
        return false;
      };

      onPan = function(e) {
        if (!isPanning) {
          return false;
        }
        cancelEvent(e);

        var delX = e.clientX - clientXLast;
        if (Math.abs(delX) < 4) {
          return true;
        }
        clientXLast = e.clientX;

        // Move range view
        var zoomHandleStatus = self.getZoomHandleStatus_();
        var leftHandlePos = zoomHandleStatus.leftHandlePos;
        var rightHandlePos = zoomHandleStatus.rightHandlePos;
        var rangeSize = rightHandlePos - leftHandlePos;
        if (leftHandlePos + delX <= self.canvasRect_.x) {
          leftHandlePos = self.canvasRect_.x;
          rightHandlePos = leftHandlePos + rangeSize;
        } else if (rightHandlePos + delX >= self.canvasRect_.x + self.canvasRect_.w) {
          rightHandlePos = self.canvasRect_.x + self.canvasRect_.w;
          leftHandlePos = rightHandlePos - rangeSize;
        } else {
          leftHandlePos += delX;
          rightHandlePos += delX;
        }
        var halfHandleWidth = self.leftZoomHandle_.width/2;
        self.leftZoomHandle_.style.left = (leftHandlePos - halfHandleWidth) + 'px';
        self.rightZoomHandle_.style.left = (rightHandlePos - halfHandleWidth) + 'px';
        self.drawInteractiveLayer_();

        // Do pan on the fly.
        if (dynamic) {
          doPan();
        }
        return true;
      };

      onPanEnd = function(e) {
        if (!isPanning) {
          return false;
        }
        isPanning = false;
        removeEvent(topElem, 'mousemove', onPan);
        removeEvent(topElem, 'mouseup', onPanEnd);
        // If on a slower device, do pan now.
        if (!dynamic) {
          doPan();
        }
        return true;
      };

      doPan = function() {
        try {
          self.isChangingRange_ = true;
          self.dygraph_.dateWindow_ = toXDataWindow(self.getZoomHandleStatus_());
          self.dygraph_.drawGraph_(false);
        } finally {
          self.isChangingRange_ = false;
        }
      };

      onCanvasHover = function(e) {
        if (isZooming || isPanning) {
          return;
        }
        var cursor = isMouseInPanZone(e) ? 'move' : 'default';
        if (cursor != self.fgcanvas_.style.cursor) {
          self.fgcanvas_.style.cursor = cursor;
        }
      };

      onZoomHandleTouchEvent = function(e) {
        if (e.type == 'touchstart' && e.targetTouches.length == 1) {
          if (onZoomStart(e.targetTouches[0])) {
            cancelEvent(e);
          }
        } else if (e.type == 'touchmove' && e.targetTouches.length == 1) {
          if (onZoom(e.targetTouches[0])) {
            cancelEvent(e);
          }
        } else {
          onZoomEnd(e);
        }
      };

      onCanvasTouchEvent = function(e) {
        if (e.type == 'touchstart' && e.targetTouches.length == 1) {
          if (onPanStart(e.targetTouches[0])) {
            cancelEvent(e);
          }
        } else if (e.type == 'touchmove' && e.targetTouches.length == 1) {
          if (onPan(e.targetTouches[0])) {
            cancelEvent(e);
          }
        } else {
          onPanEnd(e);
        }
      };

      addTouchEvents = function(elem, fn) {
        var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel'];
        for (var i = 0; i < types.length; i++) {
          self.dygraph_.addAndTrackEvent(elem, types[i], fn);
        }
      };

      this.setDefaultOption_('interactionModel', DygraphInteraction.dragIsPanInteractionModel);
      this.setDefaultOption_('panEdgeFraction', 0.0001);

      var dragStartEvent = window.opera ? 'mousedown' : 'dragstart';
      this.dygraph_.addAndTrackEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart);
      this.dygraph_.addAndTrackEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart);

      this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart);
      this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover);

      // Touch events
      if (this.hasTouchInterface_) {
        addTouchEvents(this.leftZoomHandle_, onZoomHandleTouchEvent);
        addTouchEvents(this.rightZoomHandle_, onZoomHandleTouchEvent);
        addTouchEvents(this.fgcanvas_, onCanvasTouchEvent);
      }
    };

    /**
     * @private
     * Draws the static layer in the background canvas.
     */
    rangeSelector.prototype.drawStaticLayer_ = function() {
      var ctx = this.bgcanvas_ctx_;
      ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h);
      try {
        this.drawMiniPlot_();
      } catch(ex) {
        console.warn(ex);
      }

      var margin = 0.5;
      this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorBackgroundLineWidth');
      ctx.strokeStyle = this.getOption_('rangeSelectorBackgroundStrokeColor');
      ctx.beginPath();
      ctx.moveTo(margin, margin);
      ctx.lineTo(margin, this.canvasRect_.h-margin);
      ctx.lineTo(this.canvasRect_.w-margin, this.canvasRect_.h-margin);
      ctx.lineTo(this.canvasRect_.w-margin, margin);
      ctx.stroke();
    };

    /**
     * @private
     * Draws the mini plot in the background canvas.
     */
    rangeSelector.prototype.drawMiniPlot_ = function() {
      var fillStyle = this.getOption_('rangeSelectorPlotFillColor');
      var fillGradientStyle = this.getOption_('rangeSelectorPlotFillGradientColor');
      var strokeStyle = this.getOption_('rangeSelectorPlotStrokeColor');
      if (!fillStyle && !strokeStyle) {
        return;
      }

      var stepPlot = this.getOption_('stepPlot');

      var combinedSeriesData = this.computeCombinedSeriesAndLimits_();
      var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin;

      // Draw the mini plot.
      var ctx = this.bgcanvas_ctx_;
      var margin = 0.5;

      var xExtremes = this.dygraph_.xAxisExtremes();
      var xRange = Math.max(xExtremes[1] - xExtremes[0], 1.e-30);
      var xFact = (this.canvasRect_.w - margin)/xRange;
      var yFact = (this.canvasRect_.h - margin)/yRange;
      var canvasWidth = this.canvasRect_.w - margin;
      var canvasHeight = this.canvasRect_.h - margin;

      var prevX = null, prevY = null;

      ctx.beginPath();
      ctx.moveTo(margin, canvasHeight);
      for (var i = 0; i < combinedSeriesData.data.length; i++) {
        var dataPoint = combinedSeriesData.data[i];
        var x = ((dataPoint[0] !== null) ? ((dataPoint[0] - xExtremes[0])*xFact) : NaN);
        var y = ((dataPoint[1] !== null) ? (canvasHeight - (dataPoint[1] - combinedSeriesData.yMin)*yFact) : NaN);

        // Skip points that don't change the x-value. Overly fine-grained points
        // can cause major slowdowns with the ctx.fill() call below.
        if (!stepPlot && prevX !== null && Math.round(x) == Math.round(prevX)) {
          continue;
        }

        if (isFinite(x) && isFinite(y)) {
          if(prevX === null) {
            ctx.lineTo(x, canvasHeight);
          }
          else if (stepPlot) {
            ctx.lineTo(x, prevY);
          }
          ctx.lineTo(x, y);
          prevX = x;
          prevY = y;
        }
        else {
          if(prevX !== null) {
            if (stepPlot) {
              ctx.lineTo(x, prevY);
              ctx.lineTo(x, canvasHeight);
            }
            else {
              ctx.lineTo(prevX, canvasHeight);
            }
          }
          prevX = prevY = null;
        }
      }
      ctx.lineTo(canvasWidth, canvasHeight);
      ctx.closePath();

      if (fillStyle) {
        var lingrad = this.bgcanvas_ctx_.createLinearGradient(0, 0, 0, canvasHeight);
        if (fillGradientStyle) {
          lingrad.addColorStop(0, fillGradientStyle);
        }
        lingrad.addColorStop(1, fillStyle);
        this.bgcanvas_ctx_.fillStyle = lingrad;
        ctx.fill();
      }

      if (strokeStyle) {
        this.bgcanvas_ctx_.strokeStyle = strokeStyle;
        this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorPlotLineWidth');
        ctx.stroke();
      }
    };

    /**
     * @private
     * Computes and returns the combined series data along with min/max for the mini plot.
     * The combined series consists of averaged values for all series.
     * When series have error bars, the error bars are ignored.
     * @return {Object} An object containing combined series array, ymin, ymax.
     */
    rangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() {
      var g = this.dygraph_;
      var logscale = this.getOption_('logscale');
      var i;

      // Select series to combine. By default, all series are combined.
      var numColumns = g.numColumns();
      var labels = g.getLabels();
      var includeSeries = new Array(numColumns);
      var anySet = false;
      var visibility = g.visibility();
      var inclusion = [];

      for (i = 1; i < numColumns; i++) {
        var include = this.getOption_('showInRangeSelector', labels[i]);
        inclusion.push(include);
        if (include !== null) anySet = true;  // it's set explicitly for this series
      }

      if (anySet) {
        for (i = 1; i < numColumns; i++) {
          includeSeries[i] = inclusion[i - 1];
        }
      } else {
        for (i = 1; i < numColumns; i++) {
          includeSeries[i] = visibility[i - 1];
        }
      }

      // Create a combined series (average of selected series values).
      // TODO(danvk): short-circuit if there's only one series.
      var rolledSeries = [];
      var dataHandler = g.dataHandler_;
      var options = g.attributes_;
      for (i = 1; i < g.numColumns(); i++) {
        if (!includeSeries[i]) continue;
        var series = dataHandler.extractSeries(g.rawData_, i, options);
        if (g.rollPeriod() > 1) {
          series = dataHandler.rollingAverage(series, g.rollPeriod(), options, i);
        }

        rolledSeries.push(series);
      }

      var combinedSeries = [];
      for (i = 0; i < rolledSeries[0].length; i++) {
        var sum = 0;
        var count = 0;
        for (var j = 0; j < rolledSeries.length; j++) {
          var y = rolledSeries[j][i][1];
          if (y === null || isNaN(y)) continue;
          count++;
          sum += y;
        }
        combinedSeries.push([rolledSeries[0][i][0], sum / count]);
      }

      // Compute the y range.
      var yMin = Number.MAX_VALUE;
      var yMax = -Number.MAX_VALUE;
      for (i = 0; i < combinedSeries.length; i++) {
        var yVal = combinedSeries[i][1];
        if (yVal !== null && isFinite(yVal) && (!logscale || yVal > 0)) {
          yMin = Math.min(yMin, yVal);
          yMax = Math.max(yMax, yVal);
        }
      }

      // Convert Y data to log scale if needed.
      // Also, expand the Y range to compress the mini plot a little.
      var extraPercent = 0.25;
      if (logscale) {
        yMax = log10(yMax);
        yMax += yMax*extraPercent;
        yMin = log10(yMin);
        for (i = 0; i < combinedSeries.length; i++) {
          combinedSeries[i][1] = log10(combinedSeries[i][1]);
        }
      } else {
        var yExtra;
        var yRange = yMax - yMin;
        if (yRange <= Number.MIN_VALUE) {
          yExtra = yMax*extraPercent;
        } else {
          yExtra = yRange*extraPercent;
        }
        yMax += yExtra;
        yMin -= yExtra;
      }

      return {data: combinedSeries, yMin: yMin, yMax: yMax};
    };

    /**
     * @private
     * Places the zoom handles in the proper position based on the current X data window.
     */
    rangeSelector.prototype.placeZoomHandles_ = function() {
      var xExtremes = this.dygraph_.xAxisExtremes();
      var xWindowLimits = this.dygraph_.xAxisRange();
      var xRange = xExtremes[1] - xExtremes[0];
      var leftPercent = Math.max(0, (xWindowLimits[0] - xExtremes[0])/xRange);
      var rightPercent = Math.max(0, (xExtremes[1] - xWindowLimits[1])/xRange);
      var leftCoord = this.canvasRect_.x + this.canvasRect_.w*leftPercent;
      var rightCoord = this.canvasRect_.x + this.canvasRect_.w*(1 - rightPercent);
      var handleTop = Math.max(this.canvasRect_.y, this.canvasRect_.y + (this.canvasRect_.h - this.leftZoomHandle_.height)/2);
      var halfHandleWidth = this.leftZoomHandle_.width/2;
      this.leftZoomHandle_.style.left = (leftCoord - halfHandleWidth) + 'px';
      this.leftZoomHandle_.style.top = handleTop + 'px';
      this.rightZoomHandle_.style.left = (rightCoord - halfHandleWidth) + 'px';
      this.rightZoomHandle_.style.top = this.leftZoomHandle_.style.top;

      this.leftZoomHandle_.style.visibility = 'visible';
      this.rightZoomHandle_.style.visibility = 'visible';
    };

    /**
     * @private
     * Draws the interactive layer in the foreground canvas.
     */
    rangeSelector.prototype.drawInteractiveLayer_ = function() {
      var ctx = this.fgcanvas_ctx_;
      ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h);
      var margin = 1;
      var width = this.canvasRect_.w - margin;
      var height = this.canvasRect_.h - margin;
      var zoomHandleStatus = this.getZoomHandleStatus_();

      ctx.strokeStyle = this.getOption_('rangeSelectorForegroundStrokeColor');
      ctx.lineWidth = this.getOption_('rangeSelectorForegroundLineWidth');
      if (!zoomHandleStatus.isZoomed) {
        ctx.beginPath();
        ctx.moveTo(margin, margin);
        ctx.lineTo(margin, height);
        ctx.lineTo(width, height);
        ctx.lineTo(width, margin);
        ctx.stroke();
      } else {
        var leftHandleCanvasPos = Math.max(margin, zoomHandleStatus.leftHandlePos - this.canvasRect_.x);
        var rightHandleCanvasPos = Math.min(width, zoomHandleStatus.rightHandlePos - this.canvasRect_.x);

        const veilColour = this.getOption_('rangeSelectorVeilColour');
        ctx.fillStyle = veilColour ? veilColour :
          ('rgba(240, 240, 240, ' + this.getOption_('rangeSelectorAlpha').toString() + ')');
        ctx.fillRect(0, 0, leftHandleCanvasPos, this.canvasRect_.h);
        ctx.fillRect(rightHandleCanvasPos, 0, this.canvasRect_.w - rightHandleCanvasPos, this.canvasRect_.h);

        ctx.beginPath();
        ctx.moveTo(margin, margin);
        ctx.lineTo(leftHandleCanvasPos, margin);
        ctx.lineTo(leftHandleCanvasPos, height);
        ctx.lineTo(rightHandleCanvasPos, height);
        ctx.lineTo(rightHandleCanvasPos, margin);
        ctx.lineTo(width, margin);
        ctx.stroke();
      }
    };

    /**
     * @private
     * Returns the current zoom handle position information.
     * @return {Object} The zoom handle status.
     */
    rangeSelector.prototype.getZoomHandleStatus_ = function() {
      var halfHandleWidth = this.leftZoomHandle_.width/2;
      var leftHandlePos = parseFloat(this.leftZoomHandle_.style.left) + halfHandleWidth;
      var rightHandlePos = parseFloat(this.rightZoomHandle_.style.left) + halfHandleWidth;
      return {
          leftHandlePos: leftHandlePos,
          rightHandlePos: rightHandlePos,
          isZoomed: (leftHandlePos - 1 > this.canvasRect_.x || rightHandlePos + 1 < this.canvasRect_.x+this.canvasRect_.w)
      };
    };

    /**
     * @license
     * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * A wrapper around Dygraph that implements the gviz API.
     * @param {!HTMLDivElement} container The DOM object the visualization should
     *     live in.
     * @constructor
     */
    var GVizChart = function(container) {
      this.container = container;
    };

    /**
     * @param {GVizDataTable} data
     * @param {Object.<*>} options
     */
    GVizChart.prototype.draw = function(data, options) {
      // Clear out any existing dygraph.
      // TODO(danvk): would it make more sense to simply redraw using the current
      // date_graph object?
      this.container.innerHTML = '';
      if (typeof(this.date_graph) != 'undefined') {
        this.date_graph.destroy();
      }

      this.date_graph = new Dygraph(this.container, data, options);
    };

    /**
     * Google charts compatible setSelection
     * Only row selection is supported, all points in the row will be highlighted
     * @param {Array.<{row:number}>} selection_array array of the selected cells
     * @public
     */
    GVizChart.prototype.setSelection = function(selection_array) {
      var row = false;
      if (selection_array.length) {
        row = selection_array[0].row;
      }
      this.date_graph.setSelection(row);
    };

    /**
     * Google charts compatible getSelection implementation
     * @return {Array.<{row:number,column:number}>} array of the selected cells
     * @public
     */
    GVizChart.prototype.getSelection = function() {
      var selection = [];

      var row = this.date_graph.getSelection();

      if (row < 0) return selection;

      var points = this.date_graph.layout_.points;
      for (var setIdx = 0; setIdx < points.length; ++setIdx) {
        selection.push({row: row, column: setIdx + 1});
      }

      return selection;
    };

    /**
     * @license
     * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
     * MIT-licenced: https://opensource.org/licenses/MIT
     */

    /**
     * @class Creates an interactive, zoomable chart.
     * @name Dygraph
     *
     * @constructor
     * @param {div | String} div A div or the id of a div into which to construct
     * the chart. Must not have any padding.
     * @param {String | Function} file A file containing CSV data or a function
     * that returns this data. The most basic expected format for each line is
     * "YYYY/MM/DD,val1,val2,...". For more information, see
     * http://dygraphs.com/data.html.
     * @param {Object} attrs Various other attributes, e.g. errorBars determines
     * whether the input data contains error ranges. For a complete list of
     * options, see http://dygraphs.com/options.html.
     */
    var Dygraph = function Dygraph(div, data, opts) {
      this.__init__(div, data, opts);
    };

    Dygraph.NAME = "Dygraph";
    Dygraph.VERSION = "2.2.1";

    // internal autoloader workaround
    var _addrequire = {};
    Dygraph._require = function require(what) {
      return (what in _addrequire ? _addrequire[what] : Dygraph._require._b(what));
    };
    Dygraph._require._b = null; // set by xfrmmodmap-dy.js
    Dygraph._require.add = function add(what, towhat) {
      _addrequire[what] = towhat;
    };

    // Various default values
    Dygraph.DEFAULT_ROLL_PERIOD = 1;
    Dygraph.DEFAULT_WIDTH = 480;
    Dygraph.DEFAULT_HEIGHT = 320;

    // For max 60 Hz. animation:
    Dygraph.ANIMATION_STEPS = 12;
    Dygraph.ANIMATION_DURATION = 200;

    /**
     * Standard plotters. These may be used by clients.
     * Available plotters are:
     * - Dygraph.Plotters.linePlotter: draws central lines (most common)
     * - Dygraph.Plotters.errorPlotter: draws high/low bands
     * - Dygraph.Plotters.fillPlotter: draws fills under lines (used with fillGraph)
     *
     * By default, the plotter is [fillPlotter, errorPlotter, linePlotter].
     * This causes all the lines to be drawn over all the fills/bands.
     */
    Dygraph.Plotters = DygraphCanvasRenderer._Plotters;

    // Used for initializing annotation CSS rules only once.
    Dygraph.addedAnnotationCSS = false;

    /**
     * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit
     * and context &lt;canvas&gt; inside of it. See the constructor for details.
     * on the parameters.
     * @param {Element} div the Element to render the graph into.
     * @param {string | Function} file Source data
     * @param {Object} attrs Miscellaneous other options
     * @private
     */
    Dygraph.prototype.__init__ = function(div, file, attrs) {
      this.is_initial_draw_ = true;
      this.readyFns_ = [];

      // Support two-argument constructor
      if (attrs === null || attrs === undefined) { attrs = {}; }

      attrs = Dygraph.copyUserAttrs_(attrs);

      if (typeof(div) == 'string') {
        div = document.getElementById(div);
      }

      if (!div) {
        throw new Error('Constructing dygraph with a non-existent div!');
      }

      // Copy the important bits into the object
      // TODO(danvk): most of these should just stay in the attrs_ dictionary.
      this.maindiv_ = div;
      this.file_ = file;
      this.rollPeriod_ = attrs.rollPeriod || Dygraph.DEFAULT_ROLL_PERIOD;
      this.previousVerticalX_ = -1;
      this.fractions_ = attrs.fractions || false;
      this.dateWindow_ = attrs.dateWindow || null;

      this.annotations_ = [];

      // Clear the div. This ensure that, if multiple dygraphs are passed the same
      // div, then only one will be drawn.
      div.innerHTML = "";

      const resolved = window.getComputedStyle(div, null);
      if (resolved.paddingLeft !== "0px" ||
          resolved.paddingRight !== "0px" ||
          resolved.paddingTop !== "0px" ||
          resolved.paddingBottom !== "0px")
        console.error('Main div contains padding; graph will misbehave');

      // For historical reasons, the 'width' and 'height' options trump all CSS
      // rules _except_ for an explicit 'width' or 'height' on the div.
      // As an added convenience, if the div has zero height (like <div></div> does
      // without any styles), then we use a default height/width.
      if (div.style.width === '' && attrs.width) {
        div.style.width = attrs.width + "px";
      }
      if (div.style.height === '' && attrs.height) {
        div.style.height = attrs.height + "px";
      }
      if (div.style.height === '' && div.clientHeight === 0) {
        div.style.height = Dygraph.DEFAULT_HEIGHT + "px";
        if (div.style.width === '') {
          div.style.width = Dygraph.DEFAULT_WIDTH + "px";
        }
      }
      // These will be zero if the dygraph's div is hidden. In that case,
      // use the user-specified attributes if present. If not, use zero
      // and assume the user will call resize to fix things later.
      this.width_ = div.clientWidth || attrs.width || 0;
      this.height_ = div.clientHeight || attrs.height || 0;

      // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_.
      if (attrs.stackedGraph) {
        attrs.fillGraph = true;
        // TODO(nikhilk): Add any other stackedGraph checks here.
      }

      // DEPRECATION WARNING: All option processing should be moved from
      // attrs_ and user_attrs_ to options_, which holds all this information.
      //
      // Dygraphs has many options, some of which interact with one another.
      // To keep track of everything, we maintain two sets of options:
      //
      //  this.user_attrs_   only options explicitly set by the user.
      //  this.attrs_        defaults, options derived from user_attrs_, data.
      //
      // Options are then accessed this.attr_('attr'), which first looks at
      // user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent
      // defaults without overriding behavior that the user specifically asks for.
      this.user_attrs_ = {};
      update(this.user_attrs_, attrs);

      // This sequence ensures that Dygraph.DEFAULT_ATTRS is never modified.
      this.attrs_ = {};
      updateDeep(this.attrs_, DEFAULT_ATTRS);

      this.boundaryIds_ = [];
      this.setIndexByName_ = {};
      this.datasetIndex_ = [];

      this.registeredEvents_ = [];
      this.eventListeners_ = {};

      this.attributes_ = new DygraphOptions(this);

      // Create the containing DIV and other interactive elements
      this.createInterface_();

      // Activate plugins.
      this.plugins_ = [];
      var plugins = Dygraph.PLUGINS.concat(this.getOption('plugins'));
      for (var i = 0; i < plugins.length; i++) {
        // the plugins option may contain either plugin classes or instances.
        // Plugin instances contain an activate method.
        var Plugin = plugins[i];  // either a constructor or an instance.
        var pluginInstance;
        if (typeof(Plugin.activate) !== 'undefined') {
          pluginInstance = Plugin;
        } else {
          pluginInstance = new Plugin();
        }

        var pluginDict = {
          plugin: pluginInstance,
          events: {},
          options: {},
          pluginOptions: {}
        };

        var handlers = pluginInstance.activate(this);
        for (var eventName in handlers) {
          if (!handlers.hasOwnProperty(eventName)) continue;
          // TODO(danvk): validate eventName.
          pluginDict.events[eventName] = handlers[eventName];
        }

        this.plugins_.push(pluginDict);
      }

      // At this point, plugins can no longer register event handlers.
      // Construct a map from event -> ordered list of [callback, plugin].
      for (var i = 0; i < this.plugins_.length; i++) {
        var plugin_dict = this.plugins_[i];
        for (var eventName in plugin_dict.events) {
          if (!plugin_dict.events.hasOwnProperty(eventName)) continue;
          var callback = plugin_dict.events[eventName];

          var pair = [plugin_dict.plugin, callback];
          if (!(eventName in this.eventListeners_)) {
            this.eventListeners_[eventName] = [pair];
          } else {
            this.eventListeners_[eventName].push(pair);
          }
        }
      }

      this.createDragInterface_();

      this.start_();
    };

    /**
     * Triggers a cascade of events to the various plugins which are interested in them.
     * Returns true if the "default behavior" should be prevented, i.e. if one
     * of the event listeners called event.preventDefault().
     * @private
     */
    Dygraph.prototype.cascadeEvents_ = function(name, extra_props) {
      if (!(name in this.eventListeners_)) return false;

      // QUESTION: can we use objects & prototypes to speed this up?
      var e = {
        dygraph: this,
        cancelable: false,
        defaultPrevented: false,
        preventDefault: function() {
          if (!e.cancelable) throw "Cannot call preventDefault on non-cancelable event.";
          e.defaultPrevented = true;
        },
        propagationStopped: false,
        stopPropagation: function() {
          e.propagationStopped = true;
        }
      };
      update(e, extra_props);

      var callback_plugin_pairs = this.eventListeners_[name];
      if (callback_plugin_pairs) {
        for (var i = callback_plugin_pairs.length - 1; i >= 0; i--) {
          var plugin = callback_plugin_pairs[i][0];
          var callback = callback_plugin_pairs[i][1];
          callback.call(plugin, e);
          if (e.propagationStopped) break;
        }
      }
      return e.defaultPrevented;
    };

    /**
     * Fetch a plugin instance of a particular class. Only for testing.
     * @private
     * @param {!Class} type The type of the plugin.
     * @return {Object} Instance of the plugin, or null if there is none.
     */
    Dygraph.prototype.getPluginInstance_ = function(type) {
      for (var i = 0; i < this.plugins_.length; i++) {
        var p = this.plugins_[i];
        if (p.plugin instanceof type) {
          return p.plugin;
        }
      }
      return null;
    };

    /**
     * Returns the zoomed status of the chart for one or both axes.
     *
     * Axis is an optional parameter. Can be set to 'x' or 'y'.
     *
     * The zoomed status for an axis is set whenever a user zooms using the mouse
     * or when the dateWindow or valueRange are updated. Double-clicking or calling
     * resetZoom() resets the zoom status for the chart.
     */
    Dygraph.prototype.isZoomed = function(axis) {
      const isZoomedX = !!this.dateWindow_;
      if (axis === 'x') return isZoomedX;

      const isZoomedY = this.axes_.map(axis => !!axis.valueRange).indexOf(true) >= 0;
      if (axis === null || axis === undefined) {
        return isZoomedX || isZoomedY;
      }
      if (axis === 'y') return isZoomedY;

      throw new Error(`axis parameter is [${axis}] must be null, 'x' or 'y'.`);
    };

    /**
     * Returns information about the Dygraph object, including its containing ID.
     */
    Dygraph.prototype.toString = function() {
      var maindiv = this.maindiv_;
      var id = (maindiv && maindiv.id) ? maindiv.id : maindiv;
      return "[Dygraph " + id + "]";
    };

    /**
     * @private
     * Returns the value of an option. This may be set by the user (either in the
     * constructor or by calling updateOptions) or by dygraphs, and may be set to a
     * per-series value.
     * @param {string} name The name of the option, e.g. 'rollPeriod'.
     * @param {string} [seriesName] The name of the series to which the option
     * will be applied. If no per-series value of this option is available, then
     * the global value is returned. This is optional.
     * @return {...} The value of the option.
     */
    Dygraph.prototype.attr_ = function(name, seriesName) {
      if (typeof process !== 'undefined' && "production" != 'production') {
        // For "production" code, this gets removed by uglifyjs.
        if (typeof(OPTIONS_REFERENCE$1) === 'undefined') {
          console.error('Must include options reference JS for testing');
        } else if (!OPTIONS_REFERENCE$1.hasOwnProperty(name)) {
          console.error('Dygraphs is using property ' + name + ', which has no ' +
                        'entry in the Dygraphs.OPTIONS_REFERENCE listing.');
          // Only log this error once.
          OPTIONS_REFERENCE$1[name] = true;
        }
      }
      return seriesName ? this.attributes_.getForSeries(name, seriesName) : this.attributes_.get(name);
    };

    /**
     * Returns the current value for an option, as set in the constructor or via
     * updateOptions. You may pass in an (optional) series name to get per-series
     * values for the option.
     *
     * All values returned by this method should be considered immutable. If you
     * modify them, there is no guarantee that the changes will be honored or that
     * dygraphs will remain in a consistent state. If you want to modify an option,
     * use updateOptions() instead.
     *
     * @param {string} name The name of the option (e.g. 'strokeWidth')
     * @param {string=} opt_seriesName Series name to get per-series values.
     * @return {*} The value of the option.
     */
    Dygraph.prototype.getOption = function(name, opt_seriesName) {
      return this.attr_(name, opt_seriesName);
    };

    /**
     * Like getOption(), but specifically returns a number.
     * This is a convenience function for working with the Closure Compiler.
     * @param {string} name The name of the option (e.g. 'strokeWidth')
     * @param {string=} opt_seriesName Series name to get per-series values.
     * @return {number} The value of the option.
     * @private
     */
    Dygraph.prototype.getNumericOption = function(name, opt_seriesName) {
      return /** @type{number} */(this.getOption(name, opt_seriesName));
    };

    /**
     * Like getOption(), but specifically returns a string.
     * This is a convenience function for working with the Closure Compiler.
     * @param {string} name The name of the option (e.g. 'strokeWidth')
     * @param {string=} opt_seriesName Series name to get per-series values.
     * @return {string} The value of the option.
     * @private
     */
    Dygraph.prototype.getStringOption = function(name, opt_seriesName) {
      return /** @type{string} */(this.getOption(name, opt_seriesName));
    };

    /**
     * Like getOption(), but specifically returns a boolean.
     * This is a convenience function for working with the Closure Compiler.
     * @param {string} name The name of the option (e.g. 'strokeWidth')
     * @param {string=} opt_seriesName Series name to get per-series values.
     * @return {boolean} The value of the option.
     * @private
     */
    Dygraph.prototype.getBooleanOption = function(name, opt_seriesName) {
      return /** @type{boolean} */(this.getOption(name, opt_seriesName));
    };

    /**
     * Like getOption(), but specifically returns a function.
     * This is a convenience function for working with the Closure Compiler.
     * @param {string} name The name of the option (e.g. 'strokeWidth')
     * @param {string=} opt_seriesName Series name to get per-series values.
     * @return {function(...)} The value of the option.
     * @private
     */
    Dygraph.prototype.getFunctionOption = function(name, opt_seriesName) {
      return /** @type{function(...)} */(this.getOption(name, opt_seriesName));
    };

    Dygraph.prototype.getOptionForAxis = function(name, axis) {
      return this.attributes_.getForAxis(name, axis);
    };

    /**
     * @private
     * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2')
     * @return {...} A function mapping string -> option value
     */
    Dygraph.prototype.optionsViewForAxis_ = function(axis) {
      var self = this;
      return function(opt) {
        var axis_opts = self.user_attrs_.axes;
        if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) {
          return axis_opts[axis][opt];
        }

        // I don't like that this is in a second spot.
        if (axis === 'x' && opt === 'logscale') {
          // return the default value.
          // TODO(konigsberg): pull the default from a global default.
          return false;
        }

        // user-specified attributes always trump defaults, even if they're less
        // specific.
        if (typeof(self.user_attrs_[opt]) != 'undefined') {
          return self.user_attrs_[opt];
        }

        axis_opts = self.attrs_.axes;
        if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) {
          return axis_opts[axis][opt];
        }
        // check old-style axis options
        // TODO(danvk): add a deprecation warning if either of these match.
        if (axis == 'y' && self.axes_[0].hasOwnProperty(opt)) {
          return self.axes_[0][opt];
        } else if (axis == 'y2' && self.axes_[1].hasOwnProperty(opt)) {
          return self.axes_[1][opt];
        }
        return self.attr_(opt);
      };
    };

    /**
     * Returns the current rolling period, as set by the user or an option.
     * @return {number} The number of points in the rolling window
     */
    Dygraph.prototype.rollPeriod = function() {
      return this.rollPeriod_;
    };

    /**
     * Returns the currently-visible x-range. This can be affected by zooming,
     * panning or a call to updateOptions.
     * Returns a two-element array: [left, right].
     * If the Dygraph has dates on the x-axis, these will be millis since epoch.
     */
    Dygraph.prototype.xAxisRange = function() {
      return this.dateWindow_ ? this.dateWindow_ : this.xAxisExtremes();
    };

    /**
     * Returns the lower- and upper-bound x-axis values of the data set.
     */
    Dygraph.prototype.xAxisExtremes = function() {
      var pad = this.getNumericOption('xRangePad') / this.plotter_.area.w;
      if (this.numRows() === 0) {
        return [0 - pad, 1 + pad];
      }
      var left = this.rawData_[0][0];
      var right = this.rawData_[this.rawData_.length - 1][0];
      if (pad) {
        // Must keep this in sync with dygraph-layout _evaluateLimits()
        var range = right - left;
        left -= range * pad;
        right += range * pad;
      }
      return [left, right];
    };

    /**
     * Returns the lower- and upper-bound y-axis values for each axis. These are
     * the ranges you'll get if you double-click to zoom out or call resetZoom().
     * The return value is an array of [low, high] tuples, one for each y-axis.
     */
    Dygraph.prototype.yAxisExtremes = function() {
      // TODO(danvk): this is pretty inefficient
      const packed = this.gatherDatasets_(this.rolledSeries_, null);
      const { extremes } = packed;
      const saveAxes = this.axes_;
      this.computeYAxisRanges_(extremes);
      const newAxes = this.axes_;
      this.axes_ = saveAxes;
      return newAxes.map(axis => axis.extremeRange);
    };

    /**
     * Returns the currently-visible y-range for an axis. This can be affected by
     * zooming, panning or a call to updateOptions. Axis indices are zero-based. If
     * called with no arguments, returns the range of the first axis.
     * Returns a two-element array: [bottom, top].
     */
    Dygraph.prototype.yAxisRange = function(idx) {
      if (typeof(idx) == "undefined") idx = 0;
      if (idx < 0 || idx >= this.axes_.length) {
        return null;
      }
      var axis = this.axes_[idx];
      return [ axis.computedValueRange[0], axis.computedValueRange[1] ];
    };

    /**
     * Returns the currently-visible y-ranges for each axis. This can be affected by
     * zooming, panning, calls to updateOptions, etc.
     * Returns an array of [bottom, top] pairs, one for each y-axis.
     */
    Dygraph.prototype.yAxisRanges = function() {
      var ret = [];
      for (var i = 0; i < this.axes_.length; i++) {
        ret.push(this.yAxisRange(i));
      }
      return ret;
    };

    // TODO(danvk): use these functions throughout dygraphs.
    /**
     * Convert from data coordinates to canvas/div X/Y coordinates.
     * If specified, do this conversion for the coordinate system of a particular
     * axis. Uses the first axis by default.
     * Returns a two-element array: [X, Y]
     *
     * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord
     * instead of toDomCoords(null, y, axis).
     */
    Dygraph.prototype.toDomCoords = function(x, y, axis) {
      return [ this.toDomXCoord(x), this.toDomYCoord(y, axis) ];
    };

    /**
     * Convert from data x coordinates to canvas/div X coordinate.
     * If specified, do this conversion for the coordinate system of a particular
     * axis.
     * Returns a single value or null if x is null.
     */
    Dygraph.prototype.toDomXCoord = function(x) {
      if (x === null) {
        return null;
      }

      var area = this.plotter_.area;
      var xRange = this.xAxisRange();
      return area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w;
    };

    /**
     * Convert from data x coordinates to canvas/div Y coordinate and optional
     * axis. Uses the first axis by default.
     *
     * returns a single value or null if y is null.
     */
    Dygraph.prototype.toDomYCoord = function(y, axis) {
      var pct = this.toPercentYCoord(y, axis);

      if (pct === null) {
        return null;
      }
      var area = this.plotter_.area;
      return area.y + pct * area.h;
    };

    /**
     * Convert from canvas/div coords to data coordinates.
     * If specified, do this conversion for the coordinate system of a particular
     * axis. Uses the first axis by default.
     * Returns a two-element array: [X, Y].
     *
     * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord
     * instead of toDataCoords(null, y, axis).
     */
    Dygraph.prototype.toDataCoords = function(x, y, axis) {
      return [ this.toDataXCoord(x), this.toDataYCoord(y, axis) ];
    };

    /**
     * Convert from canvas/div x coordinate to data coordinate.
     *
     * If x is null, this returns null.
     */
    Dygraph.prototype.toDataXCoord = function(x) {
      if (x === null) {
        return null;
      }

      var area = this.plotter_.area;
      var xRange = this.xAxisRange();

      if (!this.attributes_.getForAxis("logscale", 'x')) {
        return xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]);
      } else {
        var pct = (x - area.x) / area.w;
        return logRangeFraction(xRange[0], xRange[1], pct);
      }
    };

    /**
     * Convert from canvas/div y coord to value.
     *
     * If y is null, this returns null.
     * if axis is null, this uses the first axis.
     */
    Dygraph.prototype.toDataYCoord = function(y, axis) {
      if (y === null) {
        return null;
      }

      var area = this.plotter_.area;
      var yRange = this.yAxisRange(axis);

      if (typeof(axis) == "undefined") axis = 0;
      if (!this.attributes_.getForAxis("logscale", axis)) {
        return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]);
      } else {
        // Computing the inverse of toDomCoord.
        var pct = (y - area.y) / area.h;
        // Note reversed yRange, y1 is on top with pct==0.
        return logRangeFraction(yRange[1], yRange[0], pct);
      }
    };

    /**
     * Converts a y for an axis to a percentage from the top to the
     * bottom of the drawing area.
     *
     * If the coordinate represents a value visible on the canvas, then
     * the value will be between 0 and 1, where 0 is the top of the canvas.
     * However, this method will return values outside the range, as
     * values can fall outside the canvas.
     *
     * If y is null, this returns null.
     * if axis is null, this uses the first axis.
     *
     * @param {number} y The data y-coordinate.
     * @param {number} [axis] The axis number on which the data coordinate lives.
     * @return {number} A fraction in [0, 1] where 0 = the top edge.
     */
    Dygraph.prototype.toPercentYCoord = function(y, axis) {
      if (y === null) {
        return null;
      }
      if (typeof(axis) == "undefined") axis = 0;

      var yRange = this.yAxisRange(axis);

      var pct;
      var logscale = this.attributes_.getForAxis("logscale", axis);
      if (logscale) {
        var logr0 = log10(yRange[0]);
        var logr1 = log10(yRange[1]);
        pct = (logr1 - log10(y)) / (logr1 - logr0);
      } else {
        // yRange[1] - y is unit distance from the bottom.
        // yRange[1] - yRange[0] is the scale of the range.
        // (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom.
        pct = (yRange[1] - y) / (yRange[1] - yRange[0]);
      }
      return pct;
    };

    /**
     * Converts an x value to a percentage from the left to the right of
     * the drawing area.
     *
     * If the coordinate represents a value visible on the canvas, then
     * the value will be between 0 and 1, where 0 is the left of the canvas.
     * However, this method will return values outside the range, as
     * values can fall outside the canvas.
     *
     * If x is null, this returns null.
     * @param {number} x The data x-coordinate.
     * @return {number} A fraction in [0, 1] where 0 = the left edge.
     */
    Dygraph.prototype.toPercentXCoord = function(x) {
      if (x === null) {
        return null;
      }

      var xRange = this.xAxisRange();
      var pct;
      var logscale = this.attributes_.getForAxis("logscale", 'x') ;
      if (logscale === true) {  // logscale can be null so we test for true explicitly.
        var logr0 = log10(xRange[0]);
        var logr1 = log10(xRange[1]);
        pct = (log10(x) - logr0) / (logr1 - logr0);
      } else {
        // x - xRange[0] is unit distance from the left.
        // xRange[1] - xRange[0] is the scale of the range.
        // The full expression below is the % from the left.
        pct = (x - xRange[0]) / (xRange[1] - xRange[0]);
      }
      return pct;
    };

    /**
     * Returns the number of columns (including the independent variable).
     * @return {number} The number of columns.
     */
    Dygraph.prototype.numColumns = function() {
      if (!this.rawData_) return 0;
      return this.rawData_[0] ? this.rawData_[0].length : this.attr_("labels").length;
    };

    /**
     * Returns the number of rows (excluding any header/label row).
     * @return {number} The number of rows, less any header.
     */
    Dygraph.prototype.numRows = function() {
      if (!this.rawData_) return 0;
      return this.rawData_.length;
    };

    /**
     * Returns the value in the given row and column. If the row and column exceed
     * the bounds on the data, returns null. Also returns null if the value is
     * missing.
     * @param {number} row The row number of the data (0-based). Row 0 is the
     *     first row of data, not a header row.
     * @param {number} col The column number of the data (0-based)
     * @return {number} The value in the specified cell or null if the row/col
     *     were out of range.
     */
    Dygraph.prototype.getValue = function(row, col) {
      if (row < 0 || row >= this.rawData_.length) return null;
      if (col < 0 || col >= this.rawData_[row].length) return null;

      return this.rawData_[row][col];
    };

    /**
     * Generates interface elements for the Dygraph: a containing div, a div to
     * display the current point, and a textbox to adjust the rolling average
     * period. Also creates the Renderer/Layout elements.
     * @private
     */
    Dygraph.prototype.createInterface_ = function() {
      // Create the all-enclosing graph div
      var enclosing = this.maindiv_;

      this.graphDiv = document.createElement("div");

      // TODO(danvk): any other styles that are useful to set here?
      this.graphDiv.style.textAlign = 'left';  // This is a CSS "reset"
      this.graphDiv.style.position = 'relative';
      enclosing.appendChild(this.graphDiv);

      // Create the canvas for interactive parts of the chart.
      this.canvas_ = createCanvas();
      this.canvas_.style.position = "absolute";
      this.canvas_.style.top = 0;
      this.canvas_.style.left = 0;

      // ... and for static parts of the chart.
      this.hidden_ = this.createPlotKitCanvas_(this.canvas_);

      this.canvas_ctx_ = getContext(this.canvas_);
      this.hidden_ctx_ = getContext(this.hidden_);

      this.resizeElements_();

      // The interactive parts of the graph are drawn on top of the chart.
      this.graphDiv.appendChild(this.hidden_);
      this.graphDiv.appendChild(this.canvas_);
      this.mouseEventElement_ = this.createMouseEventElement_();

      // Create the grapher
      this.layout_ = new DygraphLayout(this);

      var dygraph = this;

      this.mouseMoveHandler_ = function(e) {
        dygraph.mouseMove_(e);
      };

      this.mouseOutHandler_ = function(e) {
        // The mouse has left the chart if:
        // 1. e.target is inside the chart
        // 2. e.relatedTarget is outside the chart
        var target = e.target || e.fromElement;
        var relatedTarget = e.relatedTarget || e.toElement;
        if (isNodeContainedBy(target, dygraph.graphDiv) &&
            !isNodeContainedBy(relatedTarget, dygraph.graphDiv)) {
          dygraph.mouseOut_(e);
        }
      };

      this.addAndTrackEvent(window, 'mouseout', this.mouseOutHandler_);
      this.addAndTrackEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_);

      // Don't recreate and register the resize handler on subsequent calls.
      // This happens when the graph is resized.
      if (!this.resizeHandler_) {
        this.resizeHandler_ = function(e) {
          dygraph.resize();
        };

        // Update when the window is resized.
        // TODO(danvk): drop frames depending on complexity of the chart.
        this.addAndTrackEvent(window, 'resize', this.resizeHandler_);

        this.resizeObserver_ = null;
        var resizeMode = this.getStringOption('resizable');
        if ((typeof(ResizeObserver) === 'undefined') &&
            (resizeMode !== "no")) {
          console.error('ResizeObserver unavailable; ignoring resizable property');
          resizeMode = "no";
        }
        if (resizeMode === "horizontal" ||
            resizeMode === "vertical" ||
            resizeMode === "both") {
          enclosing.style.resize = resizeMode;
        } else if (resizeMode !== "passive") {
          resizeMode = "no";
        }
        if (resizeMode !== "no") {
          window.getComputedStyle(enclosing).overflow;
          if (window.getComputedStyle(enclosing).overflow === 'visible')
            enclosing.style.overflow = 'hidden';
          this.resizeObserver_ = new ResizeObserver(this.resizeHandler_);
          this.resizeObserver_.observe(enclosing);
        }
      }
    };

    Dygraph.prototype.resizeElements_ = function() {
      this.graphDiv.style.width = this.width_ + "px";
      this.graphDiv.style.height = this.height_ + "px";

      var pixelRatioOption = this.getNumericOption('pixelRatio');

      var canvasScale = pixelRatioOption || getContextPixelRatio(this.canvas_ctx_);
      this.canvas_.width = this.width_ * canvasScale;
      this.canvas_.height = this.height_ * canvasScale;
      this.canvas_.style.width = this.width_ + "px";    // for IE
      this.canvas_.style.height = this.height_ + "px";  // for IE
      if (canvasScale !== 1) {
        this.canvas_ctx_.scale(canvasScale, canvasScale);
      }

      var hiddenScale = pixelRatioOption || getContextPixelRatio(this.hidden_ctx_);
      this.hidden_.width = this.width_ * hiddenScale;
      this.hidden_.height = this.height_ * hiddenScale;
      this.hidden_.style.width = this.width_ + "px";    // for IE
      this.hidden_.style.height = this.height_ + "px";  // for IE
      if (hiddenScale !== 1) {
        this.hidden_ctx_.scale(hiddenScale, hiddenScale);
      }
    };

    /**
     * Detach DOM elements in the dygraph and null out all data references.
     * Calling this when you're done with a dygraph can dramatically reduce memory
     * usage. See, e.g., the tests/perf.html example.
     */
    Dygraph.prototype.destroy = function() {
      this.canvas_ctx_.restore();
      this.hidden_ctx_.restore();

      // Destroy any plugins, in the reverse order that they were registered.
      for (var i = this.plugins_.length - 1; i >= 0; i--) {
        var p = this.plugins_.pop();
        if (p.plugin.destroy) p.plugin.destroy();
      }

      var removeRecursive = function(node) {
        while (node.hasChildNodes()) {
          removeRecursive(node.firstChild);
          node.removeChild(node.firstChild);
        }
      };

      this.removeTrackedEvents_();

      // remove mouse event handlers (This may not be necessary anymore)
      removeEvent(window, 'mouseout', this.mouseOutHandler_);
      removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_);

      // dispose of resizing handlers
      if (this.resizeObserver_) {
        this.resizeObserver_.disconnect();
        this.resizeObserver_ = null;
      }
      removeEvent(window, 'resize', this.resizeHandler_);
      this.resizeHandler_ = null;

      removeRecursive(this.maindiv_);

      var nullOut = function nullOut(obj) {
        for (var n in obj) {
          if (typeof(obj[n]) === 'object') {
            obj[n] = null;
          }
        }
      };
      // These may not all be necessary, but it can't hurt...
      nullOut(this.layout_);
      nullOut(this.plotter_);
      nullOut(this);
    };

    /**
     * Creates the canvas on which the chart will be drawn. Only the Renderer ever
     * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots
     * or the zoom rectangles) is done on this.canvas_.
     * @param {Object} canvas The Dygraph canvas over which to overlay the plot
     * @return {Object} The newly-created canvas
     * @private
     */
    Dygraph.prototype.createPlotKitCanvas_ = function(canvas) {
      var h = createCanvas();
      h.style.position = "absolute";
      // TODO(danvk): h should be offset from canvas. canvas needs to include
      // some extra area to make it easier to zoom in on the far left and far
      // right. h needs to be precisely the plot area, so that clipping occurs.
      h.style.top = canvas.style.top;
      h.style.left = canvas.style.left;
      h.width = this.width_;
      h.height = this.height_;
      h.style.width = this.width_ + "px";    // for IE
      h.style.height = this.height_ + "px";  // for IE
      return h;
    };

    /**
     * Creates an overlay element used to handle mouse events.
     * @return {Object} The mouse event element.
     * @private
     */
    Dygraph.prototype.createMouseEventElement_ = function() {
      return this.canvas_;
    };

    /**
     * Generate a set of distinct colors for the data series. This is done with a
     * color wheel. Saturation/Value are customizable, and the hue is
     * equally-spaced around the color wheel. If a custom set of colors is
     * specified, that is used instead.
     * @private
     */
    Dygraph.prototype.setColors_ = function() {
      var labels = this.getLabels();
      var num = labels.length - 1;
      this.colors_ = [];
      this.colorsMap_ = {};

      // These are used for when no custom colors are specified.
      var sat = this.getNumericOption('colorSaturation') || 1.0;
      var val = this.getNumericOption('colorValue') || 0.5;
      var half = Math.ceil(num / 2);

      var colors = this.getOption('colors');
      var visibility = this.visibility();
      for (var i = 0; i < num; i++) {
        if (!visibility[i]) {
          continue;
        }
        var label = labels[i + 1];
        var colorStr = this.attributes_.getForSeries('color', label);
        if (!colorStr) {
          if (colors) {
            colorStr = colors[i % colors.length];
          } else {
            // alternate colors for high contrast.
            var idx = i % 2 ? (half + (i + 1)/ 2) : Math.ceil((i + 1) / 2);
            var hue = (1.0 * idx / (1 + num));
            colorStr = hsvToRGB(hue, sat, val);
          }
        }
        this.colors_.push(colorStr);
        this.colorsMap_[label] = colorStr;
      }
    };

    /**
     * Return the list of colors. This is either the list of colors passed in the
     * attributes or the autogenerated list of rgb(r,g,b) strings.
     * This does not return colors for invisible series.
     * @return {Array.<string>} The list of colors.
     */
    Dygraph.prototype.getColors = function() {
      return this.colors_;
    };

    /**
     * Returns a few attributes of a series, i.e. its color, its visibility, which
     * axis it's assigned to, and its column in the original data.
     * Returns null if the series does not exist.
     * Otherwise, returns an object with column, visibility, color and axis properties.
     * The "axis" property will be set to 1 for y1 and 2 for y2.
     * The "column" property can be fed back into getValue(row, column) to get
     * values for this series.
     */
    Dygraph.prototype.getPropertiesForSeries = function(series_name) {
      var idx = -1;
      var labels = this.getLabels();
      for (var i = 1; i < labels.length; i++) {
        if (labels[i] == series_name) {
          idx = i;
          break;
        }
      }
      if (idx == -1) return null;

      return {
        name: series_name,
        column: idx,
        visible: this.visibility()[idx - 1],
        color: this.colorsMap_[series_name],
        axis: 1 + this.attributes_.axisForSeries(series_name)
      };
    };

    /**
     * Create the text box to adjust the averaging period
     * @private
     */
    Dygraph.prototype.createRollInterface_ = function() {
      // Create a roller if one doesn't exist already.
      var roller = this.roller_;
      if (!roller) {
        this.roller_ = roller = document.createElement("input");
        roller.type = "text";
        roller.style.display = "none";
        roller.className = 'dygraph-roller';
        this.graphDiv.appendChild(roller);
      }

      var display = this.getBooleanOption('showRoller') ? 'block' : 'none';

      var area = this.getArea();
      var textAttr = {
                       "top": (area.y + area.h - 25) + "px",
                       "left": (area.x + 1) + "px",
                       "display": display
                     };
      roller.size = "2";
      roller.value = this.rollPeriod_;
      update(roller.style, textAttr);

      const that = this;
      roller.onchange = function onchange() {
        return that.adjustRoll(roller.value);
      };
    };

    /**
     * Set up all the mouse handlers needed to capture dragging behavior for zoom
     * events.
     * @private
     */
    Dygraph.prototype.createDragInterface_ = function() {
      var context = {
        // Tracks whether the mouse is down right now
        isZooming: false,
        isPanning: false,  // is this drag part of a pan?
        is2DPan: false,    // if so, is that pan 1- or 2-dimensional?
        dragStartX: null, // pixel coordinates
        dragStartY: null, // pixel coordinates
        dragEndX: null, // pixel coordinates
        dragEndY: null, // pixel coordinates
        dragDirection: null,
        prevEndX: null, // pixel coordinates
        prevEndY: null, // pixel coordinates
        prevDragDirection: null,
        cancelNextDblclick: false,  // see comment in dygraph-interaction-model.js

        // The value on the left side of the graph when a pan operation starts.
        initialLeftmostDate: null,

        // The number of units each pixel spans. (This won't be valid for log
        // scales)
        xUnitsPerPixel: null,

        // TODO(danvk): update this comment
        // The range in second/value units that the viewport encompasses during a
        // panning operation.
        dateRange: null,

        // Top-left corner of the canvas, in DOM coords
        // TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY.
        px: 0,
        py: 0,

        // Values for use with panEdgeFraction, which limit how far outside the
        // graph's data boundaries it can be panned.
        boundedDates: null, // [minDate, maxDate]
        boundedValues: null, // [[minValue, maxValue] ...]

        // We cover iframes during mouse interactions. See comments in
        // dygraph-utils.js for more info on why this is a good idea.
        tarp: new IFrameTarp(),

        // contextB is the same thing as this context object but renamed.
        initializeMouseDown: function(event, g, contextB) {
          // prevents mouse drags from selecting page text.
          if (event.preventDefault) {
            event.preventDefault();  // Firefox, Chrome, etc.
          } else {
            event.returnValue = false;  // IE
            event.cancelBubble = true;
          }

          var canvasPos = findPos(g.canvas_);
          contextB.px = canvasPos.x;
          contextB.py = canvasPos.y;
          contextB.dragStartX = dragGetX_(event, contextB);
          contextB.dragStartY = dragGetY_(event, contextB);
          contextB.cancelNextDblclick = false;
          contextB.tarp.cover();
        },
        destroy: function() {
          var context = this;
          if (context.isZooming || context.isPanning) {
            context.isZooming = false;
            context.dragStartX = null;
            context.dragStartY = null;
          }

          if (context.isPanning) {
            context.isPanning = false;
            context.draggingDate = null;
            context.dateRange = null;
            for (var i = 0; i < self.axes_.length; i++) {
              delete self.axes_[i].draggingValue;
              delete self.axes_[i].dragValueRange;
            }
          }

          context.tarp.uncover();
        }
      };

      var interactionModel = this.getOption("interactionModel");

      // Self is the graph.
      var self = this;

      // Function that binds the graph and context to the handler.
      var bindHandler = function(handler) {
        return function(event) {
          handler(event, self, context);
        };
      };

      for (var eventName in interactionModel) {
        if (!interactionModel.hasOwnProperty(eventName)) continue;
        this.addAndTrackEvent(this.mouseEventElement_, eventName,
            bindHandler(interactionModel[eventName]));
      }

      // If the user releases the mouse button during a drag, but not over the
      // canvas, then it doesn't count as a zooming action.
      if (!interactionModel.willDestroyContextMyself) {
        var mouseUpHandler = function(event) {
          context.destroy();
        };

        this.addAndTrackEvent(document, 'mouseup', mouseUpHandler);
      }
    };

    /**
     * Draw a gray zoom rectangle over the desired area of the canvas. Also clears
     * up any previous zoom rectangles that were drawn. This could be optimized to
     * avoid extra redrawing, but it's tricky to avoid interactions with the status
     * dots.
     *
     * @param {number} direction the direction of the zoom rectangle. Acceptable
     *     values are utils.HORIZONTAL and utils.VERTICAL.
     * @param {number} startX The X position where the drag started, in canvas
     *     coordinates.
     * @param {number} endX The current X position of the drag, in canvas coords.
     * @param {number} startY The Y position where the drag started, in canvas
     *     coordinates.
     * @param {number} endY The current Y position of the drag, in canvas coords.
     * @param {number} prevDirection the value of direction on the previous call to
     *     this function. Used to avoid excess redrawing
     * @param {number} prevEndX The value of endX on the previous call to this
     *     function. Used to avoid excess redrawing
     * @param {number} prevEndY The value of endY on the previous call to this
     *     function. Used to avoid excess redrawing
     * @private
     */
    Dygraph.prototype.drawZoomRect_ = function(direction, startX, endX, startY,
                                               endY, prevDirection, prevEndX,
                                               prevEndY) {
      var ctx = this.canvas_ctx_;

      // Clean up from the previous rect if necessary
      if (prevDirection == HORIZONTAL) {
        ctx.clearRect(Math.min(startX, prevEndX), this.layout_.getPlotArea().y,
                      Math.abs(startX - prevEndX), this.layout_.getPlotArea().h);
      } else if (prevDirection == VERTICAL) {
        ctx.clearRect(this.layout_.getPlotArea().x, Math.min(startY, prevEndY),
                      this.layout_.getPlotArea().w, Math.abs(startY - prevEndY));
      }

      // Draw a light-grey rectangle to show the new viewing area
      if (direction == HORIZONTAL) {
        if (endX && startX) {
          ctx.fillStyle = "rgba(128,128,128,0.33)";
          ctx.fillRect(Math.min(startX, endX), this.layout_.getPlotArea().y,
                       Math.abs(endX - startX), this.layout_.getPlotArea().h);
        }
      } else if (direction == VERTICAL) {
        if (endY && startY) {
          ctx.fillStyle = "rgba(128,128,128,0.33)";
          ctx.fillRect(this.layout_.getPlotArea().x, Math.min(startY, endY),
                       this.layout_.getPlotArea().w, Math.abs(endY - startY));
        }
      }
    };

    /**
     * Clear the zoom rectangle (and perform no zoom).
     * @private
     */
    Dygraph.prototype.clearZoomRect_ = function() {
      this.currentZoomRectArgs_ = null;
      this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);
    };

    /**
     * Zoom to something containing [lowX, highX]. These are pixel coordinates in
     * the canvas. The exact zoom window may be slightly larger if there are no data
     * points near lowX or highX. Don't confuse this function with doZoomXDates,
     * which accepts dates that match the raw data. This function redraws the graph.
     *
     * @param {number} lowX The leftmost pixel value that should be visible.
     * @param {number} highX The rightmost pixel value that should be visible.
     * @private
     */
    Dygraph.prototype.doZoomX_ = function(lowX, highX) {
      this.currentZoomRectArgs_ = null;
      // Find the earliest and latest dates contained in this canvasx range.
      // Convert the call to date ranges of the raw data.
      var minDate = this.toDataXCoord(lowX);
      var maxDate = this.toDataXCoord(highX);
      this.doZoomXDates_(minDate, maxDate);
    };

    /**
     * Zoom to something containing [minDate, maxDate] values. Don't confuse this
     * method with doZoomX which accepts pixel coordinates. This function redraws
     * the graph.
     *
     * @param {number} minDate The minimum date that should be visible.
     * @param {number} maxDate The maximum date that should be visible.
     * @private
     */
    Dygraph.prototype.doZoomXDates_ = function(minDate, maxDate) {
      // TODO(danvk): when xAxisRange is null (i.e. "fit to data", the animation
      // can produce strange effects. Rather than the x-axis transitioning slowly
      // between values, it can jerk around.)
      var old_window = this.xAxisRange();
      var new_window = [minDate, maxDate];
      const zoomCallback = this.getFunctionOption('zoomCallback');
      const that = this;
      this.doAnimatedZoom(old_window, new_window, null, null, function animatedZoomCallback() {
        if (zoomCallback) {
          zoomCallback.call(that, minDate, maxDate, that.yAxisRanges());
        }
      });
    };

    /**
     * Zoom to something containing [lowY, highY]. These are pixel coordinates in
     * the canvas. This function redraws the graph.
     *
     * @param {number} lowY The topmost pixel value that should be visible.
     * @param {number} highY The lowest pixel value that should be visible.
     * @private
     */
    Dygraph.prototype.doZoomY_ = function(lowY, highY) {
      this.currentZoomRectArgs_ = null;
      // Find the highest and lowest values in pixel range for each axis.
      // Note that lowY (in pixels) corresponds to the max Value (in data coords).
      // This is because pixels increase as you go down on the screen, whereas data
      // coordinates increase as you go up the screen.
      var oldValueRanges = this.yAxisRanges();
      var newValueRanges = [];
      for (var i = 0; i < this.axes_.length; i++) {
        var hi = this.toDataYCoord(lowY, i);
        var low = this.toDataYCoord(highY, i);
        newValueRanges.push([low, hi]);
      }

      const zoomCallback = this.getFunctionOption('zoomCallback');
      const that = this;
      this.doAnimatedZoom(null, null, oldValueRanges, newValueRanges, function animatedZoomCallback() {
        if (zoomCallback) {
          const [minX, maxX] = that.xAxisRange();
          zoomCallback.call(that, minX, maxX, that.yAxisRanges());
        }
      });
    };

    /**
     * Transition function to use in animations. Returns values between 0.0
     * (totally old values) and 1.0 (totally new values) for each frame.
     * @private
     */
    Dygraph.zoomAnimationFunction = function(frame, numFrames) {
      var k = 1.5;
      return (1.0 - Math.pow(k, -frame)) / (1.0 - Math.pow(k, -numFrames));
    };

    /**
     * Reset the zoom to the original view coordinates. This is the same as
     * double-clicking on the graph.
     */
    Dygraph.prototype.resetZoom = function() {
      const dirtyX = this.isZoomed('x');
      const dirtyY = this.isZoomed('y');
      const dirty = dirtyX || dirtyY;

      // Clear any selection, since it's likely to be drawn in the wrong place.
      this.clearSelection();

      if (!dirty) return;

      // Calculate extremes to avoid lack of padding on reset.
      const [minDate, maxDate] = this.xAxisExtremes();

      const animatedZooms = this.getBooleanOption('animatedZooms');
      const zoomCallback = this.getFunctionOption('zoomCallback');

      // TODO(danvk): merge this block w/ the code below.
      // TODO(danvk): factor out a generic, public zoomTo method.
      if (!animatedZooms) {
        this.dateWindow_ = null;
        this.axes_.forEach(axis => {
          if (axis.valueRange) delete axis.valueRange;
        });

        this.drawGraph_();
        if (zoomCallback) {
          zoomCallback.call(this, minDate, maxDate, this.yAxisRanges());
        }
        return;
      }

      var oldWindow=null, newWindow=null, oldValueRanges=null, newValueRanges=null;
      if (dirtyX) {
        oldWindow = this.xAxisRange();
        newWindow = [minDate, maxDate];
      }

      if (dirtyY) {
        oldValueRanges = this.yAxisRanges();
        newValueRanges = this.yAxisExtremes();
      }

      const that = this;
      this.doAnimatedZoom(oldWindow, newWindow, oldValueRanges, newValueRanges,
          function animatedZoomCallback() {
            that.dateWindow_ = null;
            that.axes_.forEach(axis => {
              if (axis.valueRange) delete axis.valueRange;
            });
            if (zoomCallback) {
              zoomCallback.call(that, minDate, maxDate, that.yAxisRanges());
            }
          });
    };

    /**
     * Combined animation logic for all zoom functions.
     * either the x parameters or y parameters may be null.
     * @private
     */
    Dygraph.prototype.doAnimatedZoom = function(oldXRange, newXRange, oldYRanges, newYRanges, callback) {
      var steps = this.getBooleanOption("animatedZooms") ?
          Dygraph.ANIMATION_STEPS : 1;

      var windows = [];
      var valueRanges = [];
      var step, frac;

      if (oldXRange !== null && newXRange !== null) {
        for (step = 1; step <= steps; step++) {
          frac = Dygraph.zoomAnimationFunction(step, steps);
          windows[step-1] = [oldXRange[0]*(1-frac) + frac*newXRange[0],
                             oldXRange[1]*(1-frac) + frac*newXRange[1]];
        }
      }

      if (oldYRanges !== null && newYRanges !== null) {
        for (step = 1; step <= steps; step++) {
          frac = Dygraph.zoomAnimationFunction(step, steps);
          var thisRange = [];
          for (var j = 0; j < this.axes_.length; j++) {
            thisRange.push([oldYRanges[j][0]*(1-frac) + frac*newYRanges[j][0],
                            oldYRanges[j][1]*(1-frac) + frac*newYRanges[j][1]]);
          }
          valueRanges[step-1] = thisRange;
        }
      }

      const that = this;
      repeatAndCleanup(function (step) {
        if (valueRanges.length) {
          for (var i = 0; i < that.axes_.length; i++) {
            var w = valueRanges[step][i];
            that.axes_[i].valueRange = [w[0], w[1]];
          }
        }
        if (windows.length) {
          that.dateWindow_ = windows[step];
        }
        that.drawGraph_();
      }, steps, Dygraph.ANIMATION_DURATION / steps, callback);
    };

    /**
     * Get the current graph's area object.
     *
     * Returns: {x, y, w, h}
     */
    Dygraph.prototype.getArea = function() {
      return this.plotter_.area;
    };

    /**
     * Convert a mouse event to DOM coordinates relative to the graph origin.
     *
     * Returns a two-element array: [X, Y].
     */
    Dygraph.prototype.eventToDomCoords = function(event) {
      if (event.offsetX && event.offsetY) {
        return [ event.offsetX, event.offsetY ];
      } else {
        var eventElementPos = findPos(this.mouseEventElement_);
        var canvasx = pageX(event) - eventElementPos.x;
        var canvasy = pageY(event) - eventElementPos.y;
        return [canvasx, canvasy];
      }
    };

    /**
     * Given a canvas X coordinate, find the closest row.
     * @param {number} domX graph-relative DOM X coordinate
     * Returns {number} row number.
     * @private
     */
    Dygraph.prototype.findClosestRow = function(domX) {
      var minDistX = Infinity;
      var closestRow = -1;
      var sets = this.layout_.points;
      for (var i = 0; i < sets.length; i++) {
        var points = sets[i];
        var len = points.length;
        for (var j = 0; j < len; j++) {
          var point = points[j];
          if (!isValidPoint(point, true)) continue;
          var dist = Math.abs(point.canvasx - domX);
          if (dist < minDistX) {
            minDistX = dist;
            closestRow = point.idx;
          }
        }
      }

      return closestRow;
    };

    /**
     * Given canvas X,Y coordinates, find the closest point.
     *
     * This finds the individual data point across all visible series
     * that's closest to the supplied DOM coordinates using the standard
     * Euclidean X,Y distance.
     *
     * @param {number} domX graph-relative DOM X coordinate
     * @param {number} domY graph-relative DOM Y coordinate
     * Returns: {row, seriesName, point}
     * @private
     */
    Dygraph.prototype.findClosestPoint = function(domX, domY) {
      var minDist = Infinity;
      var dist, dx, dy, point, closestPoint, closestSeries, closestRow;
      for ( var setIdx = this.layout_.points.length - 1 ; setIdx >= 0 ; --setIdx ) {
        var points = this.layout_.points[setIdx];
        for (var i = 0; i < points.length; ++i) {
          point = points[i];
          if (!isValidPoint(point)) continue;
          dx = point.canvasx - domX;
          dy = point.canvasy - domY;
          dist = dx * dx + dy * dy;
          if (dist < minDist) {
            minDist = dist;
            closestPoint = point;
            closestSeries = setIdx;
            closestRow = point.idx;
          }
        }
      }
      var name = this.layout_.setNames[closestSeries];
      return {
        row: closestRow,
        seriesName: name,
        point: closestPoint
      };
    };

    /**
     * Given canvas X,Y coordinates, find the touched area in a stacked graph.
     *
     * This first finds the X data point closest to the supplied DOM X coordinate,
     * then finds the series which puts the Y coordinate on top of its filled area,
     * using linear interpolation between adjacent point pairs.
     *
     * @param {number} domX graph-relative DOM X coordinate
     * @param {number} domY graph-relative DOM Y coordinate
     * Returns: {row, seriesName, point}
     * @private
     */
    Dygraph.prototype.findStackedPoint = function(domX, domY) {
      var row = this.findClosestRow(domX);
      var closestPoint, closestSeries;
      for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) {
        var boundary = this.getLeftBoundary_(setIdx);
        var rowIdx = row - boundary;
        var points = this.layout_.points[setIdx];
        if (rowIdx >= points.length) continue;
        var p1 = points[rowIdx];
        if (!isValidPoint(p1)) continue;
        var py = p1.canvasy;
        if (domX > p1.canvasx && rowIdx + 1 < points.length) {
          // interpolate series Y value using next point
          var p2 = points[rowIdx + 1];
          if (isValidPoint(p2)) {
            var dx = p2.canvasx - p1.canvasx;
            if (dx > 0) {
              var r = (domX - p1.canvasx) / dx;
              py += r * (p2.canvasy - p1.canvasy);
            }
          }
        } else if (domX < p1.canvasx && rowIdx > 0) {
          // interpolate series Y value using previous point
          var p0 = points[rowIdx - 1];
          if (isValidPoint(p0)) {
            var dx = p1.canvasx - p0.canvasx;
            if (dx > 0) {
              var r = (p1.canvasx - domX) / dx;
              py += r * (p0.canvasy - p1.canvasy);
            }
          }
        }
        // Stop if the point (domX, py) is above this series' upper edge
        if (setIdx === 0 || py < domY) {
          closestPoint = p1;
          closestSeries = setIdx;
        }
      }
      var name = this.layout_.setNames[closestSeries];
      return {
        row: row,
        seriesName: name,
        point: closestPoint
      };
    };

    /**
     * When the mouse moves in the canvas, display information about a nearby data
     * point and draw dots over those points in the data series. This function
     * takes care of cleanup of previously-drawn dots.
     * @param {Object} event The mousemove event from the browser.
     * @private
     */
    Dygraph.prototype.mouseMove_ = function(event) {
      // This prevents JS errors when mousing over the canvas before data loads.
      var points = this.layout_.points;
      if (points === undefined || points === null) return;

      var canvasCoords = this.eventToDomCoords(event);
      var canvasx = canvasCoords[0];
      var canvasy = canvasCoords[1];

      var highlightSeriesOpts = this.getOption("highlightSeriesOpts");
      var selectionChanged = false;
      if (highlightSeriesOpts && !this.isSeriesLocked()) {
        var closest;
        if (this.getBooleanOption("stackedGraph")) {
          closest = this.findStackedPoint(canvasx, canvasy);
        } else {
          closest = this.findClosestPoint(canvasx, canvasy);
        }
        selectionChanged = this.setSelection(closest.row, closest.seriesName);
      } else {
        var idx = this.findClosestRow(canvasx);
        selectionChanged = this.setSelection(idx);
      }

      var callback = this.getFunctionOption("highlightCallback");
      if (callback && selectionChanged) {
        callback.call(this, event,
            this.lastx_,
            this.selPoints_,
            this.lastRow_,
            this.highlightSet_);
      }
    };

    /**
     * Fetch left offset from the specified set index or if not passed, the
     * first defined boundaryIds record (see bug #236).
     * @private
     */
    Dygraph.prototype.getLeftBoundary_ = function(setIdx) {
      if (this.boundaryIds_[setIdx]) {
          return this.boundaryIds_[setIdx][0];
      } else {
        for (var i = 0; i < this.boundaryIds_.length; i++) {
          if (this.boundaryIds_[i] !== undefined) {
            return this.boundaryIds_[i][0];
          }
        }
        return 0;
      }
    };

    Dygraph.prototype.animateSelection_ = function(direction) {
      var totalSteps = 10;
      var millis = 30;
      if (this.fadeLevel === undefined) this.fadeLevel = 0;
      if (this.animateId === undefined) this.animateId = 0;
      var start = this.fadeLevel;
      var steps = direction < 0 ? start : totalSteps - start;
      if (steps <= 0) {
        if (this.fadeLevel) {
          this.updateSelection_(1.0);
        }
        return;
      }

      var thisId = ++this.animateId;
      var that = this;
      var cleanupIfClearing = function() {
        // if we haven't reached fadeLevel 0 in the max frame time,
        // ensure that the clear happens and just go to 0
        if (that.fadeLevel !== 0 && direction < 0) {
          that.fadeLevel = 0;
          that.clearSelection();
        }
      };
      repeatAndCleanup(
        function(n) {
          // ignore simultaneous animations
          if (that.animateId != thisId) return;

          that.fadeLevel += direction;
          if (that.fadeLevel === 0) {
            that.clearSelection();
          } else {
            that.updateSelection_(that.fadeLevel / totalSteps);
          }
        },
        steps, millis, cleanupIfClearing);
    };

    /**
     * Draw dots over the selectied points in the data series. This function
     * takes care of cleanup of previously-drawn dots.
     * @private
     */
    Dygraph.prototype.updateSelection_ = function(opt_animFraction) {
      /*var defaultPrevented = */
      this.cascadeEvents_('select', {
        selectedRow: this.lastRow_ === -1 ? undefined : this.lastRow_,
        selectedX: this.lastx_ === null ? undefined : this.lastx_,
        selectedPoints: this.selPoints_
      });
      // TODO(danvk): use defaultPrevented here?

      // Clear the previously drawn vertical, if there is one
      var i;
      var ctx = this.canvas_ctx_;
      if (this.getOption('highlightSeriesOpts')) {
        ctx.clearRect(0, 0, this.width_, this.height_);
        var alpha = 1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha');
        var backgroundColor = toRGB_(this.getOption('highlightSeriesBackgroundColor'));

        if (alpha) {
          // Activating background fade includes an animation effect for a gradual
          // fade. TODO(klausw): make this independently configurable if it causes
          // issues? Use a shared preference to control animations?
          var animateBackgroundFade = this.getBooleanOption('animateBackgroundFade');
          if (animateBackgroundFade) {
            if (opt_animFraction === undefined) {
              // start a new animation
              this.animateSelection_(1);
              return;
            }
            alpha *= opt_animFraction;
          }
          ctx.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + alpha + ')';
          ctx.fillRect(0, 0, this.width_, this.height_);
        }

        // Redraw only the highlighted series in the interactive canvas (not the
        // static plot canvas, which is where series are usually drawn).
        this.plotter_._renderLineChart(this.highlightSet_, ctx);
      } else if (this.previousVerticalX_ >= 0) {
        // Determine the maximum highlight circle size.
        var maxCircleSize = 0;
        var labels = this.attr_('labels');
        for (i = 1; i < labels.length; i++) {
          var r = this.getNumericOption('highlightCircleSize', labels[i]);
          if (r > maxCircleSize) maxCircleSize = r;
        }
        var px = this.previousVerticalX_;
        ctx.clearRect(px - maxCircleSize - 1, 0,
                      2 * maxCircleSize + 2, this.height_);
      }

      if (this.selPoints_.length > 0) {
        // Draw colored circles over the center of each selected point
        var canvasx = this.selPoints_[0].canvasx;
        ctx.save();
        for (i = 0; i < this.selPoints_.length; i++) {
          var pt = this.selPoints_[i];
          if (isNaN(pt.canvasy)) continue;

          var circleSize = this.getNumericOption('highlightCircleSize', pt.name);
          var callback = this.getFunctionOption("drawHighlightPointCallback", pt.name);
          var color = this.plotter_.colors[pt.name];
          if (!callback) {
            callback = Circles.DEFAULT;
          }
          ctx.lineWidth = this.getNumericOption('strokeWidth', pt.name);
          ctx.strokeStyle = color;
          ctx.fillStyle = color;
          callback.call(this, this, pt.name, ctx, canvasx, pt.canvasy,
              color, circleSize, pt.idx);
        }
        ctx.restore();

        this.previousVerticalX_ = canvasx;
      }
    };

    /**
     * Manually set the selected points and display information about them in the
     * legend. The selection can be cleared using clearSelection() and queried
     * using getSelection().
     *
     * To set a selected series but not a selected point, call setSelection with
     * row=false and the selected series name.
     *
     * @param {number} row Row number that should be highlighted (i.e. appear with
     * hover dots on the chart).
     * @param {seriesName} optional series name to highlight that series with the
     * the highlightSeriesOpts setting.
     * @param {locked} optional If true, keep seriesName selected when mousing
     * over the graph, disabling closest-series highlighting. Call clearSelection()
     * to unlock it.
     * @param {trigger_highlight_callback} optional If true, trigger any
     * user-defined highlightCallback if highlightCallback has been set.
     */
    Dygraph.prototype.setSelection = function setSelection(row, opt_seriesName,
                                                           opt_locked,
                                                           opt_trigger_highlight_callback) {
      // Extract the points we've selected
      this.selPoints_ = [];

      var changed = false;
      if (row !== false && row >= 0) {
        if (row != this.lastRow_) changed = true;
        this.lastRow_ = row;
        for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) {
          var points = this.layout_.points[setIdx];
          // Check if the point at the appropriate index is the point we're looking
          // for.  If it is, just use it, otherwise search the array for a point
          // in the proper place.
          var setRow = row - this.getLeftBoundary_(setIdx);
          if (setRow >= 0 && setRow < points.length && points[setRow].idx == row) {
            var point = points[setRow];
            if (point.yval !== null) this.selPoints_.push(point);
          } else {
            for (var pointIdx = 0; pointIdx < points.length; ++pointIdx) {
              var point = points[pointIdx];
              if (point.idx == row) {
                if (point.yval !== null) {
                  this.selPoints_.push(point);
                }
                break;
              }
            }
          }
        }
      } else {
        if (this.lastRow_ >= 0) changed = true;
        this.lastRow_ = -1;
      }

      if (this.selPoints_.length) {
        this.lastx_ = this.selPoints_[0].xval;
      } else {
        this.lastx_ = null;
      }

      if (opt_seriesName !== undefined) {
        if (this.highlightSet_ !== opt_seriesName) changed = true;
        this.highlightSet_ = opt_seriesName;
      }

      if (opt_locked !== undefined) {
        this.lockedSet_ = opt_locked;
      }

      if (changed) {
        this.updateSelection_(undefined);

        if (opt_trigger_highlight_callback) {
          var callback = this.getFunctionOption("highlightCallback");
          if (callback) {
            var event = {};
            callback.call(this, event,
              this.lastx_,
              this.selPoints_,
              this.lastRow_,
              this.highlightSet_);
          }
        }
      }
      return changed;
    };

    /**
     * The mouse has left the canvas. Clear out whatever artifacts remain
     * @param {Object} event the mouseout event from the browser.
     * @private
     */
    Dygraph.prototype.mouseOut_ = function(event) {
      if (this.getFunctionOption("unhighlightCallback")) {
        this.getFunctionOption("unhighlightCallback").call(this, event);
      }

      if (this.getBooleanOption("hideOverlayOnMouseOut") && !this.lockedSet_) {
        this.clearSelection();
      }
    };

    /**
     * Clears the current selection (i.e. points that were highlighted by moving
     * the mouse over the chart).
     */
    Dygraph.prototype.clearSelection = function() {
      this.cascadeEvents_('deselect', {});

      this.lockedSet_ = false;
      // Get rid of the overlay data
      if (this.fadeLevel) {
        this.animateSelection_(-1);
        return;
      }
      this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_);
      this.fadeLevel = 0;
      this.selPoints_ = [];
      this.lastx_ = null;
      this.lastRow_ = -1;
      this.highlightSet_ = null;
    };

    /**
     * Returns the number of the currently selected row. To get data for this row,
     * you can use the getValue method.
     * @return {number} row number, or -1 if nothing is selected
     */
    Dygraph.prototype.getSelection = function() {
      if (!this.selPoints_ || this.selPoints_.length < 1) {
        return -1;
      }

      for (var setIdx = 0; setIdx < this.layout_.points.length; setIdx++) {
        var points = this.layout_.points[setIdx];
        for (var row = 0; row < points.length; row++) {
          if (points[row].x == this.selPoints_[0].x) {
            return points[row].idx;
          }
        }
      }
      return -1;
    };

    /**
     * Returns the name of the currently-highlighted series.
     * Only available when the highlightSeriesOpts option is in use.
     */
    Dygraph.prototype.getHighlightSeries = function() {
      return this.highlightSet_;
    };

    /**
     * Returns true if the currently-highlighted series was locked
     * via setSelection(..., seriesName, true).
     */
    Dygraph.prototype.isSeriesLocked = function() {
      return this.lockedSet_;
    };

    /**
     * Fires when there's data available to be graphed.
     * @param {string} data Raw CSV data to be plotted
     * @private
     */
    Dygraph.prototype.loadedEvent_ = function(data) {
      this.rawData_ = this.parseCSV_(data);
      this.cascadeDataDidUpdateEvent_();
      this.predraw_();
    };

    /**
     * Add ticks on the x-axis representing years, months, quarters, weeks, or days
     * @private
     */
    Dygraph.prototype.addXTicks_ = function() {
      // Determine the correct ticks scale on the x-axis: quarterly, monthly, ...
      var range;
      if (this.dateWindow_) {
        range = [this.dateWindow_[0], this.dateWindow_[1]];
      } else {
        range = this.xAxisExtremes();
      }

      var xAxisOptionsView = this.optionsViewForAxis_('x');
      var xTicks = xAxisOptionsView('ticker')(
          range[0],
          range[1],
          this.plotter_.area.w,  // TODO(danvk): should be area.width
          xAxisOptionsView,
          this);
      // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks);
      // console.log(msg);
      this.layout_.setXTicks(xTicks);
    };

    /**
     * Returns the correct handler class for the currently set options.
     * @private
     */
    Dygraph.prototype.getHandlerClass_ = function() {
      var handlerClass;
      if (this.attr_('dataHandler')) {
        handlerClass =  this.attr_('dataHandler');
      } else if (this.fractions_) {
        if (this.getBooleanOption('errorBars')) {
          handlerClass = FractionsBarsHandler;
        } else {
          handlerClass = DefaultFractionHandler;
        }
      } else if (this.getBooleanOption('customBars')) {
        handlerClass = CustomBarsHandler;
      } else if (this.getBooleanOption('errorBars')) {
        handlerClass = ErrorBarsHandler;
      } else {
        handlerClass = DefaultHandler;
      }
      return handlerClass;
    };

    /**
     * @private
     * This function is called once when the chart's data is changed or the options
     * dictionary is updated. It is _not_ called when the user pans or zooms. The
     * idea is that values derived from the chart's data can be computed here,
     * rather than every time the chart is drawn. This includes things like the
     * number of axes, rolling averages, etc.
     */
    Dygraph.prototype.predraw_ = function() {
      var start = new Date();

      // Create the correct dataHandler
      this.dataHandler_ = new (this.getHandlerClass_())();

      this.layout_.computePlotArea();

      // TODO(danvk): move more computations out of drawGraph_ and into here.
      this.computeYAxes_();

      if (!this.is_initial_draw_) {
        this.canvas_ctx_.restore();
        this.hidden_ctx_.restore();
      }

      this.canvas_ctx_.save();
      this.hidden_ctx_.save();

      // Create a new plotter.
      this.plotter_ = new DygraphCanvasRenderer(this,
                                                this.hidden_,
                                                this.hidden_ctx_,
                                                this.layout_);

      // The roller sits in the bottom left corner of the chart. We don't know where
      // this will be until the options are available, so it's positioned here.
      this.createRollInterface_();

      this.cascadeEvents_('predraw');

      // Convert the raw data (a 2D array) into the internal format and compute
      // rolling averages.
      this.rolledSeries_ = [null];  // x-axis is the first series and it's special
      for (var i = 1; i < this.numColumns(); i++) {
        // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too.
        var series = this.dataHandler_.extractSeries(this.rawData_, i, this.attributes_);
        if (this.rollPeriod_ > 1) {
          series = this.dataHandler_.rollingAverage(series, this.rollPeriod_, this.attributes_, i);
        }

        this.rolledSeries_.push(series);
      }

      // If the data or options have changed, then we'd better redraw.
      this.drawGraph_();

      // This is used to determine whether to do various animations.
      var end = new Date();
      this.drawingTimeMs_ = (end - start);
    };

    /**
     * Point structure.
     *
     * xval_* and yval_* are the original unscaled data values,
     * while x_* and y_* are scaled to the range (0.0-1.0) for plotting.
     * yval_stacked is the cumulative Y value used for stacking graphs,
     * and bottom/top/minus/plus are used for high/low band graphs.
     *
     * @typedef {{
     *     idx: number,
     *     name: string,
     *     x: ?number,
     *     xval: ?number,
     *     y_bottom: ?number,
     *     y: ?number,
     *     y_stacked: ?number,
     *     y_top: ?number,
     *     yval_minus: ?number,
     *     yval: ?number,
     *     yval_plus: ?number,
     *     yval_stacked
     * }}
     */
    Dygraph.PointType = undefined;

    /**
     * Calculates point stacking for stackedGraph=true.
     *
     * For stacking purposes, interpolate or extend neighboring data across
     * NaN values based on stackedGraphNaNFill settings. This is for display
     * only, the underlying data value as shown in the legend remains NaN.
     *
     * @param {Array.<Dygraph.PointType>} points Point array for a single series.
     *     Updates each Point's yval_stacked property.
     * @param {Array.<number>} cumulativeYval Accumulated top-of-graph stacked Y
     *     values for the series seen so far. Index is the row number. Updated
     *     based on the current series's values.
     * @param {Array.<number>} seriesExtremes Min and max values, updated
     *     to reflect the stacked values.
     * @param {string} fillMethod Interpolation method, one of 'all', 'inside', or
     *     'none'.
     * @private
     */
    Dygraph.stackPoints_ = function(
        points, cumulativeYval, seriesExtremes, fillMethod) {
      var lastXval = null;
      var prevPoint = null;
      var nextPoint = null;
      var nextPointIdx = -1;

      // Find the next stackable point starting from the given index.
      var updateNextPoint = function(idx) {
        // If we've previously found a non-NaN point and haven't gone past it yet,
        // just use that.
        if (nextPointIdx >= idx) return;

        // We haven't found a non-NaN point yet or have moved past it,
        // look towards the right to find a non-NaN point.
        for (var j = idx; j < points.length; ++j) {
          // Clear out a previously-found point (if any) since it's no longer
          // valid, we shouldn't use it for interpolation anymore.
          nextPoint = null;
          if (!isNaN(points[j].yval) && points[j].yval !== null) {
            nextPointIdx = j;
            nextPoint = points[j];
            break;
          }
        }
      };

      for (var i = 0; i < points.length; ++i) {
        var point = points[i];
        var xval = point.xval;
        if (cumulativeYval[xval] === undefined) {
          cumulativeYval[xval] = 0;
        }

        var actualYval = point.yval;
        if (isNaN(actualYval) || actualYval === null) {
          if(fillMethod == 'none') {
            actualYval = 0;
          } else {
            // Interpolate/extend for stacking purposes if possible.
            updateNextPoint(i);
            if (prevPoint && nextPoint && fillMethod != 'none') {
              // Use linear interpolation between prevPoint and nextPoint.
              actualYval = prevPoint.yval + (nextPoint.yval - prevPoint.yval) *
                  ((xval - prevPoint.xval) / (nextPoint.xval - prevPoint.xval));
            } else if (prevPoint && fillMethod == 'all') {
              actualYval = prevPoint.yval;
            } else if (nextPoint && fillMethod == 'all') {
              actualYval = nextPoint.yval;
            } else {
              actualYval = 0;
            }
          }
        } else {
          prevPoint = point;
        }

        var stackedYval = cumulativeYval[xval];
        if (lastXval != xval) {
          // If an x-value is repeated, we ignore the duplicates.
          stackedYval += actualYval;
          cumulativeYval[xval] = stackedYval;
        }
        lastXval = xval;

        point.yval_stacked = stackedYval;

        if (stackedYval > seriesExtremes[1]) {
          seriesExtremes[1] = stackedYval;
        }
        if (stackedYval < seriesExtremes[0]) {
          seriesExtremes[0] = stackedYval;
        }
      }
    };

    /**
     * Loop over all fields and create datasets, calculating extreme y-values for
     * each series and extreme x-indices as we go.
     *
     * dateWindow is passed in as an explicit parameter so that we can compute
     * extreme values "speculatively", i.e. without actually setting state on the
     * dygraph.
     *
     * @param {Array.<Array.<Array.<(number|Array<number>)>>} rolledSeries, where
     *     rolledSeries[seriesIndex][row] = raw point, where
     *     seriesIndex is the column number starting with 1, and
     *     rawPoint is [x,y] or [x, [y, err]] or [x, [y, yminus, yplus]].
     * @param {?Array.<number>} dateWindow [xmin, xmax] pair, or null.
     * @return {{
     *     points: Array.<Array.<Dygraph.PointType>>,
     *     seriesExtremes: Array.<Array.<number>>,
     *     boundaryIds: Array.<number>}}
     * @private
     */
    Dygraph.prototype.gatherDatasets_ = function(rolledSeries, dateWindow) {
      var boundaryIds = [];
      var points = [];
      var cumulativeYval = [];  // For stacked series.
      var extremes = {};  // series name -> [low, high]
      var seriesIdx, sampleIdx;
      var firstIdx, lastIdx;
      var axisIdx;

      // Loop over the fields (series).  Go from the last to the first,
      // because if they're stacked that's how we accumulate the values.
      var num_series = rolledSeries.length - 1;
      var series;
      for (seriesIdx = num_series; seriesIdx >= 1; seriesIdx--) {
        if (!this.visibility()[seriesIdx - 1]) continue;

        // Prune down to the desired range, if necessary (for zooming)
        // Because there can be lines going to points outside of the visible area,
        // we actually prune to visible points, plus one on either side.
        if (dateWindow) {
          series = rolledSeries[seriesIdx];
          var low = dateWindow[0];
          var high = dateWindow[1];

          // TODO(danvk): do binary search instead of linear search.
          // TODO(danvk): pass firstIdx and lastIdx directly to the renderer.
          firstIdx = null;
          lastIdx = null;
          for (sampleIdx = 0; sampleIdx < series.length; sampleIdx++) {
            if (series[sampleIdx][0] >= low && firstIdx === null) {
              firstIdx = sampleIdx;
            }
            if (series[sampleIdx][0] <= high) {
              lastIdx = sampleIdx;
            }
          }

          if (firstIdx === null) firstIdx = 0;
          var correctedFirstIdx = firstIdx;
          var isInvalidValue = true;
          while (isInvalidValue && correctedFirstIdx > 0) {
            correctedFirstIdx--;
            // check if the y value is null.
            isInvalidValue = series[correctedFirstIdx][1] === null;
          }

          if (lastIdx === null) lastIdx = series.length - 1;
          var correctedLastIdx = lastIdx;
          isInvalidValue = true;
          while (isInvalidValue && correctedLastIdx < series.length - 1) {
            correctedLastIdx++;
            isInvalidValue = series[correctedLastIdx][1] === null;
          }

          if (correctedFirstIdx!==firstIdx) {
            firstIdx = correctedFirstIdx;
          }
          if (correctedLastIdx !== lastIdx) {
            lastIdx = correctedLastIdx;
          }

          boundaryIds[seriesIdx-1] = [firstIdx, lastIdx];

          // .slice's end is exclusive, we want to include lastIdx.
          series = series.slice(firstIdx, lastIdx + 1);
        } else {
          series = rolledSeries[seriesIdx];
          boundaryIds[seriesIdx-1] = [0, series.length-1];
        }

        var seriesName = this.attr_("labels")[seriesIdx];
        var seriesExtremes = this.dataHandler_.getExtremeYValues(series,
            dateWindow, this.getBooleanOption("stepPlot", seriesName));

        var seriesPoints = this.dataHandler_.seriesToPoints(series,
            seriesName, boundaryIds[seriesIdx-1][0]);

        if (this.getBooleanOption("stackedGraph")) {
          axisIdx = this.attributes_.axisForSeries(seriesName);
          if (cumulativeYval[axisIdx] === undefined) {
            cumulativeYval[axisIdx] = [];
          }
          Dygraph.stackPoints_(seriesPoints, cumulativeYval[axisIdx], seriesExtremes,
                               this.getBooleanOption("stackedGraphNaNFill"));
        }

        extremes[seriesName] = seriesExtremes;
        points[seriesIdx] = seriesPoints;
      }

      return { points: points, extremes: extremes, boundaryIds: boundaryIds };
    };

    /**
     * Update the graph with new data. This method is called when the viewing area
     * has changed. If the underlying data or options have changed, predraw_ will
     * be called before drawGraph_ is called.
     *
     * @private
     */
    Dygraph.prototype.drawGraph_ = function() {
      var start = new Date();

      // This is used to set the second parameter to drawCallback, below.
      var is_initial_draw = this.is_initial_draw_;
      this.is_initial_draw_ = false;

      this.layout_.removeAllDatasets();
      this.setColors_();
      this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize');

      var packed = this.gatherDatasets_(this.rolledSeries_, this.dateWindow_);
      var points = packed.points;
      var extremes = packed.extremes;
      this.boundaryIds_ = packed.boundaryIds;

      this.setIndexByName_ = {};
      var labels = this.attr_("labels");
      var dataIdx = 0;
      for (var i = 1; i < points.length; i++) {
        if (!this.visibility()[i - 1]) continue;
        this.layout_.addDataset(labels[i], points[i]);
        this.datasetIndex_[i] = dataIdx++;
      }
      for (var i = 0; i < labels.length; i++) {
        this.setIndexByName_[labels[i]] = i;
      }

      this.computeYAxisRanges_(extremes);
      this.layout_.setYAxes(this.axes_);

      this.addXTicks_();

      // Tell PlotKit to use this new data and render itself
      this.layout_.evaluate();
      this.renderGraph_(is_initial_draw);

      if (this.getStringOption("timingName")) {
        var end = new Date();
        console.log(this.getStringOption("timingName") + " - drawGraph: " + (end - start) + "ms");
      }
    };

    /**
     * This does the work of drawing the chart. It assumes that the layout and axis
     * scales have already been set (e.g. by predraw_).
     *
     * @private
     */
    Dygraph.prototype.renderGraph_ = function(is_initial_draw) {
      this.cascadeEvents_('clearChart');
      this.plotter_.clear();

      const underlayCallback = this.getFunctionOption('underlayCallback');
      if (underlayCallback) {
        // NOTE: we pass the dygraph object to this callback twice to avoid breaking
        // users who expect a deprecated form of this callback.
        underlayCallback.call(this,
            this.hidden_ctx_, this.layout_.getPlotArea(), this, this);
      }

      var e = {
        canvas: this.hidden_,
        drawingContext: this.hidden_ctx_
      };
      this.cascadeEvents_('willDrawChart', e);
      this.plotter_.render();
      this.cascadeEvents_('didDrawChart', e);
      this.lastRow_ = -1;  // because plugins/legend.js clears the legend

      // TODO(danvk): is this a performance bottleneck when panning?
      // The interaction canvas should already be empty in that situation.
      this.canvas_.getContext('2d').clearRect(0, 0, this.width_, this.height_);

      const drawCallback = this.getFunctionOption("drawCallback");
      if (drawCallback !== null) {
        drawCallback.call(this, this, is_initial_draw);
      }
      if (is_initial_draw) {
        this.readyFired_ = true;
        while (this.readyFns_.length > 0) {
          var fn = this.readyFns_.pop();
          fn(this);
        }
      }
    };

    /**
     * @private
     * Determine properties of the y-axes which are independent of the data
     * currently being displayed. This includes things like the number of axes and
     * the style of the axes. It does not include the range of each axis and its
     * tick marks.
     * This fills in this.axes_.
     * axes_ = [ { options } ]
     *   indices are into the axes_ array.
     */
    Dygraph.prototype.computeYAxes_ = function() {
      var axis, opts, v;

      // this.axes_ doesn't match this.attributes_.axes_.options. It's used for
      // data computation as well as options storage.
      // Go through once and add all the axes.
      this.axes_ = [];

      for (axis = 0; axis < this.attributes_.numAxes(); axis++) {
        // Add a new axis, making a copy of its per-axis options.
        opts = { g : this };
        update(opts, this.attributes_.axisOptions(axis));
        this.axes_[axis] = opts;
      }

      for (axis = 0; axis < this.axes_.length; axis++) {
        if (axis === 0) {
          opts = this.optionsViewForAxis_('y' + (axis ? '2' : ''));
          v = opts("valueRange");
          if (v) this.axes_[axis].valueRange = v;
        } else {  // To keep old behavior
          var axes = this.user_attrs_.axes;
          if (axes && axes.y2) {
            v = axes.y2.valueRange;
            if (v) this.axes_[axis].valueRange = v;
          }
        }
      }
    };

    /**
     * Returns the number of y-axes on the chart.
     * @return {number} the number of axes.
     */
    Dygraph.prototype.numAxes = function() {
      return this.attributes_.numAxes();
    };

    /**
     * @private
     * Returns axis properties for the given series.
     * @param {string} setName The name of the series for which to get axis
     * properties, e.g. 'Y1'.
     * @return {Object} The axis properties.
     */
    Dygraph.prototype.axisPropertiesForSeries = function(series) {
      // TODO(danvk): handle errors.
      return this.axes_[this.attributes_.axisForSeries(series)];
    };

    /**
     * @private
     * Determine the value range and tick marks for each axis.
     * @param {Object} extremes A mapping from seriesName -> [low, high]
     * This fills in the valueRange and ticks fields in each entry of this.axes_.
     */
    Dygraph.prototype.computeYAxisRanges_ = function(extremes) {
      var isNullUndefinedOrNaN = function(num) {
        return isNaN(parseFloat(num));
      };
      var numAxes = this.attributes_.numAxes();
      var ypadCompat, span, series, ypad;

      var p_axis;

      // Compute extreme values, a span and tick marks for each axis.
      for (var i = 0; i < numAxes; i++) {
        var axis = this.axes_[i];
        var logscale = this.attributes_.getForAxis("logscale", i);
        var includeZero = this.attributes_.getForAxis("includeZero", i);
        var independentTicks = this.attributes_.getForAxis("independentTicks", i);
        series = this.attributes_.seriesForAxis(i);

        // Add some padding. This supports two Y padding operation modes:
        //
        // - backwards compatible (yRangePad not set):
        //   10% padding for automatic Y ranges, but not for user-supplied
        //   ranges, and move a close-to-zero edge to zero, since drawing at the edge
        //   results in invisible lines. Unfortunately lines drawn at the edge of a
        //   user-supplied range will still be invisible. If logscale is
        //   set, add a variable amount of padding at the top but
        //   none at the bottom.
        //
        // - new-style (yRangePad set by the user):
        //   always add the specified Y padding.
        //
        ypadCompat = true;
        ypad = 0.1; // add 10%
        const yRangePad = this.getNumericOption('yRangePad');
        if (yRangePad !== null) {
          ypadCompat = false;
          // Convert pixel padding to ratio
          ypad = yRangePad / this.plotter_.area.h;
        }

        if (series.length === 0) {
          // If no series are defined or visible then use a reasonable default
          axis.extremeRange = [0, 1];
        } else {
          // Calculate the extremes of extremes.
          var minY = Infinity;  // extremes[series[0]][0];
          var maxY = -Infinity;  // extremes[series[0]][1];
          var extremeMinY, extremeMaxY;

          for (var j = 0; j < series.length; j++) {
            // this skips invisible series
            if (!extremes.hasOwnProperty(series[j])) continue;

            // Only use valid extremes to stop null data series' from corrupting the scale.
            extremeMinY = extremes[series[j]][0];
            if (extremeMinY !== null) {
              minY = Math.min(extremeMinY, minY);
            }
            extremeMaxY = extremes[series[j]][1];
            if (extremeMaxY !== null) {
              maxY = Math.max(extremeMaxY, maxY);
            }
          }

          // Include zero if requested by the user.
          if (includeZero && !logscale) {
            if (minY > 0) minY = 0;
            if (maxY < 0) maxY = 0;
          }

          // Ensure we have a valid scale, otherwise default to [0, 1] for safety.
          if (minY == Infinity) minY = 0;
          if (maxY == -Infinity) maxY = 1;

          span = maxY - minY;
          // special case: if we have no sense of scale, center on the sole value.
          if (span === 0) {
            if (maxY !== 0) {
              span = Math.abs(maxY);
            } else {
              // ... and if the sole value is zero, use range 0-1.
              maxY = 1;
              span = 1;
            }
          }

          var maxAxisY = maxY, minAxisY = minY;
          if (ypadCompat) {
            if (logscale) {
              maxAxisY = maxY + ypad * span;
              minAxisY = minY;
            } else {
              maxAxisY = maxY + ypad * span;
              minAxisY = minY - ypad * span;

              // Backwards-compatible behavior: Move the span to start or end at zero if it's
              // close to zero.
              if (minAxisY < 0 && minY >= 0) minAxisY = 0;
              if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0;
            }
          }
          axis.extremeRange = [minAxisY, maxAxisY];
        }
        if (axis.valueRange) {
          // This is a user-set value range for this axis.
          var y0 = isNullUndefinedOrNaN(axis.valueRange[0]) ? axis.extremeRange[0] : axis.valueRange[0];
          var y1 = isNullUndefinedOrNaN(axis.valueRange[1]) ? axis.extremeRange[1] : axis.valueRange[1];
          axis.computedValueRange = [y0, y1];
        } else {
          axis.computedValueRange = axis.extremeRange;
        }
        if (!ypadCompat) {
          // When using yRangePad, adjust the upper/lower bounds to add
          // padding unless the user has zoomed/panned the Y axis range.

          y0 = axis.computedValueRange[0];
          y1 = axis.computedValueRange[1];

          // special case #781: if we have no sense of scale, center on the sole value.
          if (y0 === y1) {
            if(y0 === 0) {
              y1 = 1;
            } else {
              var delta = Math.abs(y0 / 10);
              y0 -= delta;
              y1 += delta;
            }
          }

          if (logscale) {
            var y0pct = ypad / (2 * ypad - 1);
            var y1pct = (ypad - 1) / (2 * ypad - 1);
            axis.computedValueRange[0] = logRangeFraction(y0, y1, y0pct);
            axis.computedValueRange[1] = logRangeFraction(y0, y1, y1pct);
          } else {
            span = y1 - y0;
            axis.computedValueRange[0] = y0 - span * ypad;
            axis.computedValueRange[1] = y1 + span * ypad;
          }
        }

        if (independentTicks) {
          axis.independentTicks = independentTicks;
          var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));
          var ticker = opts('ticker');
          axis.ticks = ticker(axis.computedValueRange[0],
                  axis.computedValueRange[1],
                  this.plotter_.area.h,
                  opts,
                  this);
          // Define the first independent axis as primary axis.
          if (!p_axis) p_axis = axis;
        }
      }
      if (p_axis === undefined) {
        throw ("Configuration Error: At least one axis has to have the \"independentTicks\" option activated.");
      }
      // Add ticks. By default, all axes inherit the tick positions of the
      // primary axis. However, if an axis is specifically marked as having
      // independent ticks, then that is permissible as well.
      for (var i = 0; i < numAxes; i++) {
        var axis = this.axes_[i];

        if (!axis.independentTicks) {
          var opts = this.optionsViewForAxis_('y' + (i ? '2' : ''));
          var ticker = opts('ticker');
          var p_ticks = p_axis.ticks;
          var p_scale = p_axis.computedValueRange[1] - p_axis.computedValueRange[0];
          var scale = axis.computedValueRange[1] - axis.computedValueRange[0];
          var tick_values = [];
          for (var k = 0; k < p_ticks.length; k++) {
            var y_frac = (p_ticks[k].v - p_axis.computedValueRange[0]) / p_scale;
            var y_val = axis.computedValueRange[0] + y_frac * scale;
            tick_values.push(y_val);
          }

          axis.ticks = ticker(axis.computedValueRange[0],
                              axis.computedValueRange[1],
                              this.plotter_.area.h,
                              opts,
                              this,
                              tick_values);
        }
      }
    };

    /**
     * Detects the type of the str (date or numeric) and sets the various
     * formatting attributes in this.attrs_ based on this type.
     * @param {string} str An x value.
     * @private
     */
    Dygraph.prototype.detectTypeFromString_ = function(str) {
      var isDate = false;
      var dashPos = str.indexOf('-');  // could be 2006-01-01 _or_ 1.0e-2
      if ((dashPos > 0 && (str[dashPos-1] != 'e' && str[dashPos-1] != 'E')) ||
          str.indexOf('/') >= 0 ||
          isNaN(parseFloat(str))) {
        isDate = true;
      }

      this.setXAxisOptions_(isDate);
    };

    Dygraph.prototype.setXAxisOptions_ = function(isDate) {
      if (isDate) {
        this.attrs_.xValueParser = dateParser;
        this.attrs_.axes.x.valueFormatter = dateValueFormatter;
        this.attrs_.axes.x.ticker = dateTicker;
        this.attrs_.axes.x.axisLabelFormatter = dateAxisLabelFormatter;
      } else {
        /** @private (shut up, jsdoc!) */
        this.attrs_.xValueParser = function(x) { return parseFloat(x); };
        // TODO(danvk): use Dygraph.numberValueFormatter here?
        /** @private (shut up, jsdoc!) */
        this.attrs_.axes.x.valueFormatter = function(x) { return x; };
        this.attrs_.axes.x.ticker = numericTicks;
        this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
      }
    };

    /**
     * @private
     * Parses a string in a special csv format.  We expect a csv file where each
     * line is a date point, and the first field in each line is the date string.
     * We also expect that all remaining fields represent series.
     * if the errorBars attribute is set, then interpret the fields as:
     * date, series1, stddev1, series2, stddev2, ...
     * @param {[Object]} data See above.
     *
     * @return [Object] An array with one entry for each row. These entries
     * are an array of cells in that row. The first entry is the parsed x-value for
     * the row. The second, third, etc. are the y-values. These can take on one of
     * three forms, depending on the CSV and constructor parameters:
     * 1. numeric value
     * 2. [ value, stddev ]
     * 3. [ low value, center value, high value ]
     */
    Dygraph.prototype.parseCSV_ = function(data) {
      var ret = [];
      var line_delimiter = detectLineDelimiter(data);
      var lines = data.split(line_delimiter || "\n");
      var vals, j;

      // Use the default delimiter or fall back to a tab if that makes sense.
      var delim = this.getStringOption('delimiter');
      if (lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0) {
        delim = '\t';
      }

      var start = 0;
      if (!('labels' in this.user_attrs_)) {
        // User hasn't explicitly set labels, so they're (presumably) in the CSV.
        start = 1;
        this.attrs_.labels = lines[0].split(delim);  // NOTE: _not_ user_attrs_.
        this.attributes_.reparseSeries();
      }

      var xParser;
      var defaultParserSet = false;  // attempt to auto-detect x value type
      var expectedCols = this.attr_("labels").length;
      var outOfOrder = false;
      for (var i = start; i < lines.length; i++) {
        var line = lines[i];
        if (line.length === 0) continue;  // skip blank lines
        if (line[0] == '#') continue;    // skip comment lines
        var inFields = line.split(delim);
        if (inFields.length < 2) continue;

        var fields = [];
        if (!defaultParserSet) {
          this.detectTypeFromString_(inFields[0]);
          xParser = this.getFunctionOption("xValueParser");
          defaultParserSet = true;
        }
        fields[0] = xParser(inFields[0], this);

        // If fractions are expected, parse the numbers as "A/B"
        if (this.fractions_) {
          for (j = 1; j < inFields.length; j++) {
            // TODO(danvk): figure out an appropriate way to flag parse errors.
            vals = inFields[j].split("/");
            if (vals.length != 2) {
              console.error('Expected fractional "num/den" values in CSV data ' +
                            "but found a value '" + inFields[j] + "' on line " +
                            (1 + i) + " ('" + line + "') which is not of this form.");
              fields[j] = [0, 0];
            } else {
              fields[j] = [parseFloat_(vals[0], i, line),
                           parseFloat_(vals[1], i, line)];
            }
          }
        } else if (this.getBooleanOption("errorBars")) {
          // If there are sigma-based high/low bands, values are (value, stddev) pairs
          if (inFields.length % 2 != 1) {
            console.error('Expected alternating (value, stdev.) pairs in CSV data ' +
                          'but line ' + (1 + i) + ' has an odd number of values (' +
                          (inFields.length - 1) + "): '" + line + "'");
          }
          for (j = 1; j < inFields.length; j += 2) {
            fields[(j + 1) / 2] = [parseFloat_(inFields[j], i, line),
                                   parseFloat_(inFields[j + 1], i, line)];
          }
        } else if (this.getBooleanOption("customBars")) {
          // Custom high/low bands are a low;centre;high tuple
          for (j = 1; j < inFields.length; j++) {
            var val = inFields[j];
            if (/^ *$/.test(val)) {
              fields[j] = [null, null, null];
            } else {
              vals = val.split(";");
              if (vals.length == 3) {
                fields[j] = [ parseFloat_(vals[0], i, line),
                              parseFloat_(vals[1], i, line),
                              parseFloat_(vals[2], i, line) ];
              } else {
                console.warn('When using customBars, values must be either blank ' +
                             'or "low;center;high" tuples (got "' + val +
                             '" on line ' + (1+i) + ')');
              }
            }
          }
        } else {
          // Values are just numbers
          for (j = 1; j < inFields.length; j++) {
            fields[j] = parseFloat_(inFields[j], i, line);
          }
        }
        if (ret.length > 0 && fields[0] < ret[ret.length - 1][0]) {
          outOfOrder = true;
        }

        if (fields.length != expectedCols) {
          console.error("Number of columns in line " + i + " (" + fields.length +
                        ") does not agree with number of labels (" + expectedCols +
                        ") " + line);
        }

        // If the user specified the 'labels' option and none of the cells of the
        // first row parsed correctly, then they probably double-specified the
        // labels. We go with the values set in the option, discard this row and
        // log a warning to the JS console.
        if (i === 0 && this.attr_('labels')) {
          var all_null = true;
          for (j = 0; all_null && j < fields.length; j++) {
            if (fields[j]) all_null = false;
          }
          if (all_null) {
            console.warn("The dygraphs 'labels' option is set, but the first row " +
                         "of CSV data ('" + line + "') appears to also contain " +
                         "labels. Will drop the CSV labels and use the option " +
                         "labels.");
            continue;
          }
        }
        ret.push(fields);
      }

      if (outOfOrder) {
        console.warn("CSV is out of order; order it correctly to speed loading.");
        ret.sort(function(a,b) { return a[0] - b[0]; });
      }

      return ret;
    };

    // In native format, all values must be dates or numbers.
    // This check isn't perfect but will catch most mistaken uses of strings.
    function validateNativeFormat(data) {
      const firstRow = data[0];
      const firstX = firstRow[0];
      if (typeof firstX !== 'number' && !isDateLike(firstX)) {
        throw new Error(`Expected number or date but got ${typeof firstX}: ${firstX}.`);
      }
      for (let i = 1; i < firstRow.length; i++) {
        const val = firstRow[i];
        if (val === null || val === undefined) continue;
        if (typeof val === 'number') continue;
        if (isArrayLike(val)) continue;  // e.g. errorBars or customBars
        throw new Error(`Expected number or array but got ${typeof val}: ${val}.`);
      }
    }

    /**
     * The user has provided their data as a pre-packaged JS array. If the x values
     * are numeric, this is the same as dygraphs' internal format. If the x values
     * are dates, we need to convert them from Date objects to ms since epoch.
     * @param {!Array} data
     * @return {Object} data with numeric x values.
     * @private
     */
    Dygraph.prototype.parseArray_ = function(data) {
      // Peek at the first x value to see if it's numeric.
      if (data.length === 0) {
        data = [[0]];
      }
      if (data[0].length === 0) {
        console.error("Data set cannot contain an empty row");
        return null;
      }

      validateNativeFormat(data);

      var i;
      if (this.attr_("labels") === null) {
        console.warn("Using default labels. Set labels explicitly via 'labels' " +
                     "in the options parameter");
        this.attrs_.labels = [ "X" ];
        for (i = 1; i < data[0].length; i++) {
          this.attrs_.labels.push("Y" + i); // Not user_attrs_.
        }
        this.attributes_.reparseSeries();
      } else {
        var num_labels = this.attr_("labels");
        if (num_labels.length != data[0].length) {
          console.error("Mismatch between number of labels (" + num_labels + ")" +
                        " and number of columns in array (" + data[0].length + ")");
          return null;
        }
      }

      if (isDateLike(data[0][0])) {
        // Some intelligent defaults for a date x-axis.
        this.attrs_.axes.x.valueFormatter = dateValueFormatter;
        this.attrs_.axes.x.ticker = dateTicker;
        this.attrs_.axes.x.axisLabelFormatter = dateAxisLabelFormatter;

        // Assume they're all dates.
        var parsedData = clone(data);
        for (i = 0; i < data.length; i++) {
          if (parsedData[i].length === 0) {
            console.error("Row " + (1 + i) + " of data is empty");
            return null;
          }
          if (parsedData[i][0] === null ||
              typeof(parsedData[i][0].getTime) != 'function' ||
              isNaN(parsedData[i][0].getTime())) {
            console.error("x value in row " + (1 + i) + " is not a Date");
            return null;
          }
          parsedData[i][0] = parsedData[i][0].getTime();
        }
        return parsedData;
      } else {
        // Some intelligent defaults for a numeric x-axis.
        /** @private (shut up, jsdoc!) */
        this.attrs_.axes.x.valueFormatter = function(x) { return x; };
        this.attrs_.axes.x.ticker = numericTicks;
        this.attrs_.axes.x.axisLabelFormatter = numberAxisLabelFormatter;
        return data;
      }
    };

    /**
     * Parses a DataTable object from gviz.
     * The data is expected to have a first column that is either a date or a
     * number. All subsequent columns must be numbers. If there is a clear mismatch
     * between this.xValueParser_ and the type of the first column, it will be
     * fixed. Fills out rawData_.
     * @param {!google.visualization.DataTable} data See above.
     * @private
     */
    Dygraph.prototype.parseDataTable_ = function(data) {
      var shortTextForAnnotationNum = function(num) {
        // converts [0-9]+ [A-Z][a-z]*
        // example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab
        // and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz
        var shortText = String.fromCharCode(65 /* A */ + num % 26);
        num = Math.floor(num / 26);
        while ( num > 0 ) {
          shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26 ) + shortText.toLowerCase();
          num = Math.floor((num - 1) / 26);
        }
        return shortText;
      };

      var cols = data.getNumberOfColumns();
      var rows = data.getNumberOfRows();

      var indepType = data.getColumnType(0);
      if (indepType == 'date' || indepType == 'datetime') {
        this.attrs_.xValueParser = dateParser;
        this.attrs_.axes.x.valueFormatter = dateValueFormatter;
        this.attrs_.axes.x.ticker = dateTicker;
        this.attrs_.axes.x.axisLabelFormatter = dateAxisLabelFormatter;
      } else if (indepType == 'number') {
        this.attrs_.xValueParser = function(x) { return parseFloat(x); };
        this.attrs_.axes.x.valueFormatter = function(x) { return x; };
        this.attrs_.axes.x.ticker = numericTicks;
        this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
      } else {
        throw new Error(
              "only 'date', 'datetime' and 'number' types are supported " +
              "for column 1 of DataTable input (Got '" + indepType + "')");
      }

      // Array of the column indices which contain data (and not annotations).
      var colIdx = [];
      var annotationCols = {};  // data index -> [annotation cols]
      var hasAnnotations = false;
      var i, j;
      for (i = 1; i < cols; i++) {
        var type = data.getColumnType(i);
        if (type == 'number') {
          colIdx.push(i);
        } else if (type == 'string' && this.getBooleanOption('displayAnnotations')) {
          // This is OK -- it's an annotation column.
          var dataIdx = colIdx[colIdx.length - 1];
          if (!annotationCols.hasOwnProperty(dataIdx)) {
            annotationCols[dataIdx] = [i];
          } else {
            annotationCols[dataIdx].push(i);
          }
          hasAnnotations = true;
        } else {
          throw new Error(
              "Only 'number' is supported as a dependent type with Gviz." +
              " 'string' is only supported if displayAnnotations is true");
        }
      }

      // Read column labels
      // TODO(danvk): add support back for errorBars
      var labels = [data.getColumnLabel(0)];
      for (i = 0; i < colIdx.length; i++) {
        labels.push(data.getColumnLabel(colIdx[i]));
        if (this.getBooleanOption("errorBars")) i += 1;
      }
      this.attrs_.labels = labels;
      cols = labels.length;

      var ret = [];
      var outOfOrder = false;
      var annotations = [];
      for (i = 0; i < rows; i++) {
        var row = [];
        if (typeof(data.getValue(i, 0)) === 'undefined' ||
            data.getValue(i, 0) === null) {
          console.warn("Ignoring row " + i +
                       " of DataTable because of undefined or null first column.");
          continue;
        }

        if (indepType == 'date' || indepType == 'datetime') {
          row.push(data.getValue(i, 0).getTime());
        } else {
          row.push(data.getValue(i, 0));
        }
        if (!this.getBooleanOption("errorBars")) {
          for (j = 0; j < colIdx.length; j++) {
            var col = colIdx[j];
            row.push(data.getValue(i, col));
            if (hasAnnotations &&
                annotationCols.hasOwnProperty(col) &&
                data.getValue(i, annotationCols[col][0]) !== null) {
              var ann = {};
              ann.series = data.getColumnLabel(col);
              ann.xval = row[0];
              ann.shortText = shortTextForAnnotationNum(annotations.length);
              ann.text = '';
              for (var k = 0; k < annotationCols[col].length; k++) {
                if (k) ann.text += "\n";
                ann.text += data.getValue(i, annotationCols[col][k]);
              }
              annotations.push(ann);
            }
          }

          // Strip out infinities, which give dygraphs problems later on.
          for (j = 0; j < row.length; j++) {
            if (!isFinite(row[j])) row[j] = null;
          }
        } else {
          for (j = 0; j < cols - 1; j++) {
            row.push([ data.getValue(i, 1 + 2 * j), data.getValue(i, 2 + 2 * j) ]);
          }
        }
        if (ret.length > 0 && row[0] < ret[ret.length - 1][0]) {
          outOfOrder = true;
        }
        ret.push(row);
      }

      if (outOfOrder) {
        console.warn("DataTable is out of order; order it correctly to speed loading.");
        ret.sort(function(a,b) { return a[0] - b[0]; });
      }
      this.rawData_ = ret;

      if (annotations.length > 0) {
        this.setAnnotations(annotations, true);
      }
      this.attributes_.reparseSeries();
    };

    /**
     * Signals to plugins that the chart data has updated.
     * This happens after the data has updated but before the chart has redrawn.
     * @private
     */
    Dygraph.prototype.cascadeDataDidUpdateEvent_ = function() {
      // TODO(danvk): there are some issues checking xAxisRange() and using
      // toDomCoords from handlers of this event. The visible range should be set
      // when the chart is drawn, not derived from the data.
      this.cascadeEvents_('dataDidUpdate', {});
    };

    /**
     * Get the CSV data. If it's in a function, call that function. If it's in a
     * file, do an XMLHttpRequest to get it.
     * @private
     */
    Dygraph.prototype.start_ = function() {
      var data = this.file_;

      // Functions can return references of all other types.
      if (typeof data == 'function') {
        data = data();
      }

      const datatype = typeArrayLike(data);
      if (datatype == 'array') {
        this.rawData_ = this.parseArray_(data);
        this.cascadeDataDidUpdateEvent_();
        this.predraw_();
      } else if (datatype == 'object' &&
                 typeof data.getColumnRange == 'function') {
        // must be a DataTable from gviz.
        this.parseDataTable_(data);
        this.cascadeDataDidUpdateEvent_();
        this.predraw_();
      } else if (datatype == 'string') {
        // Heuristic: a newline means it's CSV data. Otherwise it's an URL.
        var line_delimiter = detectLineDelimiter(data);
        if (line_delimiter) {
          this.loadedEvent_(data);
        } else {
          // REMOVE_FOR_IE
          var req;
          if (window.XMLHttpRequest) {
            // Firefox, Opera, IE7, and other browsers will use the native object
            req = new XMLHttpRequest();
          } else {
            // IE 5 and 6 will use the ActiveX control
            req = new ActiveXObject("Microsoft.XMLHTTP");
          }

          var caller = this;
          req.onreadystatechange = function () {
            if (req.readyState == 4) {
              if (req.status === 200 ||  // Normal http
                  req.status === 0) {    // Chrome w/ --allow-file-access-from-files
                caller.loadedEvent_(req.responseText);
              }
            }
          };

          req.open("GET", data, true);
          req.send(null);
        }
      } else {
        console.error("Unknown data format: " + datatype);
      }
    };

    /**
     * Changes various properties of the graph. These can include:
     * <ul>
     * <li>file: changes the source data for the graph</li>
     * <li>errorBars: changes whether the data contains stddev</li>
     * </ul>
     *
     * There's a huge variety of options that can be passed to this method. For a
     * full list, see http://dygraphs.com/options.html.
     *
     * @param {Object} input_attrs The new properties and values
     * @param {boolean} block_redraw Usually the chart is redrawn after every
     *     call to updateOptions(). If you know better, you can pass true to
     *     explicitly block the redraw. This can be useful for chaining
     *     updateOptions() calls, avoiding the occasional infinite loop and
     *     preventing redraws when it's not necessary (e.g. when updating a
     *     callback).
     */
    Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) {
      if (typeof(block_redraw) == 'undefined') block_redraw = false;

      // copyUserAttrs_ drops the "file" parameter as a convenience to us.
      var file = input_attrs.file;
      var attrs = Dygraph.copyUserAttrs_(input_attrs);
      var prevNumAxes = this.attributes_.numAxes();

      // TODO(danvk): this is a mess. Move these options into attr_.
      if ('rollPeriod' in attrs) {
        this.rollPeriod_ = attrs.rollPeriod;
      }
      if ('dateWindow' in attrs) {
        this.dateWindow_ = attrs.dateWindow;
      }

      // TODO(danvk): validate per-series options.
      // Supported:
      // strokeWidth
      // pointSize
      // drawPoints
      // highlightCircleSize

      // Check if this set options will require new points.
      var requiresNewPoints = isPixelChangingOptionList(this.attr_("labels"), attrs);

      updateDeep(this.user_attrs_, attrs);

      this.attributes_.reparseSeries();

      if (prevNumAxes < this.attributes_.numAxes()) this.plotter_.clear();
      if (file) {
        // This event indicates that the data is about to change, but hasn't yet.
        // TODO(danvk): support cancellation of the update via this event.
        this.cascadeEvents_('dataWillUpdate', {});

        this.file_ = file;
        if (!block_redraw) this.start_();
      } else {
        if (!block_redraw) {
          if (requiresNewPoints) {
            this.predraw_();
          } else {
            this.renderGraph_(false);
          }
        }
      }
    };

    /**
     * Make a copy of input attributes, removing file as a convenience.
     * @private
     */
    Dygraph.copyUserAttrs_ = function(attrs) {
      var my_attrs = {};
      for (var k in attrs) {
        if (!attrs.hasOwnProperty(k)) continue;
        if (k == 'file') continue;
        if (attrs.hasOwnProperty(k)) my_attrs[k] = attrs[k];
      }
      return my_attrs;
    };

    /**
     * Resizes the dygraph. If no parameters are specified, resizes to fill the
     * containing div (which has presumably changed size since the dygraph was
     * instantiated). If the width/height are specified, the div will be resized.
     *
     * This is far more efficient than destroying and re-instantiating a
     * Dygraph, since it doesn't have to reparse the underlying data.
     *
     * @param {number} width Width (in pixels)
     * @param {number} height Height (in pixels)
     */
    Dygraph.prototype.resize = function(width, height) {
      if (this.resize_lock) {
        return;
      }
      this.resize_lock = true;

      if ((width === null) != (height === null)) {
        console.warn("Dygraph.resize() should be called with zero parameters or " +
                     "two non-NULL parameters. Pretending it was zero.");
        width = height = null;
      }

      var old_width = this.width_;
      var old_height = this.height_;

      if (width) {
        this.maindiv_.style.width = width + "px";
        this.maindiv_.style.height = height + "px";
        this.width_ = width;
        this.height_ = height;
      } else {
        this.width_ = this.maindiv_.clientWidth;
        this.height_ = this.maindiv_.clientHeight;
      }

      if (old_width != this.width_ || old_height != this.height_) {
        // Resizing a canvas erases it, even when the size doesn't change, so
        // any resize needs to be followed by a redraw.
        this.resizeElements_();
        this.predraw_();
      }

      this.resize_lock = false;
    };

    /**
     * Adjusts the number of points in the rolling average. Updates the graph to
     * reflect the new averaging period.
     * @param {number} length Number of points over which to average the data.
     */
    Dygraph.prototype.adjustRoll = function(length) {
      this.rollPeriod_ = length;
      this.predraw_();
    };

    /**
     * Returns a boolean array of visibility statuses.
     */
    Dygraph.prototype.visibility = function() {
      // Do lazy-initialization, so that this happens after we know the number of
      // data series.
      if (!this.getOption("visibility")) {
        this.attrs_.visibility = [];
      }
      // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs.
      while (this.getOption("visibility").length < this.numColumns() - 1) {
        this.attrs_.visibility.push(true);
      }
      return this.getOption("visibility");
    };

    /**
     * Changes the visibility of one or more series.
     *
     * @param {number|number[]|object} num the series index or an array of series indices
     *                                     or a boolean array of visibility states by index
     *                                     or an object mapping series numbers, as keys, to
     *                                     visibility state (boolean values)
     * @param {boolean} value the visibility state expressed as a boolean
     */
    Dygraph.prototype.setVisibility = function(num, value) {
      var x = this.visibility();
      var numIsObject = false;

      if (!Array.isArray(num)) {
        if (num !== null && typeof num === 'object') {
          numIsObject = true;
        } else {
          num = [num];
        }
      }

      if (numIsObject) {
        for (var i in num) {
          if (num.hasOwnProperty(i)) {
            if (i < 0 || i >= x.length) {
              console.warn("Invalid series number in setVisibility: " + i);
            } else {
              x[i] = num[i];
            }
          }
        }
      } else {
        for (var i = 0; i < num.length; i++) {
          if (typeof num[i] === 'boolean') {
            if (i >= x.length) {
              console.warn("Invalid series number in setVisibility: " + i);
            } else {
              x[i] = num[i];
            }
          } else {
            if (num[i] < 0 || num[i] >= x.length) {
              console.warn("Invalid series number in setVisibility: " + num[i]);
            } else {
              x[num[i]] = value;
            }
          }
        }
      }

      this.predraw_();
    };

    /**
     * How large of an area will the dygraph render itself in?
     * This is used for testing.
     * @return A {width: w, height: h} object.
     * @private
     */
    Dygraph.prototype.size = function() {
      return { width: this.width_, height: this.height_ };
    };

    /**
     * Update the list of annotations and redraw the chart.
     * See dygraphs.com/annotations.html for more info on how to use annotations.
     * @param ann {Array} An array of annotation objects.
     * @param suppressDraw {Boolean} Set to "true" to block chart redraw (optional).
     */
    Dygraph.prototype.setAnnotations = function(ann, suppressDraw) {
      // Only add the annotation CSS rule once we know it will be used.
      this.annotations_ = ann;
      if (!this.layout_) {
        console.warn("Tried to setAnnotations before dygraph was ready. " +
                     "Try setting them in a ready() block. See " +
                     "dygraphs.com/tests/annotation.html");
        return;
      }

      this.layout_.setAnnotations(this.annotations_);
      if (!suppressDraw) {
        this.predraw_();
      }
    };

    /**
     * Return the list of annotations.
     */
    Dygraph.prototype.annotations = function() {
      return this.annotations_;
    };

    /**
     * Get the list of label names for this graph. The first column is the
     * x-axis, so the data series names start at index 1.
     *
     * Returns null when labels have not yet been defined.
     */
    Dygraph.prototype.getLabels = function() {
      var labels = this.attr_("labels");
      return labels ? labels.slice() : null;
    };

    /**
     * Get the index of a series (column) given its name. The first column is the
     * x-axis, so the data series start with index 1.
     */
    Dygraph.prototype.indexFromSetName = function(name) {
      return this.setIndexByName_[name];
    };

    /**
     * Find the row number corresponding to the given x-value.
     * Returns null if there is no such x-value in the data.
     * If there are multiple rows with the same x-value, this will return the
     * first one.
     * @param {number} xVal The x-value to look for (e.g. millis since epoch).
     * @return {?number} The row number, which you can pass to getValue(), or null.
     */
    Dygraph.prototype.getRowForX = function(xVal) {
      var low = 0,
          high = this.numRows() - 1;

      while (low <= high) {
        var idx = (high + low) >> 1;
        var x = this.getValue(idx, 0);
        if (x < xVal) {
          low = idx + 1;
        } else if (x > xVal) {
          high = idx - 1;
        } else if (low != idx) {  // equal, but there may be an earlier match.
          high = idx;
        } else {
          return idx;
        }
      }

      return null;
    };

    /**
     * Trigger a callback when the dygraph has drawn itself and is ready to be
     * manipulated. This is primarily useful when dygraphs has to do an XHR for the
     * data (i.e. a URL is passed as the data source) and the chart is drawn
     * asynchronously. If the chart has already drawn, the callback will fire
     * immediately.
     *
     * This is a good place to call setAnnotation().
     *
     * @param {function(!Dygraph)} callback The callback to trigger when the chart
     *     is ready.
     */
    Dygraph.prototype.ready = function(callback) {
      if (this.is_initial_draw_) {
        this.readyFns_.push(callback);
      } else {
        callback.call(this, this);
      }
    };

    /**
     * Add an event handler. This event handler is kept until the graph is
     * destroyed with a call to graph.destroy().
     *
     * @param {!Node} elem The element to add the event to.
     * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
     * @param {function(Event):(boolean|undefined)} fn The function to call
     *     on the event. The function takes one parameter: the event object.
     * @private
     */
    Dygraph.prototype.addAndTrackEvent = function(elem, type, fn) {
      addEvent(elem, type, fn);
      this.registeredEvents_.push({elem, type, fn});
    };

    Dygraph.prototype.removeTrackedEvents_ = function() {
      if (this.registeredEvents_) {
        for (var idx = 0; idx < this.registeredEvents_.length; idx++) {
          var reg = this.registeredEvents_[idx];
          removeEvent(reg.elem, reg.type, reg.fn);
        }
      }

      this.registeredEvents_ = [];
    };

    // Installed plugins, in order of precedence (most-general to most-specific).
    Dygraph.PLUGINS = [
      Legend,
      axes,
      rangeSelector, // Has to be before ChartLabels so that its callbacks are called after ChartLabels' callbacks.
      chart_labels,
      annotations,
      grid
    ];

    // There are many symbols which have historically been available through the
    // Dygraph class. These are exported here for backwards compatibility.
    Dygraph.GVizChart = GVizChart;
    Dygraph.DASHED_LINE = DASHED_LINE;
    Dygraph.DOT_DASH_LINE = DOT_DASH_LINE;
    Dygraph.dateAxisLabelFormatter = dateAxisLabelFormatter;
    Dygraph.toRGB_ = toRGB_;
    Dygraph.findPos = findPos;
    Dygraph.pageX = pageX;
    Dygraph.pageY = pageY;
    Dygraph.dateString_ = dateString_;
    Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel;
    Dygraph.nonInteractiveModel = Dygraph.nonInteractiveModel_ = DygraphInteraction.nonInteractiveModel_;
    Dygraph.Circles = Circles;

    Dygraph.Plugins = {
      Legend: Legend,
      Axes: axes,
      Annotations: annotations,
      ChartLabels: chart_labels,
      Grid: grid,
      RangeSelector: rangeSelector
    };

    Dygraph.DataHandlers = {
      DefaultHandler,
      BarsHandler,
      CustomBarsHandler,
      DefaultFractionHandler,
      ErrorBarsHandler,
      FractionsBarsHandler
    };

    Dygraph.startPan = DygraphInteraction.startPan;
    Dygraph.startZoom = DygraphInteraction.startZoom;
    Dygraph.movePan = DygraphInteraction.movePan;
    Dygraph.moveZoom = DygraphInteraction.moveZoom;
    Dygraph.endPan = DygraphInteraction.endPan;
    Dygraph.endZoom = DygraphInteraction.endZoom;

    Dygraph.numericLinearTicks = numericLinearTicks;
    Dygraph.numericTicks = numericTicks;
    Dygraph.dateTicker = dateTicker;
    Dygraph.Granularity = Granularity;
    Dygraph.getDateAxis = getDateAxis;
    Dygraph.floatFormat = floatFormat;

    setupDOMready_(Dygraph);

    /**
        (C) 2023 - ntop.org
    */

    /* *********************************************** */

    const MAX_BAR_WIDTH = 90; /* 100 px */
    const FILL_COLORS = [
      'rgb(144, 238, 144)',
      'rgb(25, 135, 84)',
      'rgb(255, 193, 7)',
      'rgb(220, 53, 69)'
    ];

    /* *********************************************** */

    function darkenColor(colorStr) {
      const color = Dygraph.toRGB_(colorStr);
      color.r = Math.floor((255 + color.r) / 2);
      color.g = Math.floor((255 + color.g) / 2);
      color.b = Math.floor((255 + color.b) / 2);
      return 'rgb(' + color.r + ',' + color.g + ',' + color.b + ', 0.9)';
    }

    /* *********************************************** */

    function getColor(current_value, max_value, default_color) {
      if(!max_value || !current_value) {
        return darkenColor(default_color);
      }
      /* Security check */
      if(current_value > max_value) {
        current_value = max_value;
      }

      const colors_module = max_value / FILL_COLORS.length;
      for(let i = 1; i < FILL_COLORS.length + 1; i++) {
        if(current_value <= colors_module * i) {
          return FILL_COLORS[i - 1];
        }
      }
    }

    /* *********************************************** */

    /* This function is used to create a bar chart instead of a line chart */
    function barChartPlotter(e) {
      const ctx = e.drawingContext;
      const points = e.points;
      const y_bottom = e.dygraph.toDomYCoord(0);
      const max_value = e.dygraph.user_attrs_.valueRange[1];
      const default_color = e.color;
      
      /* Find the minimum separation between x-values.
       * This determines the bar width.
       */
      let min_sep = Infinity;
      for (let i = 1; i < points.length; i++) {
        const sep = points[i].canvasx - points[i - 1].canvasx;
        if (sep < min_sep && sep > 0) min_sep = sep;
      }

      if(min_sep > MAX_BAR_WIDTH) {
        min_sep = MAX_BAR_WIDTH;
      }

      /* Keep just a little distance between the bars */
      const bar_width = Math.floor(0.9 * min_sep);

      /* Do the actual plotting */
      for (var i = 0; i < points.length; i++) {
        const p = points[i];
        const center_x = p.canvasx;
        const current_value = p.yval;
        ctx.fillStyle = getColor(Math.abs(current_value), Math.abs(max_value), default_color);
        ctx.fillRect(center_x - bar_width / 2, p.canvasy,
          bar_width, y_bottom - p.canvasy);
        ctx.strokeRect(center_x - bar_width / 2, p.canvasy,
          bar_width, y_bottom - p.canvasy);
      }
    }

    /* *********************************************** */

    const dygraphPlotters = function () {
      return {
        barChartPlotter,
      };
    }();

    /**
        (C) 2022 - ntop.org    
    */


    /* ***************************************** */

    function transformColors(colors) {
        let colorsPositionDict = {};
        colors.forEach((c, i) => {
    	if (colorsPositionDict[c] == null) {
    	    colorsPositionDict[c] = [i];
    	} else {
    	    colorsPositionDict[c].push(i);
    	}
        });
        // clone colors
        let newColors = ntopng_utility$1.clone(colors);
        
        for (let color in colorsPositionDict) {
    	let colorsPosition = colorsPositionDict[color];
    	let n = colorsPosition.length;
    	// colorsGenerated.length == colorsPosition.length always true
    	let colorsGenerated = getColorsFromColor(color, n);
    	colorsGenerated.forEach((c, i) => {
    	    let cPosition = colorsPosition[i];
    	    newColors[cPosition] = c;
    	});
        }
        return newColors;
    }

    /* ***************************************** */

    function getColorsFromColor(color, n) {
        return [...Array(n).keys()].map((c, i) => {
    	return generateColor(color, i + 1, n);
        });
    }

    /* ***************************************** */

    /**
     * Generate a color that represent the index-th tint of n of baseColor.
     * @param {baseColor} string color in hex format.
     * @param {index} integer in interval [1, n].
     * @param {n} total number of colors to generate
    **/
    function generateColor(baseColor, index, n) {
        let sourceColor = baseColor.replace("#", "");

        let redSource = parseInt(sourceColor.substring(0, 2), 16);
        let greenSource = parseInt(sourceColor.substring(2, 4), 16);
        let blueSource = parseInt(sourceColor.substring(4, 6), 16);

        let cRed = getColorInterpolation(redSource, index, n);
        let cGreen = getColorInterpolation(greenSource, index, n);
        let cBlue = getColorInterpolation(blueSource, index, n);

        return rgbToHex(cRed, cGreen, cBlue);
    }

    /* ***************************************** */

    function getColorInterpolation(colorSource, i, n) {    
        if (n <= 1) {
    	return colorSource;
        }
        let colorStart = Math.trunc(colorSource / 2);
        let colorEnd = Math.trunc(colorSource + ((255 - colorSource) / 2));
        let interval = Math.trunc((colorEnd - colorStart) / n);

        return colorStart + i * interval;
        // return colorStart + (n - i) * interval;
    }

    /* ***************************************** */

    function rgbToHex(r, g, b) {
        return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
    }

    /* ***************************************** */

    function componentToHex(c) {
        let hex = c.toString(16);
        return hex.length == 1 ? "0" + hex : hex;
    }

    /* ***************************************** */

    const colorsInterpolation = function() {
        return {
    	    transformColors,
        };
    }();

    /**
        (C) 2023 - ntop.org
    */

    /* ***************************************** */

    /* This function return the default config for dygraph charts */
    function getDefaultConfig() {
        return {
            labelsSeparateLines: true,
            legend: "follow",
            connectSeparatedPoints: true,
            includeZero: true,
            drawPoints: true,
            highlightSeriesBackgroundAlpha: 0.7,
            highlightSeriesOpts: {
                strokeWidth: 2,
                pointSize: 3,
                highlightCircleSize: 6,
            },
            axisLabelFontSize: 12,
            axes: {
                x: {
                    axisLabelWidth: 90
                }
            },
        };
    }

    /* ***************************************** */

    /* This function put the correct formatters in the configuration */
    function changeFormatters(config, options) {
        if (options.formatters.length > 1) {
            /* Multiple formatters */
            /* NOTE: at most 2 formatters can be used */
            config.axes.y1 = getAxisConfiguration(formatterUtils.getFormatter(options.formatters[0]));
            config.axes.y2 = getAxisConfiguration(formatterUtils.getFormatter(options.formatters[1]));
        } else if (options.formatters.length == 1) {
            /* Single formatter */
            config.axes.y = getAxisConfiguration(formatterUtils.getFormatter(options.formatters[0]));
        }
    }

    /* ***************************************** */

    /* This function return the color of the serie when highlighted */
    function getHighlightColor() {
        const is_dark_mode = document.getElementsByClassName('body dark').length > 0;
        let highlight_color = 'rgb(255, 255, 255)';
        if (is_dark_mode) {
            highlight_color = 'rgb(13, 17, 23)';
        }
        return highlight_color;
    }

    /* ***************************************** */

    /* This function is used to format the value on the legend */
    function getAxisConfiguration(formatter) {
        return {
            axisLabelFormatter: formatter,
            valueFormatter: function (num_or_millis, opts, seriesName, dygraph, row, col) {
                const serie_point = dygraph?.rawData_?.[row][col];
                let data = '';
                if (typeof (serie_point) == "object") {
                    /* This is the case for the serie with bounds */
                    serie_point.forEach((el) => {
                        data = `${data} / ${formatter(el || 0)}`;
                    });
                    data = data.substring(3); /* Remove the first three characters ' / ' */
                } else {
                    /* This is the standard case */
                    data = formatter(num_or_millis);
                }
                return (data);
            },
            axisLabelWidth: 80,
        }
    }

    /* ***************************************** */

    /* This function is used to format the value on the legend */
    function getDefaultLegendFormatter(options) {
        return function (data) {
            if (!data.x) return ""; // no hover
            const timeBadge = `<h6><span class="badge bg-light mb-1 text-dark">${data.xHTML}</span></h6>`;
            let total = 0;
            let setTotal = false;
            let totalString = '';

            // Entries
            const seriesHTML = data.series.filter(s => s.isVisible && s.yHTML).map(s => {
                if (!s.isVisible) return "";
                if (!isNaN(s.y) && !s.labelHTML.includes(i18n('details.ago'))) {
                    // Skip e.g. 30 min Ago series from the total
                    setTotal = true;
                    total = total + Math.abs(s.y);
                }
                const colorDot = `<span class="badge rounded-pill me-1" style="background-color:${s.color}"> </span>`;
                return `<div class="mt-1 d-flex"><div class="me-4">${colorDot}${s.labelHTML}</div><div class="ms-auto">${s.yHTML}</div></div>`;
            }).join("");

            if (setTotal) {
                const formatter = formatterUtils.getFormatter(options.formatters[0]);
                totalString = `<div class="mt-1 d-flex"><div class="me-4"><strong>Total</strong></div><div class="ms-auto">${formatter(total)}</div></div>`;
            }

            return `<div style="font-size:13px; line-height:1.4;">${timeBadge}${totalString}${seriesHTML}</div>`;
        }
    }

    /* ***************************************** */

    /* This function merges the default config with the options requested */
    function buildChartOptions(options) {
        const interpolated_colors = colorsInterpolation.transformColors(options.colors);
        const highlight_color = getHighlightColor();
        const config = getDefaultConfig();
        const legendFormatter = getDefaultLegendFormatter(options);

        config.customBars = options.customBars;
        config.labels = options.labels;
        config.series = options.properties;
        config.data = options.serie;
        config.stackedGraph = options.stacked;
        config.valueRange = options.value_range;
        config.highlightSeriesBackgroundColor = highlight_color;
        config.colors = interpolated_colors;
        config.disableTsList = options.disable_ts_list;
        config.yRangePad = options.yRangePad || 1;
        config.legendFormatter = legendFormatter;
        config.blockStacked = options.block_stacked;

        /* Change the plotter */
        if (options.plotter) {
            config.plotter = options.plotter;
        }

        changeFormatters(config, options);

        return config;
    }

    /* ***************************************** */

    function formatSerieProperties(type) {
        switch (type) {
            case 'dash':
                return {
                    fillGraph: false,
                    customBars: false,
                    strokePattern: Dygraph.DASHED_LINE
                };
            case 'point':
                return {
                    fillGraph: false,
                    customBars: false,
                    strokeWidth: 0.0,
                    pointSize: 2.0,
                };
            case 'bounds':
                return {
                    fillGraph: false,
                    strokeWidth: 1.0,
                    pointSize: 1.5,
                    fillAlpha: 0.5
                };
            case 'line':
                return {
                    fillGraph: false,
                    customBars: false,
                    strokeWidth: 1.5,
                    pointSize: 1.5,
                };
            default:
                return {
                    fillGraph: true,
                    customBars: false,
                    strokeWidth: 1.0,
                    pointSize: 1.5,
                    fillAlpha: 0.5
                };
        }
    }

    /* ***************************************** */

    const dygraphConfig = function () {
        return {
            buildChartOptions,
            formatSerieProperties
        };
    }();

    /**
        (C) 2023 - ntop.org
    */


    /* ***************************************** */

    const defaultColors = [
        "#C6D9FD",
        "#90EE90",
        "#EE8434",
        "#C95D63",
        "#AE8799",
        "#717EC3",
        "#496DDB",
        "#5A7ADE",
        "#6986E1",
        "#7791E4",
        "#839BE6",
        "#8EA4E8",
    ];

    /* ***************************************** */

    const constant_serie_colors = {
        "default_color": "#C6D9FD",
        "95_perc": "#8EA4E8",
        "avg": "#839BE6",
    };

    /* ***************************************** */

    function getSerieId$1(serie) {
        return `${serie.id}`;
    }

    /* ***************************************** */

    function formatSerieColors(palette_list) {
        let colors_list = palette_list;
        let count0 = 0, count1 = 0;
        let colors0 = defaultColors;
        let colors1 = d3v7.schemeCategory10;
        colors_list.forEach((s, index) => {
            if (s.palette == 0) {
                if (palette_list.find((element, j) => (element.color === s.color && j !== index))) {
                    palette_list[index] = colors0[count0 % colors0.length];
                } else {
                    palette_list[index] = s.color;
                }
                count0 += 1;
            } else if (s.palette == 1) {
                palette_list[index] = colors1[count1 % colors1.length];
                count1 += 1;
            }
        });
    }

    /* *********************************************** */

    /* Return the formatted serie name */
    function getSerieName$1(name, id, tsGroup, useFullName) {
        if (name == null) {
            name = id;
        }
        let name_more_space = "";
        if (name != null) {
            name_more_space = `${name}`;
        }
        if (useFullName == false) {
            return name;
        }
        let source_index = 0;
        let source_def_array = tsGroup.source_type.source_def_array;
        for (let i = 0; i < source_def_array.length; i += 1) {
            let source_def = source_def_array[i];
            if (source_def.main_source_def == true) {
                source_index = i;
                break;
            }
        }

        let source = tsGroup.source_array[source_index];
        let prefix = `${source.label}`;
        return `${prefix} - ${name_more_space}`;
    }

    /* *********************************************** */

    /* Given all the info about a timeserie, return the correct name to be displayed */
    function getName(ts_info, metadata) {
        let name = (metadata?.use_serie_name == true) ? ts_info.name : metadata.label;

        if (ts_info.ext_label) {
            name = ts_info.ext_label;
        }
        if (ts_info.label) {
            name = ts_info.label;
        }
        return { timeserie_name: name, show_full_name: (ts_info.label == null) }
    }

    /* *********************************************** */

    /* This function return the plotting function */
    function getPlotter(chart_type) {
        switch (chart_type) {
            case 'bar':
                return dygraphPlotters.barChartPlotter;
        }
    }

    /* *********************************************** */

    function addNewSerie(serie_name, chart_type, color, config) {
        config.labels.push(serie_name);
        if (config.properties == null)
            config.properties = {};
        config.properties[serie_name] = {};
        config.properties[serie_name] = dygraphConfig.formatSerieProperties(chart_type);
        config.colors.push(color);
    }

    /* *********************************************** */

    /* This function given a serie, format the array needed */
    function compactSerie(config, ts_info, extra_timeseries, serie, past_serie, scalar, step, epoch_begin, names) {
        const avg_value = ts_info.statistics["average"];
        const perc_value = ts_info.statistics["95th_percentile"];
        let time = epoch_begin;

        /* Now format the timeserie */
        for (let point = 0; point < serie.length; point++) {
            const serie_point = serie[point];
            /* If the point is inserted for the first time, add the time before everything else */
            if (!config.serie[time]) {
                config.serie[time] = [{ value: new Date(time * 1000), name: "Time" }];
            }
            /* Add the point to the array or NaN if it's null */
            (serie_point !== null) ?
                config.serie[time].push({ value: serie_point * scalar, name: names.serie_name }) :
                config.serie[time].push({ value: NaN, name: names.serie_name });

            /* Add extra series, avg, 95th and past timeseries */
            if (extra_timeseries?.avg == true) {
                config.serie[time].push({ value: avg_value * scalar, name: names.avg_name });
            }
            if (extra_timeseries?.perc_95 == true) {
                config.serie[time].push({ value: perc_value * scalar, name: names.perc_name });
            }
            if (extra_timeseries?.past == true) {
                const past_value = (past_serie) ? past_serie[point] : null;
                (past_value) ?
                    config.serie[time].push({ value: past_value * scalar, name: names.past_name }) :
                    config.serie[time].push({ value: NaN, name: names.past_label });
            }

            /* Increase the time using the step */
            time = time + step;
        }
    }

    /* *********************************************** */

    /* This function format the Bound type serie in the correct format */
    function splitBoundSerie(series, timeserie_info) {
        let color = {};
        let formatter = null;
        let serie_name = null;
        let properties = {};
        let full_serie = [];

        /* A bound timeserie should be composed by 3 timeseries:
         *    - metric (main)
         *    - lower_bound (the lower bound)
         *    - upper_bound (the upper bound)
         */
        series.forEach((ts_info, j) => {
            const ts_id = getSerieId$1(ts_info);
            const serie = ts_info.data || []; /* Safety check */
            const metadata = timeserie_info.metric.timeseries[ts_id];
            const scalar = (metadata?.invert_direction === true) ? -1 : 1;

            /* Just add the name, properties, colors, ecc, for the 
             * "main" timeserie and not for the bounds ones 
             */
            if (metadata.type == "metric") {
                serie_name = getSerieName$1(metadata.label, ts_id, timeserie_info, true);
                properties = dygraphConfig.formatSerieProperties('bounds');
                color = { color: metadata.color, palette: 0 };
                formatter = timeserie_info.metric.measure_unit;
            }
            for (let point = 0; point < serie.length; point++) {
                let serie_point = (serie[point] === null) ? NaN : serie[point];
                if (full_serie[point] == null) {
                    full_serie[point] = [0, NaN, 0];
                }

                if (metadata.type == "lower_bound") {
                    full_serie[point][0] = serie_point * scalar;
                } else if (metadata.type == "metric") {
                    full_serie[point][1] = serie_point * scalar;
                } else if (metadata.type == "upper_bound") {
                    full_serie[point][2] = serie_point * scalar;
                }
            }
        });

        return { serie: full_serie, color: color, formatter: formatter, serie_name: serie_name, properties: properties };
    }

    /* *********************************************** */

    /* This function, given a serie format the bounds serie */
    function formatBoundsSerie(timeserie_info, timeserie_options, config) {
        /* By default the chart type is line */
        const chart_type = timeserie_info.metric.chart_type || "filled";
        const series = timeserie_options.series || [];
        const epoch_begin = timeserie_options.metadata.epoch_begin;
        const step = timeserie_options.metadata.epoch_step;
        const { serie, color, formatter, serie_name, properties } = splitBoundSerie(series, timeserie_info);
        let time = epoch_begin;
        /* TODO: add avg, past, ecc. timeseries to the bounds one */

        /* Update the config */
        const formatted_name = `${serie_name} ${i18n('lower_value_upper')}`;
        const formatter_found = config.formatters.find(el => el == formatter);
        if (!formatter_found)
            config.formatters.push(formatter);
        config.plotter = getPlotter(chart_type);
        config.customBars = true;
        config.colors.push(color);
        config.labels.push(formatted_name);
        config.properties[formatted_name] = properties;

        /* Update the serie */
        Object.keys(serie).forEach((key) => {
            if (!config.serie[time]) {
                config.serie[time] = [
                    { value: new Date(time * 1000), name: "Time" },
                    { value: serie[key], name: formatted_name }
                ];
            }

            time = time + step;
        });
    }

    /* *********************************************** */

    function formatStandardSerie(timeserie_info, timeserie_options, config, tsCompare) {
        /* Iterate all the timeseries currently contained inside the single ts:
         * e.g. in the Traffic timeseries we have the Bytes sent and Bytes rcvd
         */
        const series = timeserie_options.series || [];
        const chart_type = timeserie_info.metric.chart_type || "filled";
        const epoch_begin = timeserie_options.metadata.epoch_begin;
        const step = timeserie_options.metadata.epoch_step;
        const formatter = timeserie_info.metric.measure_unit;
        const max_value = timeserie_info.metric.max_value || null;
        const min_value = timeserie_info.metric.min_value || null;
        const past_serie = timeserie_options.additional_series;
        let disable_past_ts = false;
        let disable_perc_95_ts = false;

        config.value_range = [min_value, max_value];
        config.plotter = getPlotter(chart_type);
        config.block_stacked = false;
        if (!config.stacked) {
            config.stacked = timeserie_info.metric.draw_stacked || false;
        }
        if (timeserie_info.metric.disable_default_ago_ts) {
            disable_past_ts = timeserie_info.metric.disable_default_ago_ts || true;
        }
        if (timeserie_info.metric.disable_perc_95_ts) {
            disable_perc_95_ts = timeserie_info.metric.disable_perc_95_ts || true;
        }

        series.forEach((ts_info, j) => {
            const serie = ts_info.data || []; /* Safety check */
            const extra_timeseries = timeserie_info.timeseries[0]; /* e.g. the Average */
            const ts_id = getSerieId$1(ts_info);
            const metadata = timeserie_info.metric.timeseries[ts_id];
            const scalar = (metadata?.invert_direction === true) ? -1 : 1;
            config.block_stacked = (metadata?.invert_direction === true) ? (true ) : (config.block_stacked);
            const { timeserie_name, show_full_name } = getName(ts_info, metadata);
            /* Check if show_full_name is null or undefined */
            const serie_name = getSerieName$1(timeserie_name, ts_id, timeserie_info, (show_full_name !== null || show_full_name !== undefined) ? (config.use_full_name && show_full_name) : config.use_full_name);
            const avg_name = getSerieName$1(timeserie_name + " Avg", ts_id, timeserie_info, config.use_full_name);
            const perc_name = getSerieName$1(timeserie_name + " 95th Perc", ts_id, timeserie_info, config.use_full_name);
            const past_name = getSerieName$1(timeserie_name + " " + tsCompare + " " + i18n('details.ago'), ts_id, timeserie_info, config.use_full_name);
            const past_value = (past_serie) ? past_serie[`${tsCompare}_ago`]?.series[j]?.data : null;
            /* An option used to not display a timeserie */
            if (metadata.hidden) {
                return;
            }

            /* Search for the formatter in the array, if not found, add it. */
            const formatter_found = config.formatters.find(el => el == formatter);
            if (!formatter_found)
                config.formatters.push(formatter);

            /* Add the serie */
            addNewSerie(serie_name, chart_type, { color: metadata.color, palette: 0 }, config);

            /* Adding the extra timeseries, 30m ago, avg and 95th */
            if (extra_timeseries?.avg == true) {
                addNewSerie(avg_name, "point", { color: constant_serie_colors["avg"], palette: 1 }, config);
            }
            if (extra_timeseries?.perc_95 == true && !disable_perc_95_ts) {
                addNewSerie(perc_name, "point", { color: constant_serie_colors["perc_95"], palette: 1 }, config);
            }
            if (extra_timeseries?.past == true && !disable_past_ts) {
                addNewSerie(past_name, "dash", { color: constant_serie_colors["past"], palette: 1 }, config);
            }
            /* ************************************** */

            compactSerie(config, ts_info, extra_timeseries, serie, past_value, scalar, step, epoch_begin, {
                serie_name: serie_name,
                avg_name: avg_name,
                perc_name: perc_name,
                past_name: past_name
            });
        });
    }

    /* ************************************** */

    /* This function finally format the timeseries and compact it togheter */
    function formatFullSerie(config) {
        const full_serie = [];
        const serie_keys = Object.keys(config.serie);

        /* Iterate the serie and for each label, get the value and set to null in case it does not exists */
        serie_keys.forEach((key, index) => {
            full_serie[index] = [];
            config.labels.forEach((label) => {
                let found = false;
                for (let j = 0; j < config.serie[key].length; j++) {
                    if (config.serie[key][j].name == label) {
                        full_serie[index].push(config.serie[key][j].value);
                        found = true;
                        break;
                    }
                }

                /* Push null if no value is found */
                if (found == false) {
                    full_serie[index].push(null);
                }
            });
        });
        config.serie = full_serie;
    }

    /* ************************************** */

    function formatSingleSerie(timeserie_info, timeserie_options, tsCompare, config) {
        if (timeserie_info.source_type.f_map_ts_options != null) {
            const f_map_ts_options = timeserie_info.source_type.f_map_ts_options;
            timeserie_options = f_map_ts_options(timeserie_options, timeserie_info);
        }

        /* Format the data */

        /* the data in Dygraphs should be formatted as follow:
         * { [ time_1, serie1_1, serie2_1 ], [ time_2, serie1_2, serie2_2 ] } 
         */
        const bounds = timeserie_info.metric.bounds || false;

        /* The serie can possibly have multiple timeseries, like for the 
         * bytes, we have sent and rcvd, so compact them 
         */
        if (bounds == true) {
            formatBoundsSerie(timeserie_info, timeserie_options, config);
        } else {
            formatStandardSerie(timeserie_info, timeserie_options, config, tsCompare);
        }
    }

    /* *********************************************** */

    function formatSimpleSerie$1(data, serie_name, chart_type, formatters, value_range) {
        let counter = 1;
        const tmp_serie = [];
        data.serie.forEach((value) => {
            tmp_serie.push([counter, value]);
            counter++;
        });

        /* To not have an error, just add a null value */
        if (tmp_serie.length == 0) {
            tmp_serie.push([1, null]);
        }

        const config = {
            serie: tmp_serie,
            formatters: formatters,
            labels: ["index"],
            colors: [],
            stacked: false,
            customBars: false,
            use_full_name: false,
            plotter: getPlotter(chart_type),
            value_range: value_range,
            disable_ts_list: true,
        };

        if (typeof (serie_name) === "string") {
            addNewSerie(serie_name, chart_type, { color: constant_serie_colors["default_color"], palette: 0 }, config);
        } else {
            serie_name.forEach((el) => {
                addNewSerie(el, chart_type, { color: constant_serie_colors["default_color"], palette: 0 }, config);
            });
        }
        formatSerieColors(config.colors);
        return dygraphConfig.buildChartOptions(config);
    }

    /* *********************************************** */

    function formatSerie(tsOptionsArray, tsGroupsArray, tsCompare, useFullName) {
        const config = {
            serie: [],
            formatters: [],
            labels: ["Time"],
            colors: [],
            properties: [],
            stacked: false,
            customBars: false,
            use_full_name: (useFullName != null) ? useFullName : false
        };

        /* Go throught each serie */
        tsOptionsArray.forEach((tsOptions, i) => {
            formatSingleSerie(tsGroupsArray[i], tsOptions, tsCompare, config);
        });

        /* Need to finally format the serie as requested by Dygraph, with
           NULL as value in case the serie has NOT THAT POINT (e.g. with a 5 minutes frequency, the user
            is confronting a chart with 1 minute frequency, there are 4 minutes with no existing points)
         */
        formatFullSerie(config);
        formatSerieColors(config.colors);
        return dygraphConfig.buildChartOptions(config);
    }

    /* *********************************************** */

    const dygraphFormat = function () {
        return {
            formatSerie,
            formatSimpleSerie: formatSimpleSerie$1,
            getSerieId: getSerieId$1,
            getSerieName: getSerieName$1,
        };
    }();

    /**
    		(C) 2023 - ntop.org
    */

    /* Override Dygraph plugins to have a better legend */
    Dygraph.Plugins.Legend.prototype.select = function (e) {
    	var xValue = e.selectedX;
    	var points = e.selectedPoints;
    	var row = e.selectedRow;

    	var legendMode = e.dygraph.getOption('legend');
    	if (legendMode === 'never') {
    		this.legend_div_.style.display = 'none';
    		return;
    	}

    	var html = Dygraph.Plugins.Legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_, row);
    	if (html instanceof Node && html.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
    		this.legend_div_.innerHTML = '';
    		this.legend_div_.appendChild(html);
    	} else
    		this.legend_div_.innerHTML = html;
    	// must be done now so offsetWidth isn’t 0…
    	this.legend_div_.style.display = '';

    	if (legendMode === 'follow') {
    		// create floating legend div
    		var area = e.dygraph.plotter_.area;
    		var labelsDivWidth = this.legend_div_.offsetWidth;
    		var yAxisLabelWidth = e.dygraph.getOptionForAxis('axisLabelWidth', 'y');
    		// find the closest data point by checking the currently highlighted series,
    		// or fall back to using the first data point available
    		var highlightSeries = e.dygraph.getHighlightSeries();
    		var point;
    		if (highlightSeries) {
    			point = points.find(p => p.name === highlightSeries);
    			if (!point)
    				point = points[0];
    		} else
    			point = points[0];

    		// determine floating [left, top] coordinates of the legend div
    		// within the plotter_ area
    		// offset 50 px to the right and down from the first selection point
    		// 50 px is guess based on mouse cursor size
    		const followOffsetX = e.dygraph.getNumericOption('legendFollowOffsetX');
    		const x = (point?.x != null) ? point.x : 1;
    		var leftLegend = x * area.w + followOffsetX;

    		// if legend floats to end of the chart area, it flips to the other
    		// side of the selection point
    		if ((leftLegend + labelsDivWidth + 1) > area.w) {
    			leftLegend = leftLegend - 2 * followOffsetX - labelsDivWidth - (yAxisLabelWidth - area.x);
    		}

    		this.legend_div_.style.left = yAxisLabelWidth + leftLegend + "px";
    		document.addEventListener("mousemove", (e) => {
    			localStorage.setItem('timeseries-mouse-top-position', e.clientY + 50 + "px");
    		});
    		this.legend_div_.style.top = localStorage.getItem('timeseries-mouse-top-position');
    	} else if (legendMode === 'onmouseover' && this.is_generated_div_) {
    		// synchronise this with Legend.prototype.predraw below
    		var area = e.dygraph.plotter_.area;
    		var labelsDivWidth = this.legend_div_.offsetWidth;
    		this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px";
    		this.legend_div_.style.top = area.y + "px";
    	}
    };

    var css_248z$u = ".dygraph-legend{position:absolute;font-size:14px;z-index:10;width:250px;background:#fff;line-height:normal;text-align:left;overflow:hidden}.dygraph-legend[dir=rtl]{text-align:right}.dygraph-legend-line{display:inline-block;position:relative;bottom:.5ex;padding-left:1em;height:1px;border-bottom-width:2px;border-bottom-style:solid}.dygraph-legend-dash{display:inline-block;position:relative;bottom:.5ex;height:1px;border-bottom-width:2px;border-bottom-style:solid}.dygraph-roller{position:absolute;z-index:10}.dygraph-annotation{position:absolute;z-index:10;overflow:hidden}.dygraph-default-annotation{border:1px solid #000;background-color:#fff;text-align:center}.dygraph-axis-label{z-index:10;line-height:normal;overflow:hidden;color:#000}.dygraph-title{font-weight:700;z-index:10;text-align:center}.dygraph-xlabel{text-align:center}.dygraph-label-rotate-left{text-align:center;transform:rotate(90deg);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-o-transform:rotate(90deg);-ms-transform:rotate(90deg)}.dygraph-label-rotate-right{text-align:center;transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-o-transform:rotate(-90deg);-ms-transform:rotate(-90deg)}";
    styleInject(css_248z$u);

    /*jslint vars: true, nomen: true, plusplus: true, maxerr: 500, indent: 4 */

    Dygraph.Export = {};

    Dygraph.Export.DEFAULT_ATTRS = {
      backgroundColor: "white",

      //Texts displayed below the chart's x-axis and to the left of the y-axis 
      titleFont: "bold 18px system-ui",
      titleFontColor: "black",

      //Texts displayed below the chart's x-axis and to the left of the y-axis 
      axisLabelFont: "bold 12px system-ui",
      axisLabelFontColor: "black",

      // Texts for the axis ticks
      labelFont: "normal 12px system-ui",
      labelFontColor: "black",

      // Text for the chart legend
      legendFont: "bold 14px system-ui",
      legendFontColor: "black",

      // Default position for vertical labels
      vLabelLeft: 20,

      legendHeight: 20,    // Height of the legend area
      legendMargin: 20,
      lineHeight: 30,
      maxlabelsWidth: 0,
      labelTopMargin: 35,
      magicNumbertop: 8
    };

    Dygraph.Export.DARK_MODE_DEFAULT_ATTRS = {
      backgroundColor: "transparent",

      //Texts displayed below the chart's x-axis and to the left of the y-axis 
      titleFont: "bold 18px system-ui",
      titleFontColor: "white",

      //Texts displayed below the chart's x-axis and to the left of the y-axis 
      axisLabelFont: "bold 12px system-ui",
      axisLabelFontColor: "white",

      // Texts for the axis ticks
      labelFont: "normal 12px system-ui",
      labelFontColor: "white",

      // Text for the chart legend
      legendFont: "bold 14px system-ui",
      legendFontColor: "white",

      // Default position for vertical labels
      vLabelLeft: 20,

      legendHeight: 20,    // Height of the legend area
      legendMargin: 20,
      lineHeight: 30,
      maxlabelsWidth: 0,
      labelTopMargin: 35,
      magicNumbertop: 8
    };

    /**
     * Tests whether the browser supports the canvas API and its methods for 
     * drawing text and exporting it as a data URL.
     */
    Dygraph.Export.isSupported = function () {
      try {
        var canvas = document.createElement("canvas");
        var context = canvas.getContext("2d");
        return (!!canvas.toDataURL && !!context.fillText);
      } catch (e) {
        // Silent exception.
      }
      return false;
    };

    /**
     * Exports a dygraph object as a PNG image.
     *
     *  dygraph: A Dygraph object
     *  img: An IMG DOM node
     *  userOptions: An object with the user specified options.
     *
     */
    Dygraph.Export.asPNG = function (dygraph, img, userOptions) {
      var canvas = Dygraph.Export.asCanvas(dygraph, userOptions);
      img.src = canvas.toDataURL();
    };

    /**
     * Exports a dygraph into a single canvas object.
     *
     * Returns a canvas object that can be exported as a PNG.
     *
     *  dygraph: A Dygraph object
     *  userOptions: An object with the user specified options.
     *
     */
    Dygraph.Export.update = function (self, o) {
      if (typeof (o) != 'undefined' && o !== null) {
        for (var k in o) {
          if (o.hasOwnProperty(k)) {
            self[k] = o[k];
          }
        }
      }
      return self;
    };

    Dygraph.Export.asCanvas = function (dygraph, userOptions) {
      var options = {},
        canvas = document.createElement('canvas');

      if (document.getElementsByClassName('body dark').length > 0) {
        Dygraph.Export.update(options, Dygraph.Export.DARK_MODE_DEFAULT_ATTRS);
      } else {
        Dygraph.Export.update(options, Dygraph.Export.DEFAULT_ATTRS);
      }
      Dygraph.Export.update(options, userOptions);

      canvas.width = dygraph.width_;
      canvas.height = dygraph.height_ + options.legendHeight;

      Dygraph.Export.drawPlot(canvas, dygraph, options);
      Dygraph.Export.drawLegend(canvas, dygraph, options);

      return canvas;
    };

    /**
     * Adds the plot and the axes to a canvas context.
     */
    Dygraph.Export.drawPlot = function (canvas, dygraph, options) {
      var ctx = canvas.getContext("2d");

      // Add user defined background
      ctx.fillStyle = options.backgroundColor;
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      // Copy the plot canvas into the context of the new image.
      var plotCanvas = dygraph.hidden_;

      let i = 0;

      ctx.drawImage(plotCanvas, 0, 0);

      // Add the x and y axes
      var axesPluginDict = Dygraph.Export.getPlugin(dygraph, 'Axes Plugin');
      if (axesPluginDict) {
        var axesPlugin = axesPluginDict.plugin;

        for (i = 0; i < axesPlugin.ylabels_.length; i++) {
          Dygraph.Export.putLabel(ctx, axesPlugin.ylabels_[i], options,
            options.labelFont, options.labelFontColor);
        }

        for (i = 0; i < axesPlugin.xlabels_.length; i++) {
          Dygraph.Export.putLabel(ctx, axesPlugin.xlabels_[i], options,
            options.labelFont, options.labelFontColor);
        }
      }

      // Title and axis labels

      var labelsPluginDict = Dygraph.Export.getPlugin(dygraph, 'ChartLabels Plugin');
      if (labelsPluginDict) {
        var labelsPlugin = labelsPluginDict.plugin;

        Dygraph.Export.putLabel(ctx, labelsPlugin.title_div_, options,
          options.titleFont, options.titleFontColor);

        Dygraph.Export.putLabel(ctx, labelsPlugin.xlabel_div_, options,
          options.axisLabelFont, options.axisLabelFontColor);

        Dygraph.Export.putVerticalLabelY1(ctx, labelsPlugin.ylabel_div_, options,
          options.axisLabelFont, options.axisLabelFontColor, "center");

        Dygraph.Export.putVerticalLabelY2(ctx, labelsPlugin.y2label_div_, options,
          options.axisLabelFont, options.axisLabelFontColor, "center");
      }

      for (i = 0; i < dygraph.layout_.annotations.length; i++) {
        Dygraph.Export.putLabelAnn(ctx, dygraph.layout_.annotations[i], options,
          options.labelFont, options.labelColor);
      }

    };

    /**
     * Draws a label (axis label or graph title) at the same position 
     * where the div containing the text is located.
     */
    Dygraph.Export.putLabel = function (ctx, divLabel, options, font, color) {

      if (!divLabel || !divLabel.style) {
        return;
      }

      var top = parseInt(divLabel.style.top, 10);
      var left = parseInt(divLabel.style.left, 10);

      if (!divLabel.style.top.length) {
        var bottom = parseInt(divLabel.style.bottom, 10);
        var height = parseInt(divLabel.style.height, 10);

        top = ctx.canvas.height - options.legendHeight - bottom - height;
      }

      // FIXME: Remove this 'magic' number needed to get the line-height. 
      top = top + options.magicNumbertop;

      var width = parseInt(divLabel.style.width, 10);

      switch (divLabel.style.textAlign) {
        case "center":
          left = left + Math.ceil(width / 2);
          break;
        case "right":
          left = left + width;
          break;
      }

      Dygraph.Export.putText(ctx, left, top, divLabel, font, color);
    };

    /**
     * Draws a label Y1 rotated 90 degrees counterclockwise.
     */
    Dygraph.Export.putVerticalLabelY1 = function (ctx, divLabel, options, font, color, textAlign) {
      if (!divLabel) {
        return;
      }

      var top = parseInt(divLabel.style.top, 10);
      var left = parseInt(divLabel.style.left, 10) + parseInt(divLabel.style.width, 10) / 2;
      var text = divLabel.innerText || divLabel.textContent;


      // FIXME: The value of the 'left' property is frequently 0, used the option.
      if (!left)
        left = options.vLabelLeft;

      if (textAlign == "center") {
        var textDim = ctx.measureText(text);
        top = Math.ceil((ctx.canvas.height - textDim.width) / 2 + textDim.width);
      }

      ctx.save();
      ctx.translate(0, ctx.canvas.height);
      ctx.rotate(-Math.PI / 2);

      ctx.fillStyle = color;
      ctx.font = font;
      ctx.textAlign = textAlign;
      ctx.fillText(text, top, left);

      ctx.restore();
    };

    /**
     * Draws a label Y2 rotated 90 degrees clockwise.
     */
    Dygraph.Export.putVerticalLabelY2 = function (ctx, divLabel, options, font, color, textAlign) {
      if (!divLabel) {
        return;
      }

      var top = parseInt(divLabel.style.top, 10);
      var right = parseInt(divLabel.style.right, 10) + parseInt(divLabel.style.width, 10) * 2;
      var text = divLabel.innerText || divLabel.textContent;

      if (textAlign == "center") {
        top = Math.ceil(ctx.canvas.height / 2);
      }

      ctx.save();
      ctx.translate(parseInt(divLabel.style.width, 10), 0);
      ctx.rotate(Math.PI / 2);

      ctx.fillStyle = color;
      ctx.font = font;
      ctx.textAlign = textAlign;
      ctx.fillText(text, top, right - ctx.canvas.width);

      ctx.restore();
    };

    /**
     * Draws the text contained in 'divLabel' at the specified position.
     */
    Dygraph.Export.putText = function (ctx, left, top, divLabel, font, color) {
      var textAlign = divLabel.style.textAlign || "left";
      var text = divLabel.innerText || divLabel.textContent;

      ctx.fillStyle = color;
      ctx.font = font;
      ctx.textAlign = textAlign;
      ctx.textBaseline = "middle";
      ctx.fillText(text, left, top);
    };

    /**
     * Draws the legend of a dygraph
     *
     */
    Dygraph.Export.drawLegend = function (canvas, dygraph, options) {
      var ctx = canvas.getContext("2d");

      // Margin from the plot
      var labelTopMargin = 10;

      // Margin between labels
      var labelMargin = 5;

      var colors = dygraph.getColors();
      // Drop the first element, which is the label for the time dimension
      var labels = dygraph.attr_("labels").slice(1);

      // 1. Compute the width of the labels:
      var labelsWidth = 0;

      var i;
      for (i = 0; i < labels.length; i++) {
        labelsWidth = labelsWidth + ctx.measureText("- " + labels[i]).width + labelMargin;
      }

      var labelsX = Math.floor((canvas.width - labelsWidth) / 2);
      var labelsY = canvas.height - options.legendHeight + labelTopMargin;


      var labelVisibility = dygraph.attr_("visibility");

      ctx.font = options.legendFont;
      ctx.textAlign = "left";
      ctx.textBaseline = "middle";

      var usedColorCount = 0;
      for (i = 0; i < labels.length; i++) {
        if (labelVisibility[i]) {
          //TODO Replace the minus sign by a proper dash, although there is a
          //     problem when the page encoding is different than the encoding 
          //     of this file (UTF-8).
          var txt = "- " + labels[i];
          ctx.fillStyle = colors[usedColorCount];
          usedColorCount++;
          ctx.fillText(txt, labelsX, labelsY);
          labelsX = labelsX + ctx.measureText(txt).width + labelMargin;
        }
      }
    };

    /**
     * Finds a plugin by the value returned by its toString method..
     *
     * Returns the the dictionary corresponding to the plugin for the argument.
     * If the plugin is not found, it returns null.
     */
    Dygraph.Export.getPlugin = function (dygraph, name) {
      for (let i = 0; i < dygraph.plugins_.length; i++) {
        if (dygraph.plugins_[i].plugin.toString() == name) {
          return dygraph.plugins_[i];
        }
      }
      return null;
    };

    var script$2i = {
        components: {
        },
        props: {
            id: String,
            chart_type: String,
            register_on_status_change: Boolean,
            base_url_request: String,
            get_params_url_request: Function,
            get_custom_chart_options: Function,
            disable_fixed_height: Boolean,
        },
        emits: ["apply", "hidden", "showed", "chart_reloaded", "zoom"],
        /** This method is the first method of the component called, it's called before html template creation. */
        created() { },
        beforeUnmount() { },
        data() {
            return {
                chart: null,
                stacked: null,
                block_stacked: false,
                chart_options: null,
                from_zoom: false,
                reload: false,
                timeseries_visibility: null,
                timeseries_list: [],
                last_url_request: '',
                i18n: (t) => i18n(t),
            };
        },
        /** This method is the first method called after html template creation. */
        async mounted() {
            await this.init();
            ntopng_sync.ready(this.$props["id"]);
        },
        methods: {
            init: async function () {
                let status = ntopng_status_manager.get_status();
                let url_request = this.get_url_request(status);
                if (this.register_on_status_change) {
                    this.register_status(status);
                }
                await this.draw_chart(url_request);
            },
            get_image: function (image) {
                return Dygraph.Export.asPNG(this.chart, image, this.$refs["chart"]);
            },
            changeStacked: function () {
                this.stacked = !this.stacked;
                localStorage.setItem('ntopng.timeseries.chartStackedOption.' + this.$props["id"], this.stacked);
                this.updateIntoStackedChart(this.stacked);
            },
            change_visibility: function (visible, id) {
                if (this.timeseries_list[id] != null) {
                    this.timeseries_list[id]["checked"] = visible;
                    this.chart.setVisibility(id, visible);
                }
            },
            register_status: function (status) {
                let url_request = this.get_url_request(status);
                ntopng_status_manager.on_status_change(this.id, (new_status) => {
                    if (this.from_zoom == true) {
                        this.from_zoom = false;
                        //return;
                    }
                    let new_url_request = this.get_url_request(new_status);
                    if (new_url_request == url_request) {
                        url_request = new_url_request;
                        return;
                    }
                    url_request = new_url_request;
                    this.update_chart(new_url_request);
                }, false);
            },
            get_url_request: function (status) {
                let url_params;
                if (this.$props.get_params_url_request != null) {
                    if (status == null) {
                        status = ntopng_status_manager.get_status();
                    }
                    url_params = this.$props.get_params_url_request(status);
                } else {
                    url_params = ntopng_url_manager.get_url_params();
                }

                return `${this.$props.base_url_request || ''}?${url_params}`;
            },
            ensure_path: function (obj, path) {
                return path.reduce((acc, key) => (acc[key] ??= {}), obj);
            },
            get_chart_options: async function (url_request) {
                let chart_options = {};
                const date_format = await ntopng_utility.get_date_format(false, this.$props.csrf, http_prefix);

                /* Retrieve the chart options */
                if (this.$props.get_custom_chart_options == null) {
                    chart_options = await ntopng_utility.http_request(url_request);
                } else {
                    chart_options = await this.$props.get_custom_chart_options(url_request);
                }
                if (!chart_options) {
                    chart_options = {};
                }
                const xAxis = this.ensure_path(chart_options, ["axes", "x"]);
                /* Set the date depending on the server date */
                xAxis.axisLabelFormatter ??= function (date) {
                    return ntopng_utility.from_utc_to_server_date_format(date, date_format);
                };
                xAxis.valueFormatter ??= function (date) {
                    return ntopng_utility.from_utc_to_server_date_format(date, date_format);
                };
                /* Emit the chart_reloaded event */
                this.$emit('chart_reloaded', chart_options);
                if (this.stacked === null && !chart_options.blockStacked) {
                    // First loading of the chart
                    const is_stacked_option_found = localStorage.getItem('ntopng.timeseries.chartStackedOption.' + this.$props["id"]);
                    if (is_stacked_option_found !== null) {
                        this.stacked = (is_stacked_option_found == 'true');
                        chart_options.stackedGraph = this.stacked;
                    } else {
                        this.stacked = chart_options.stackedGraph;
                    }
                } else {
                    if (chart_options.blockStacked)
                        this.stacked = false;
                    chart_options.stackedGraph = this.stacked;
                }
                return chart_options;
            },
            draw_chart: async function (url_request) {
                let chart_options = await this.get_chart_options(url_request);
                const data = chart_options.data || [];
                chart_options.data = null;
                chart_options.zoomCallback = this.on_zoomed;
                if (chart_options.blockStacked) {
                    this.block_stacked = true;
                }
                this.timeseries_list = [];
                let id = 0;
                if (!chart_options.disableTsList) {
                    for (const key in chart_options.series) {
                        this.timeseries_list.push({ name: key, checked: true, id: id, color: chart_options.colors[id] + "!important" });
                        id = id + 1;
                    }
                }
                this.chart = new Dygraph(this.$refs["chart"], data, chart_options);
            },
            update_chart: async function (url_request) {
                if (this.chart) {
                    let chart_options = await this.get_chart_options(url_request);
                    this.chart.updateChart(chart_options);
                }
            },
            update_chart_options: function (chart_options) {
                if (this.chart) {
                    this.chart.updateChart(chart_options);
                }
            },
            updateIntoStackedChart: function (stacked) {
                if (this.chart) {
                    this.chart.updateOptions({ 'stackedGraph': stacked });
                }
            },
            update_chart_series: function (series) {
                if (series == null) { return; }
                if (this.chart) {
                    this.chart.updateOptions({ 'file': series });
                }
            },
            on_zoomed: function (minDate, maxDate) {
                this.from_zoom = true;
                const begin = moment(minDate);
                const end = moment(maxDate);
                // the timestamps are in milliseconds, convert them into seconds
                let new_epoch_status = { epoch_begin: Number.parseInt(begin.unix()), epoch_end: Number.parseInt(end.unix()) };
                ntopng_events_manager.emit_event(ntopng_events.EPOCH_CHANGE, new_epoch_status, this.id);
                this.$emit('zoom', new_epoch_status);
            },
        },
    };

    const _hoisted_1$25 = { style: {"overflow-x":"auto","white-space":"nowrap","margin-bottom":"10px","min-height":"31px"} };
    const _hoisted_2$1U = { class: "d-flex align-items-center" };
    const _hoisted_3$1I = ["title"];
    const _hoisted_4$1F = ["checked", "disabled"];
    const _hoisted_5$1p = { class: "form-check-label" };
    const _hoisted_6$1a = { class: "ms-auto" };
    const _hoisted_7$13 = { class: "form-check-label form-control-sm" };
    const _hoisted_8$V = ["onClick", "checked"];
    const _hoisted_9$K = {
      key: 0,
      class: "mb-3",
      style: {"width":"100%"},
      ref: "chart"
    };
    const _hoisted_10$E = {
      key: 1,
      class: "mb-3",
      style: {"width":"100%","min-height":"320px"},
      ref: "chart"
    };

    function render$c(_ctx, _cache, $props, $setup, $data, $options) {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$25, [
          createCommentVNode(" legend-wrapper "),
          createBaseVNode("div", _hoisted_2$1U, [
            createCommentVNode(" legend-div "),
            createBaseVNode("div", {
              class: "form-check form-switch form-control-sm ms-1",
              "data-bs-toggle": "tooltip",
              title: $data.block_stacked ? $data.i18n('stacked_blocked_title') : $data.i18n('stacked_unblocked_title')
            }, [
              createBaseVNode("input", {
                type: "checkbox",
                class: "form-check-input",
                onClick: _cache[0] || (_cache[0] = (...args) => ($options.changeStacked && $options.changeStacked(...args))),
                checked: $data.stacked,
                disabled: $data.block_stacked
              }, null, 8 /* PROPS */, _hoisted_4$1F),
              createBaseVNode("label", _hoisted_5$1p, toDisplayString($data.i18n('stacked')), 1 /* TEXT */)
            ], 8 /* PROPS */, _hoisted_3$1I),
            createBaseVNode("div", _hoisted_6$1a, [
              (openBlock(true), createElementBlock(Fragment, null, renderList($data.timeseries_list, (item, i) => {
                return (openBlock(), createElementBlock("label", _hoisted_7$13, [
                  createBaseVNode("input", {
                    type: "checkbox",
                    class: "form-check-input align-middle mt-0",
                    onClick: $event => ($options.change_visibility(!item.checked, i)),
                    checked: item.checked,
                    style: normalizeStyle([{"border-color":"#0d6efd"}, { backgroundColor: item.color }])
                  }, null, 12 /* STYLE, PROPS */, _hoisted_8$V),
                  createTextVNode(" " + toDisplayString(item.name), 1 /* TEXT */)
                ]))
              }), 256 /* UNKEYED_FRAGMENT */))
            ])
          ])
        ]),
        ($props.disable_fixed_height == true)
          ? (openBlock(), createElementBlock("div", _hoisted_9$K, null, 512 /* NEED_PATCH */))
          : (openBlock(), createElementBlock("div", _hoisted_10$E, null, 512 /* NEED_PATCH */))
      ], 64 /* STABLE_FRAGMENT */))
    }

    var css_248z$t = "\n.dygraph-legend {\n    color: #111111;\n    background-color: #FFFFFF !important;\n    border-color: #a7a6a6;\n    border-style: solid;\n    border-width: thin;\n    z-index: 80 !important;\n    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, .15);\n    border-radius: 0.375rem;\n    position: fixed;\n    width: auto;\n    word-wrap: break-word;\n    padding: 8px !important;\n}\n.dygraph-legend>span {\n    color: #111111;\n    padding-left: 5px;\n    padding-right: 2px;\n    margin-left: -5px;\n    background-color: #FFFFFF !important;\n}\n.dygraph-legend>span:first-child {\n    margin-top: 2px;\n}\n.dygraph-axis-label {\n    z-index: 10;\n    line-height: normal;\n    overflow: hidden;\n    color: var(--ntop-text-color);\n}\n";
    styleInject(css_248z$t);

    script$2i.render = render$c;
    script$2i.__file = "http_src/vue/timeseries-chart.vue";

    const _hoisted_1$24 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$1T = { class: "col-11" };
    const _hoisted_3$1H = { class: "col-form-label" };
    const _hoisted_4$1E = { class: "container" };
    const _hoisted_5$1o = { class: "row custom-ms me-2 mt-1" };
    const _hoisted_6$19 = { class: "col-5" };
    const _hoisted_7$12 = ["onUpdate:modelValue"];
    const _hoisted_8$U = { class: "custom-control-label ms-1 form-check-label" };
    const _hoisted_9$J = { class: "col-2" };
    const _hoisted_10$D = ["onUpdate:modelValue"];
    const _hoisted_11$A = { class: "col-2" };
    const _hoisted_12$v = ["onUpdate:modelValue"];
    const _hoisted_13$p = { class: "col-3" };
    const _hoisted_14$n = ["onUpdate:modelValue"];


    var script$2h = {
      __name: 'list-timeseries',
      props: {
        id: String,
        timeseries: Array,
        title: String,
        show_delete_button: Boolean,
    },
      emits: ['delete_ts', 'update:timeseries'],
      setup(__props, { emit: __emit }) {

    const props = __props;

    const emit = __emit;

    function update_timeseries() {
        emit('update:timeseries', props.timeseries);
    }

    function delete_ts() {
        emit('delete_ts', props.id);
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", null, [
        createBaseVNode("div", _hoisted_1$24, [
          createBaseVNode("div", _hoisted_2$1T, [
            createBaseVNode("label", _hoisted_3$1H, [
              createBaseVNode("b", null, toDisplayString(__props.title), 1 /* TEXT */)
            ])
          ]),
          (__props.show_delete_button)
            ? (openBlock(), createElementBlock("div", {
                key: 0,
                class: "col-1",
                onClick: delete_ts
              }, [...(_cache[0] || (_cache[0] = [
                createBaseVNode("button", {
                  type: "button",
                  class: "btn border"
                }, [
                  createBaseVNode("span", null, [
                    createBaseVNode("i", { class: "fas fa-trash" })
                  ])
                ], -1 /* CACHED */)
              ]))]))
            : createCommentVNode("v-if", true)
        ]),
        createBaseVNode("div", _hoisted_4$1E, [
          (openBlock(true), createElementBlock(Fragment, null, renderList(__props.timeseries, (item) => {
            return (openBlock(), createElementBlock("div", _hoisted_5$1o, [
              createBaseVNode("div", _hoisted_6$19, [
                withDirectives(createBaseVNode("input", {
                  type: "checkbox",
                  class: "custom-control-input whitespace form-check-input",
                  onChange: update_timeseries,
                  "onUpdate:modelValue": $event => ((item.raw) = $event)
                }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_7$12), [
                  [vModelCheckbox, item.raw]
                ]),
                createBaseVNode("label", _hoisted_8$U, toDisplayString(item.label), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_9$J, [
                withDirectives(createBaseVNode("input", {
                  type: "checkbox",
                  class: "custom-control-input whitespace form-check-input",
                  onChange: update_timeseries,
                  "onUpdate:modelValue": $event => ((item.past) = $event)
                }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_10$D), [
                  [vModelCheckbox, item.past]
                ]),
                _cache[1] || (_cache[1] = createBaseVNode("label", { class: "custom-control-label ms-1 form-check-label" }, "Past ", -1 /* CACHED */))
              ]),
              createBaseVNode("div", _hoisted_11$A, [
                withDirectives(createBaseVNode("input", {
                  type: "checkbox",
                  class: "custom-control-input whitespace form-check-input",
                  onChange: update_timeseries,
                  "onUpdate:modelValue": $event => ((item.avg) = $event)
                }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_12$v), [
                  [vModelCheckbox, item.avg]
                ]),
                _cache[2] || (_cache[2] = createBaseVNode("label", { class: "custom-control-label ms-1 form-check-label" }, "Avg ", -1 /* CACHED */))
              ]),
              createBaseVNode("div", _hoisted_13$p, [
                withDirectives(createBaseVNode("input", {
                  type: "checkbox",
                  class: "custom-control-input whitespace form-check-input",
                  onChange: update_timeseries,
                  "onUpdate:modelValue": $event => ((item.perc_95) = $event)
                }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_14$n), [
                  [vModelCheckbox, item.perc_95]
                ]),
                _cache[3] || (_cache[3] = createBaseVNode("label", { class: "custom-control-label ms-1 form-check-label" }, "95th Perc ", -1 /* CACHED */))
              ])
            ]))
          }), 256 /* UNKEYED_FRAGMENT */))
        ])
      ]))
    }
    }

    };

    var css_248z$s = "\n.custom-ms[data-v-185590b6] {\n  margin-left: 2rem !important;\n}\n.inline-block[data-v-185590b6] {\n    display: inline-block;\n    margin-right: 1rem;\n}\n.border[data-v-185590b6] {\n    border-style: solid !important;\n}\n";
    styleInject(css_248z$s);

    script$2h.__scopeId = "data-v-185590b6";
    script$2h.__file = "http_src/vue/list-timeseries.vue";

    const bytesToSizeFormatter$3 = formatterUtils.getFormatter(formatterUtils.types.bytes.id);
    const handlerIdAddLink$3 = "page-stats-action-link";
    const handlerIdJumpHistorical$1 = "page-stats-action-jump-historical";
    const handlerIdJumpLive$1 = "page-stats-action-jump-live";

    const top_application$1 = {
        table_value: "interface",
        title: i18n('page_stats.top.top_applications'),
        view: "top_protocols",
        default_sorting_columns: 1,
        default: true,
        columnDefs: [
            { type: "file-size", targets: 1 },
        ],
        columns: [{
            columnName: i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, handlerId: handlerIdJumpHistorical$1,
            render_if: function (context) { return context.is_history_enabled },
            render: function (data, type, service) {
                let context = this;
                const jump_to_historical = {
                    handlerId: handlerIdJumpHistorical$1,
                    onClick: function () {
                        const epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
                        const epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");
                        let l7_proto = ntopng_url_manager$1.serialize_param("l7proto", `${service.protocol.id};eq`);
                        let historical_flows_url = `${http_prefix}/lua/pro/db_search.lua?epoch_begin=${epoch_begin}&epoch_end=${epoch_end}&${l7_proto}`;
                        let source_type = context.source_type;
                        let source_array = context.source_array;

                        let params = "";
                        let params_array = source_type.source_def_array.map((source_def, i) => {
                            let source = source_array[i];
                            if (source_def.value == "ifid") {
                                return ntopng_url_manager$1.serialize_param("ifid", source.value);
                            } else if (source_def.value == "host") {
                                return ntopng_url_manager$1.serialize_param("ip", `${source.value};eq`);
                            }
                        });
                        params = params_array.join("&");
                        historical_flows_url = `${historical_flows_url}&${params}`;
                        window.open(historical_flows_url);
                    }
                };
                const jump_to_live = {
                    handlerId: handlerIdJumpLive$1,
                    onClick: function () {
                        let l7_proto = ntopng_url_manager$1.serialize_param("application", `${service.protocol.id}`);
                        let live_flows_url = `${http_prefix}/lua/flows_stats.lua?${l7_proto}`;
                        let source_type = context.source_type;
                        let source_array = context.source_array;

                        let params = "";
                        let params_array = source_type.source_def_array.map((source_def, i) => {
                            let source = source_array[i];
                            if (source_def.value == "ifid") {
                                return ntopng_url_manager$1.serialize_param("ifid", source.value);
                            } else if (source_def.value == "host") {
                                live_flows_url = `${http_prefix}/lua/host_details.lua?page=flows&${l7_proto}`;
                                return ntopng_url_manager$1.serialize_param("host", `${source.value}`);
                            }
                        });
                        params = params_array.join("&");
                        live_flows_url = `${live_flows_url}&${params}`;
                        window.open(live_flows_url);
                    }
                };
                return DataTableUtils$1.createActionButtons([
                    { class: 'dropdown-item', icon: 'fas fa-search-plus', href: '#', title: i18n('db_explorer.historical_data'), handler: jump_to_historical },
                    { class: 'dropdown-item', icon: 'fas fa-stream', href: '#', title: i18n('flows_page.live_flows'), handler: jump_to_live },
                ]);
            }
        }, {
            columnName: i18n("application"), name: 'application', data: 'protocol', handlerId: handlerIdAddLink$3,
            render: function (data, type, service) {
                let context = this;
                let handler = {
                    handlerId: handlerIdAddLink$3,
                    onClick: function () {
                        // console.log(data);
                        // console.log(service);
                        let schema = `top:${service.ts_schema}`;
                        context.add_metric_from_metric_schema(schema, service.ts_query);
                    },
                };
                return DataTableUtils$1.createLinkCallback({ text: data.label, handler });
            },
        }, {
            columnName: i18n("traffic"), name: 'traffic', className: 'text-end', data: 'traffic', orderable: true,
            render: (data, type) => {
                    if (!data) {
                        return '';
                    }
                    if (type === 'sort' || type === 'type') {
                        return data
                    }
                    return bytesToSizeFormatter$3(data);
                },
        }, {
            columnName: i18n("percentage"), name: 'traffic_perc', className: 'text-center', data: 'percentage', orderable: false,
            render: (data) => {
                if (!data)
                    return '';

                const percentage = data.toFixed(1);
                return NtopUtils$1.createProgressBar(percentage)
            }
        }],
    };

    const top_categories = {
        table_value: "interface",
        title: i18n('page_stats.top.top_categories'),
        view: "top_categories",
        default_sorting_columns: 2,
        columnDefs: [
            { type: "file-size", targets: 1 },
        ],
        columns: [{
            columnName: i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, handlerId: handlerIdJumpHistorical$1,
            render_if: function (context) { return context.is_history_enabled },
            render: function (data, type, service) {
                let context = this;
                const jump_to_historical = {
                    handlerId: handlerIdJumpHistorical$1,
                    onClick: function () {
                        const epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
                        const epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");
                        let category = ntopng_url_manager$1.serialize_param("l7cat", `${service.category.id};eq`);
                        let historical_flows_url = `${http_prefix}/lua/pro/db_search.lua?epoch_begin=${epoch_begin}&epoch_end=${epoch_end}&${category}`;
                        let source_type = context.source_type;
                        let source_array = context.source_array;

                        let params = "";
                        let params_array = source_type.source_def_array.map((source_def, i) => {
                            let source = source_array[i];
                            if (source_def.value == "ifid") {
                                return ntopng_url_manager$1.serialize_param("ifid", source.value);
                            } else if (source_def.value == "host") {
                                return ntopng_url_manager$1.serialize_param("ip", `${source.value};eq`);
                            }
                        });
                        params = params_array.join("&");
                        historical_flows_url = `${historical_flows_url}&${params}`;
                        // console.log(historical_flows_url);
                        window.open(historical_flows_url);
                    }
                };
                const jump_to_live = {
                    handlerId: handlerIdJumpLive$1,
                    onClick: function () {
                        let l7_category = ntopng_url_manager$1.serialize_param("application", `cat_${service.category.id}`);
                        let live_flows_url = `${http_prefix}/lua/flows_stats.lua?${l7_category}`;
                        let source_type = context.source_type;
                        let source_array = context.source_array;

                        let params = "";
                        let params_array = source_type.source_def_array.map((source_def, i) => {
                            let source = source_array[i];
                            if (source_def.value == "ifid") {
                                return ntopng_url_manager$1.serialize_param("ifid", source.value);
                            } else if (source_def.value == "host") {
                                live_flows_url = `${http_prefix}/lua/host_details.lua?page=flows&${l7_category}`;
                                return ntopng_url_manager$1.serialize_param("host", `${source.value}`);
                            }
                        });
                        params = params_array.join("&");
                        live_flows_url = `${live_flows_url}&${params}`;
                        window.open(live_flows_url);
                    }
                };
                return DataTableUtils$1.createActionButtons([
                    { class: 'dropdown-item', icon: 'fas fa-search-plus', href: '#', title: i18n('db_explorer.historical_data'), handler: jump_to_historical },
                    { class: 'dropdown-item', icon: 'fas fa-stream', href: '#', title: i18n('flows_page.live_flows'), handler: jump_to_live },
                ]);
            }
        }, {
            columnName: i18n("category"), name: 'category', data: 'category', handlerId: handlerIdAddLink$3,
            render: function (data, type, service) {
                let context = this;
                let handler = {
                    handlerId: handlerIdAddLink$3,
                    onClick: function () {
                        // console.log(data);
                        // console.log(service);
                        let schema = `top:${service.ts_schema}`;
                        context.add_metric_from_metric_schema(schema, service.ts_query);
                    },
                };
                return DataTableUtils$1.createLinkCallback({ text: data.label, handler });
            },
        }, {
            columnName: i18n("traffic"), name: 'traffic', className: 'text-end', data: 'traffic', orderable: true,
            render: (data, type) => {
                if (!data) {
                    return '';
                }
                if (type === 'sort' || type === 'type') {
                    return data
                }
                return bytesToSizeFormatter$3(data);
                //return NtopUtils.bytesToSize(data)
            },
        }, {
            columnName: i18n("percentage"), name: 'traffic_perc', className: 'text-center', data: 'percentage',
            render: (data) => {
                const percentage = data.toFixed(1);
                return NtopUtils$1.createProgressBar(percentage)
            }
        }],
    };

    const top_senders = {
        table_value: "interface",
        title: i18n('page_stats.top.top_senders'),
        view: "top_senders",
        default_sorting_columns: 1,
        columnDefs: [
            { type: "file-size", targets: 1 },
        ],
        columns: [
            {
                columnName: i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, handlerId: handlerIdJumpHistorical$1,
                render_if: function (context) { return context.is_history_enabled },
                render: function (data, type, service) {
                    let context = this;
                    const host = service.host.id;
                    const host_ts_available = service.host.is_local;
                    const jump_to_historical = {
                        handlerId: handlerIdJumpHistorical$1,
                        onClick: function () {
                            const epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
                            const epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");
                            let historical_flows_url = `${http_prefix}/lua/pro/db_search.lua?epoch_begin=${epoch_begin}&epoch_end=${epoch_end}`;

                            let params = "";
                            let params_array = [];
                            for (let key in service.tags) {
                                let value = service.tags[key];
                                let p_url = "";
                                if (key == "ifid") {
                                    p_url = ntopng_url_manager$1.serialize_param(key, value);
                                } else if (key == "host") {
                                    p_url = ntopng_url_manager$1.serialize_param("ip", `${value};eq`);
                                }
                                params_array.push(p_url);
                            }
                            params = params_array.join("&");
                            historical_flows_url = `${historical_flows_url}&${params}`;
                            window.open(historical_flows_url);
                        }
                    };
                    const jump_to_live = {
                        handlerId: handlerIdJumpLive$1,
                        onClick: function () {
                            let live_flows_url = `${http_prefix}/lua/host_details.lua?page=flows`;
                            let params = "";
                            let params_array = [];
                            for (let key in service.tags) {
                                let value = service.tags[key];
                                let p_url = "";
                                if (key == "ifid") {
                                    p_url = ntopng_url_manager$1.serialize_param(key, value);
                                } else if (key == "host") {
                                    p_url = ntopng_url_manager$1.serialize_param("host", `${value}`);
                                }
                                params_array.push(p_url);
                            }
                            params = params_array.join("&");
                            live_flows_url = `${live_flows_url}&${params}`;
                            window.open(live_flows_url);
                        }
                    };

                    const jump_to_host = {
                        handlerId: handlerIdJumpHistorical$1,
                        onClick: function () {
                            const ifid = ntopng_url_manager$1.get_url_entry('ifid');
                            const host_url = `${http_prefix}/lua/host_details.lua?host=${host}&page=historical&ts_query=ifid:${ifid},host:${host}&ts_schema=host:details&epoch_begin=${context.status.epoch_begin}&epoch_end=${context.status.epoch_end}`;

                            window.open(host_url);
                        }
                    };
                    const is_host_available = (service && service.host) ? service.host.is_available : false;
                    const dropdown = [
                        { class: 'dropdown-item', icon: 'fas fa-search-plus', href: '#', title: i18n('db_explorer.historical_data'), handler: jump_to_historical },
                        { class: is_host_available ? 'dropdown-item' : 'dropdown-item disabled', icon: 'fas fa-stream', href: '#', title: i18n('flows_page.live_flows'), handler: jump_to_live },
                    ];
                    if (context.sources_types_enabled["host"] && host_ts_available) {
                        dropdown.push({ class: 'dropdown-item', icon: 'fas fa-laptop', href: '#', title: i18n('db_explorer.host_data'), handler: jump_to_host });
                    }

                    return DataTableUtils$1.createActionButtons(dropdown);
                }
            }, {
                columnName: i18n("page_stats.top.host_name"), name: 'host_name', data: 'host', handlerId: handlerIdAddLink$3,
                render: function (data, type, service) {
                    let context = this;
                    let label = data.label;
                    let host_ref = '';
                    let handler = {
                        handlerId: handlerIdAddLink$3,
                        onClick: async function () {
                            let schema = `host:traffic`;
                            context.add_ts_group_from_source_value_dict("host", service.tags, schema);
                        },
                    };
                    if (context.sources_types_enabled["host"] && data.is_local) {
                        label = DataTableUtils$1.createLinkCallback({ text: data.label, handler });
                    }
                    if (data.is_available) {
                        host_ref = ` <a href="/lua/host_details.lua?host=${data.id}" data-bs-toggle="tooltip" title=""><i class="fas fa-laptop"></i></a>`;
                    }

                    return `${label}${host_ref}`;
                },
            }, {
                columnName: i18n("page_stats.top.sent"), name: 'sent', className: 'text-end', data: 'traffic', orderable: true,
                render: (data, type) => {
                    if (type === 'sort' || type === 'type') {
                        return data
                    }
                    return bytesToSizeFormatter$3(data);
                },
            }],
    };

    const top_receivers = {
        table_value: "interface",
        title: i18n('page_stats.top.top_receivers'),
        view: "top_receivers",
        default_sorting_columns: 1,
        columnDefs: [
            { type: "file-size", targets: 1 },
        ],
        columns: [
            {
                columnName: i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, handlerId: handlerIdJumpHistorical$1,
                render_if: function (context) { return context.is_history_enabled },
                render: function (data, type, service) {
                    let context = this;
                    const host = service.host.id;
                    const host_ts_available = service.host.is_local;
                    const jump_to_historical = {
                        handlerId: handlerIdJumpHistorical$1,
                        onClick: function () {
                            const epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
                            const epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");
                            let historical_flows_url = `${http_prefix}/lua/pro/db_search.lua?epoch_begin=${epoch_begin}&epoch_end=${epoch_end}`;

                            let params = "";
                            let params_array = [];
                            for (let key in service.tags) {
                                let value = service.tags[key];
                                let p_url = "";
                                if (key == "ifid") {
                                    p_url = ntopng_url_manager$1.serialize_param(key, value);
                                } else if (key == "host") {
                                    p_url = ntopng_url_manager$1.serialize_param("ip", `${value};eq`);
                                }
                                params_array.push(p_url);
                            }
                            params = params_array.join("&");
                            historical_flows_url = `${historical_flows_url}&${params}`;
                            window.open(historical_flows_url);
                        }
                    };
                    const jump_to_live = {
                        handlerId: handlerIdJumpLive$1,
                        onClick: function () {
                            let live_flows_url = `${http_prefix}/lua/host_details.lua?page=flows`;
                            let params = "";
                            let params_array = [];
                            for (let key in service.tags) {
                                let value = service.tags[key];
                                let p_url = "";
                                if (key == "ifid") {
                                    p_url = ntopng_url_manager$1.serialize_param(key, value);
                                } else if (key == "host") {
                                    p_url = ntopng_url_manager$1.serialize_param("host", `${value}`);
                                }
                                params_array.push(p_url);
                            }
                            params = params_array.join("&");
                            live_flows_url = `${live_flows_url}&${params}`;
                            window.open(live_flows_url);
                        }
                    };

                    const jump_to_host = {
                        handlerId: handlerIdJumpHistorical$1,
                        onClick: function () {
                            const ifid = ntopng_url_manager$1.get_url_entry('ifid');
                            const host_url = `${http_prefix}/lua/host_details.lua?host=${host}&page=historical&ts_query=ifid:${ifid},host:${host}&ts_schema=host:details&epoch_begin=${context.status.epoch_begin}&epoch_end=${context.status.epoch_end}`;

                            window.open(host_url);
                        }
                    };

                    const is_host_available = (service && service.host) ? service.host.is_available : false;
                    const dropdown = [
                        { class: 'dropdown-item', icon: 'fas fa-search-plus', href: '#', title: i18n('db_explorer.historical_data'), handler: jump_to_historical },
                        { class: is_host_available ? 'dropdown-item' : 'dropdown-item disabled', icon: 'fas fa-stream', href: '#', title: i18n('flows_page.live_flows'), handler: jump_to_live },
                    ];
                    if (context.sources_types_enabled["host"] && host_ts_available) {
                        dropdown.push({ class: 'dropdown-item', icon: 'fas fa-laptop', href: '#', title: i18n('db_explorer.host_data'), handler: jump_to_host });
                    }

                    return DataTableUtils$1.createActionButtons(dropdown);
                },
            }, {
                columnName: i18n("page_stats.top.host_name"), name: 'host_name', data: 'host', handlerId: handlerIdAddLink$3,
                render: function (data, type, service) {
                    let context = this;
                    let label = data.label;
                    let host_ref = '';
                    let handler = {
                        handlerId: handlerIdAddLink$3,
                        onClick: async function () {
                            let schema = `host:traffic`;
                            context.add_ts_group_from_source_value_dict("host", service.tags, schema);
                        },
                    };
                    if (context.sources_types_enabled["host"] && data.is_local) {
                        label = DataTableUtils$1.createLinkCallback({ text: data.label, handler });
                    }
                    if (data.is_available) {
                        host_ref = ` <a href="/lua/host_details.lua?host=${data.id}" data-bs-toggle="tooltip" title=""><i class="fas fa-laptop"></i></a>`;
                    }

                    return `${label}${host_ref}`;
                },
            }, {
                columnName: i18n("page_stats.top.received"), name: 'received', className: 'text-end', data: 'traffic', orderable: true,
                render: (data, type) => {
                    if (type === 'sort' || type === 'type') {
                        return data
                    }
                    return bytesToSizeFormatter$3(data);
                },
            }],
    };

    const interface_top_tables = [top_application$1, top_categories, top_senders, top_receivers];

    let top_application_interface = interface_top_tables.find((t) => t.view == "top_protocols");
    if (top_application_interface != null) {
        top_application_interface = ntopng_utility$1.clone(top_application_interface);
        top_application_interface.table_value = "host";
    }
    let top_category_interface = interface_top_tables.find((t) => t.view == "top_categories");
    if (top_category_interface != null) {
        top_category_interface = ntopng_utility$1.clone(top_category_interface);
        top_category_interface.table_value = "host";
    }

    const top_application = top_application_interface;
    const top_category = top_category_interface;

    const host_top_tables = [top_application, top_category];

    const bytesToSizeFormatter$2 = formatterUtils.getFormatter(formatterUtils.types.bytes.id);
    const bpsFormatter$2 = formatterUtils.getFormatter(formatterUtils.types.bps.id);
    const handlerIdAddLink$2 = "page-stats-action-link";
    const handlerIdJumpToDetails$1 = "page-stats-details";

    const top_snmp_interface$1 = {
    	table_value: "snmp",
    	table_source_def_value_dict: { ifid: true, device: true, if_index: false },
    	title: i18n('page_stats.top.top_interfaces'),
    	view: "top_snmp_ifaces",
    	default_sorting_columns: 4,
    	columnDefs: [
    		{ type: "file-size", targets: 1 },
    		{ type: "file-size", targets: 2 },
    		{ type: "file-size", targets: 3 },
    	],
    	f_get_label: (ts_group) => {
    		let source_def_array = ts_group.source_type.source_def_array;
    		let source_label;
    		for (let i = 0; i < source_def_array.length; i += 1) {
    			if (source_def_array[i].value != "device") { continue; }
    			source_label = ts_group.source_array[i].label;
    			break;
    		}
    		return `${i18n('page_stats.top.top_interfaces')} - SNMP ${i18n('page_stats.source_def.device')} ${source_label}`;
    	},
    	default: true,

    	columns: [{
    		columnName: i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, handlerId: handlerIdJumpToDetails$1,
    		render_if: function (context) { return context.is_history_enabled },
    		render: function (data, type, service) {
    			const jump_to_interface_page = {
    				handlerId: handlerIdJumpToDetails$1,
    				onClick: function () {
    					const interface_id = ntopng_url_manager.serialize_param("snmp_port_idx", `${service.tags.if_index}`);
    					const device_ip = ntopng_url_manager.serialize_param("host", `${service.tags.device}`);
    					const interface_details_page = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?${interface_id}&${device_ip}`;
    					window.open(interface_details_page);
    				}
    			};
    			return DataTableUtils$1.createActionButtons([
    				{ class: 'dropdown-item', icon: 'fas fa-laptop', href: '#', title: i18n('page_stats.interface_details'), handler: jump_to_interface_page },
    			]);
    		}
    	}, {
    		columnName: i18n("interface_name"), name: 'interface', data: 'interface', handlerId: handlerIdAddLink$2,
    		render: function (data, type, service) {
    			let context = this;
    			let handler = {
    				handlerId: handlerIdAddLink$2,
    				onClick: function () {
    					let schema = `snmp_if:traffic`;
    					context.add_ts_group_from_source_value_dict("snmp_interface", service.tags, schema);
    				},
    			};
    			let label_text = `${data.name}`;
    			return DataTableUtils$1.createLinkCallback({ text: label_text, handler });
    		},
    	}, {
    		columnName: i18n("interface_alias"), name: 'interface', data: 'interface', handlerId: handlerIdAddLink$2,
    		render: function (data) {
    			let label_text = `${data.alias}`;
    			return label_text;
    		},
    	}, {
    		columnName: i18n("page_stats.top.sent"), name: 'sent', className: 'text-end', data: 'sent', orderable: true,
    		render: (data) => {
    			return bytesToSizeFormatter$2(data);
    			//return NtopUtils.bytesToSize(data)
    		},
    	}, {
    		columnName: i18n("page_stats.top.received"), name: 'received', className: 'text-end', data: 'rcvd', orderable: true,
    		render: (data) => {
    			return bytesToSizeFormatter$2(data);
    			//return NtopUtils.bytesToSize(data)
    		},
    	}, {
    		columnName: i18n("traffic"), name: 'traffic', className: 'text-end', data: 'total', orderable: true,
    		render: (data, type) => {
                if (!data) {
                        return '';
                    }
                if (type === 'sort' || type === 'type') {
                    return data
                }
    			return bytesToSizeFormatter$2(data);
    			//return NtopUtils.bytesToSize(data)
    		},
    	}, {
    		columnName: i18n("last_downlink_usage"), name: 'traffic_perc', className: 'text-center no-wrap', data: 'downlink_usage_percentage',
    		render: (data) => {
    			let percentage = data.toFixed(1);
    			if (percentage > 100) percentage = 100;
    			return NtopUtils$1.createProgressBar(percentage)
    		}
    	}, {
    		columnName: i18n("last_uplink_usage"), name: 'traffic_perc', className: 'text-center no-wrap', data: 'uplink_usage_percentage',
    		render: (data) => {
    			let percentage = data.toFixed(1);
    			if (percentage > 100) percentage = 100;
    			return NtopUtils$1.createProgressBar(percentage)
    		}
    	}, {
    		columnName: i18n("page_stats.top.throughput"), name: 'throughput', className: 'text-end', data: 'throughput', orderable: true,
    		render: (data, type) => {
                if (!data) {
                        return '';
                    }
                if (type === 'sort' || type === 'type') {
                    return data
                }
    			return bpsFormatter$2(data);
    			//return NtopUtils.bytesToSize(data)
    		},
    	},],
    };

    const snmp_interface_top_tables$1 = [top_snmp_interface$1];

    const bytesToSizeFormatter$1 = formatterUtils.getFormatter(formatterUtils.types.bytes.id);
    const bpsFormatter$1 = formatterUtils.getFormatter(formatterUtils.types.bps.id);
    const handlerIdAddLink$1 = "page-stats-action-link";
    const handlerIdJumpToDetails = "page-stats-details";

    const top_snmp_interface = {
    	table_value: "snmp",
    	table_source_def_value_dict: { ifid: true, device: true, if_index: false },
    	title: i18n('page_stats.top.top_interfaces'),
    	view: "top_snmp_ifaces",
    	default_sorting_columns: 4,
    	columnDefs: [
    		{ type: "file-size", targets: 1 },
    		{ type: "file-size", targets: 2 },
    		{ type: "file-size", targets: 3 },
    	],
    	f_get_label: (ts_group) => {
    		let source_def_array = ts_group.source_type.source_def_array;
    		let source_label;
    		for (let i = 0; i < source_def_array.length; i += 1) {
    			if (source_def_array[i].value != "device") { continue; }
    			source_label = ts_group.source_array[i].label;
    			break;
    		}
    		return `${i18n('page_stats.top.top_interfaces')} - SNMP ${i18n('page_stats.source_def.device')} ${source_label}`;
    	},
    	default: true,

    	columns: [{
    		columnName: i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, handlerId: handlerIdJumpToDetails,
    		render_if: function (context) { return context.is_history_enabled },
    		render: function (data, type, service) {
    			const jump_to_interface_page = {
    				handlerId: handlerIdJumpToDetails,
    				onClick: function () {
    					const interface_id = ntopng_url_manager.serialize_param("snmp_port_idx", `${service.tags.if_index}`);
    					const device_ip = ntopng_url_manager.serialize_param("host", `${service.tags.device}`);
    					const interface_details_page = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?${interface_id}&${device_ip}`;
    					window.open(interface_details_page);
    				}
    			};
    			return DataTableUtils$1.createActionButtons([
    				{ class: 'dropdown-item', icon: 'fas fa-laptop', href: '#', title: i18n('page_stats.interface_details'), handler: jump_to_interface_page },
    			]);
    		}
    	}, {
    		columnName: i18n("interface"), name: 'interface', data: 'interface', handlerId: handlerIdAddLink$1,
    		render: function (data, type, service) {
    			let context = this;
    			let handler = {
    				handlerId: handlerIdAddLink$1,
    				onClick: function () {
    					let schema = `snmp_if:traffic`;
    					context.add_ts_group_from_source_value_dict("snmp_interface", service.tags, schema);
    				},
    			};
    			let label_text = `${data.label}`;
    			return DataTableUtils$1.createLinkCallback({ text: label_text, handler });
    		},
    	}, {
    		columnName: i18n("page_stats.top.sent"), name: 'sent', className: 'text-end', data: 'sent', orderable: true,
    		render: (data) => {
    			return bytesToSizeFormatter$1(data);
    			//return NtopUtils.bytesToSize(data)
    		},
    	}, {
    		columnName: i18n("page_stats.top.received"), name: 'received', className: 'text-end', data: 'rcvd', orderable: true,
    		render: (data) => {
    			return bytesToSizeFormatter$1(data);
    			//return NtopUtils.bytesToSize(data)
    		},
    	}, {
    		columnName: i18n("traffic"), name: 'traffic', className: 'text-end', data: 'total', orderable: true,
    		render: (data, type) => {
                if (!data) {
                        return '';
                    }
                if (type === 'sort' || type === 'type') {
                    return data
                }
    			return bytesToSizeFormatter$1(data);
    			//return NtopUtils.bytesToSize(data)
    		},
    	}, {
    		columnName: i18n("last_downlink_usage"), name: 'traffic_perc', className: 'text-center no-wrap', data: 'downlink_usage_percentage',
    		render: (data) => {
    			let percentage = data.toFixed(1);
    			if (percentage > 100) percentage = 100;
    			return NtopUtils$1.createProgressBar(percentage)
    		}
    	}, {
    		columnName: i18n("last_uplink_usage"), name: 'traffic_perc', className: 'text-center no-wrap', data: 'uplink_usage_percentage',
    		render: (data) => {
    			let percentage = data.toFixed(1);
    			if (percentage > 100) percentage = 100;
    			return NtopUtils$1.createProgressBar(percentage)
    		}
    	}, {
    		columnName: i18n("page_stats.top.throughput"), name: 'throughput', className: 'text-end', data: 'throughput', orderable: true,
    		render: (data, type) => {
                if (!data) {
                        return '';
                    }
                if (type === 'sort' || type === 'type') {
                    return data
                }
    			return bpsFormatter$1(data);
    			//return NtopUtils.bytesToSize(data)
    		},
    	},],
    };

    const snmp_interface_top_tables = [top_snmp_interface];

    const bytesToSizeFormatter = formatterUtils.getFormatter(formatterUtils.types.bytes.id);
    const bpsFormatter = formatterUtils.getFormatter(formatterUtils.types.bps.id);
    const handlerIdAddLink = "page-stats-action-link";
    const handlerIdJumpHistorical = "page-stats-action-jump-historical";
    const handlerIdJumpLive = "page-stats-action-jump-live";
    const handlerIdJumpDetails = "page-stats-action-jump-details";

    const top_flow_interface = {
    	table_value: "flowdevice",
    	table_source_def_value_dict: { ifid: true, device: true, if_index: false },
    	title: i18n('page_stats.top.top_interfaces'),
    	view: "top_flowdev_ifaces",
    	default_sorting_columns: 4,
    	columnDefs: [
    		{ type: "file-size", targets: 1 },
    		{ type: "file-size", targets: 2 },
    		{ type: "file-size", targets: 3 },
    	],
    	f_get_label: (ts_group) => {
    		let source_def_array = ts_group.source_type.source_def_array;
    		let source_label;
    		for (let i = 0; i < source_def_array.length; i += 1) {
    			if (source_def_array[i].value != "device") { continue; }
    			source_label = ts_group.source_array[i].label;
    			break;
    		}
    		return `${i18n('page_stats.top.top_interfaces')} - Flow Exporter ${source_label}`;
    	},
    	default: true,

    	columns: [{
    		columnName: i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, handlerId: handlerIdJumpHistorical,
    		render_if: function (context) { return context.is_history_enabled },
    		render: function (data, type, service) {
    			let context = this;
    			const jump_to_historical = {
    				handlerId: handlerIdJumpHistorical,
    				onClick: function () {
    					let historical_flows_url = `${http_prefix}/lua/pro/db_search.lua?epoch_begin=${context.status.epoch_begin}&epoch_end=${context.status.epoch_end}`;

    					let params = "";
    					let params_array = [];
    					for (let key in service.tags) {
    						let value = service.tags[key];
    						let p_url = "";
    						if (key == "ifid") {
    							p_url = ntopng_url_manager.serialize_param(key, value);
    						} else if (key == "device") {
    							p_url = ntopng_url_manager.serialize_param("probe_ip", `${value};eq`);
    						}
    						params_array.push(p_url);
    					}
    					params = params_array.join("&");
    					historical_flows_url = `${historical_flows_url}&${params}`;
    					window.open(historical_flows_url);
    				}
    			};
    			const jump_to_live = {
    				handlerId: handlerIdJumpLive,
    				onClick: function () {
    					let live_flows_url = `${http_prefix}/lua/flows_stats.lua?page=flows`;
    					let params = "";
    					let params_array = [];
    					for (let key in service.tags) {
    						let value = service.tags[key];
    						let p_url = "";
    						if (key == "ifid") {
    							p_url = ntopng_url_manager.serialize_param(key, value);
    						} else if (key == "device") {
    							p_url = ntopng_url_manager.serialize_param("deviceIP", `${value}`);
    						}
    						params_array.push(p_url);
    					}
    					params = params_array.join("&");
    					live_flows_url = `${live_flows_url}&${params}`;
    					window.open(live_flows_url);
    				}
    			};

    			const jump_to_host = {
    				handlerId: handlerIdJumpDetails,
    				onClick: function () {
    					const ifid = ntopng_url_manager.get_url_entry('ifid');
    					const port = service.tags.port;
    					const host = service.tags.device;
    					const interface_url = `${http_prefix}/lua/pro/enterprise/flowdevice_interface_details.lua?snmp_port_idx=${port}&ip=${host}&page=historical&ts_query=ifid:${ifid},device:${host},port:${port}&ts_schema=host:details&epoch_begin=${context.status.epoch_begin}&epoch_end=${context.status.epoch_end}`;

    					window.open(interface_url);
    				}
    			};

    			const dropdown = [
    				{ class: 'dropdown-item', icon: 'fas fa-search-plus', href: '#', title: i18n('db_explorer.historical_data'), handler: jump_to_historical },				
    				{ class: 'dropdown-item', icon: 'fas fa-stream', href: '#', title: i18n('flows_page.live_flows'), handler: jump_to_live },
    				{ class: 'dropdown-item', icon: 'fas fa-file-export', href: '#', title: i18n('db_explorer.interface_data'), handler: jump_to_host }
    			];
    			
    			return DataTableUtils$1.createActionButtons(dropdown);
    		},
    	}, {
    		columnName: i18n("interface_name"), name: 'interface', data: 'interface', handlerId: handlerIdAddLink,
    		render: function (data, type, service) {
    			let context = this;
    			let handler = {
    				handlerId: handlerIdAddLink,
    				onClick: function () {
    					let schema = `flowdev_port:traffic`;
    					context.add_ts_group_from_source_value_dict("flow_interface", service.tags, schema);
    				},
    			};
    			let label_text = `${data.label}`;
    			return DataTableUtils$1.createLinkCallback({ text: label_text, handler });
    		},
    	},  {
    		columnName: i18n("page_stats.top.sent"), name: 'sent', className: 'text-end', data: 'sent', orderable: true,
    		render: (data) => {
    			return bytesToSizeFormatter(data);
    		},
    	}, {
    		columnName: i18n("page_stats.top.received"), name: 'received', className: 'text-end', data: 'rcvd', orderable: true,
    		render: (data) => {
    			return bytesToSizeFormatter(data);
    		},
    	}, {
    		columnName: i18n("traffic"), name: 'traffic', className: 'text-end', data: 'total', orderable: true,
    		render: (data, type) => {
                if (!data) {
                        return '';
                    }
                if (type === 'sort' || type === 'type') {
                    return data
                }
    			return bytesToSizeFormatter(data);
    		},
    	}, {
    		columnName: i18n("percentage"), name: 'traffic_perc', className: 'text-center', data: 'percentage', orderable: false,
    		render: (data) => {
    			const percentage = data.toFixed(1);
    			return NtopUtils$1.createProgressBar(percentage)
    		}
    	}, {
    		columnName: i18n("page_stats.top.throughput"), name: 'throughput', className: 'text-end', data: 'throughput', orderable: true,
    		render: (data,type) => {
                if (!data) {
                        return '';
                    }
                if (type === 'sort' || type === 'type') {
                    return data
                }
    			return bpsFormatter(data);
    		},
    	},],
    };

    const flow_dev_top_tables = [top_flow_interface];

    const available_interfaces = async (host) => {
        const params = {
            host: host
        };
        const url_params = ntopng_url_manager$1.obj_to_url_params(params);
        const snmp_device_port_url = `${http_prefix}/lua/pro/rest/v2/get/snmp/device/available_interfaces.lua?${url_params}`;
        const interfaces_list = await ntopng_utility$1.http_request(snmp_device_port_url);
        return interfaces_list;
    };

    const snmp_device_ports = async (host) => {
        let interfaces = await available_interfaces(host);
        let result_interfaces = interfaces.map((iface) => {
            if(iface.name != null && iface.name != "" && iface.name != iface.id) {
                return { label: `${iface.name} (${iface.id})`, id: iface.id, name: iface.name };
            }
            return { label: iface.id, id: iface.id,  name: iface.id };
        });

        return result_interfaces.sort(NtopUtils$1.sortAlphabetically)
    };

    const proxy_snmp = function () {
        return {
            available_interfaces,
            snmp_device_ports,
        };
    }();

    const ui_types$1 = {
    	hide: "hide",
    	select: "select",
    	input: "input",
    	input: "input_confirm",
    };

    const sources_url_el_to_source$1 = {
    	ifid: (s) => {
    		let label = s.ifname;
    		if (s.name != null) {
    			label = s.name;
    		}
    		return {
    			label,
    			value: s.ifid,
    		};
    	},
    	pool: (p) => {
    		let label = p.pool_id;
    		if (p.name != null) { label = p.name; }
    		return {
    			label,
    			value: p.pool_id,
    		};
    	},
    	am_host: (am) => {
    		let label = `${am.label} ${am.measurement}`;
    		let value = `${am.host},metric:${am.measurement_key}`;
    		return {
    			label,
    			value,
    		};
    	},
    	blacklist: (b) => {
    		let label = `${b.column_name}`;
    		let value = label;
    		return {
    			label,
    			value,
    		};
    	},
    	device: (device) => {
    		let label = `${device.name}`;
    		let value = `${device.ip}`;
    		return {
    			label,
    			value,
    		};
    	},
    	if_index: (index) => {
    		return '';
    		/*	let label = `${index.name}`;
    			let value = `${index.ip}`;
    			return {
    			label,
    			value,
    			};*/
    	}
    };

    const sources_types_tables$1 = {
    	interface: interface_top_tables,
    	host: host_top_tables,
    	snmp_device: snmp_interface_top_tables$1,
    	snmp_interface: snmp_interface_top_tables,
    	flow_device: flow_dev_top_tables,
    	flow_interface: flow_dev_top_tables,
    };

    const sources_types$1 = [
    	{
    		id: "interface", //unique id
    		regex_page_url: "lua\/if_stats", // regex to match url page
    		label: i18n("page_stats.source_def.interface"),
    		query: "iface",
    		f_map_ts_options: null, // convert rest result
    		source_def_array: [{
    			main_source_def: true,
    			label: i18n("page_stats.source_def.interface"),
    			regex_type: null,
    			refresh_on_sources_change: false, // if true sources list are realoaded every time some selected sources changed 
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua", // url to get sources list
    			sources_function: null, // custom function that return sources_list, overwrite sources_url
    			value: "ifid", // used in tsQuery parameter, to get init and set value in url
    			value_url: null, // overwrite value to get and set value in url
    			value_map_sources_res: null,
    			disable_tskey: null,
    			f_get_value_url: null, // overwrite value and value_url to get start value from url
    			f_set_value_url: null, // overwrite value and value_url to set start value in url
    			ui_type: ui_types$1.select,
    		}],
    	},
    	{
    		id: "blacklist", //unique id
    		regex_page_url: "lua\/admin\/blacklists", // regex to match url page
    		label: i18n("page_stats.source_def.blacklist"),
    		query: "blacklist",
    		f_map_ts_options: (ts_options, ts_group) => {
    			if (ts_options.series == null) {
    				return ts_options;
    			}
    			ts_options.series.forEach((s) => {
    				if (ts_group.metric.query != null) {
    					s.name = ts_group.metric.label;
    				} else {
    					s.name = s.name.replaceAll("_", " ");
    					s.type = undefined;
    				}
    			});
    			return ts_options;
    		},
    		// display_full_name: true,
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "System", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		},]
    	},
    	{
    		id: "vulnerability_scan", //unique id
    		regex_page_url: "lua\/vulnerability_scan", // regex to match url page
    		label: i18n("page_stats.source_def.vuln_scan"),
    		query: "am_vuln_scan",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "System", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		}]

    	},
    	{
    		id: "host",
    		regex_page_url: "lua\/host_details",
    		label: i18n("page_stats.source_def.host"),
    		table_value: "host",
    		query: "host",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.host"),
    			regex_type: "ip",
    			value: "host",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "mac",
    		regex_page_url: "lua\/mac_details",
    		label: i18n("page_stats.source_def.mac"),
    		query: "mac",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.mac"),
    			regex_type: "macAddress",
    			value: "mac",
    			value_url: "host",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "network",
    		regex_page_url: "lua\/network_details",
    		label: i18n("page_stats.source_def.network"),
    		query: "subnet",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.network"),
    			regex_type: "text",
    			value: "subnet",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "as",
    		regex_page_url: "lua\/as_overview",
    		label: i18n("page_stats.source_def.as"),
    		query: "asn",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.as"),
    			regex_type: "text",
    			value: "asn",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "all_asn",
    		regex_page_url: "lua\/as_stats",
    		label: i18n("page_stats.source_def.as"),
    		query: "all_asn",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}],
    	},
    	{
    		id: "country",
    		regex_page_url: "lua\/country_details",
    		label: i18n("page_stats.source_def.country"),
    		query: "country",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.country"),
    			regex_type: "text",
    			value: "country",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "os",
    		regex_page_url: "lua\/os_details",
    		label: i18n("page_stats.source_def.os"),
    		query: "os",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.os"),
    			regex_type: "text",
    			value: "os",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "vlan",
    		regex_page_url: "lua\/vlan_details",
    		label: i18n("page_stats.source_def.vlan"),
    		query: "vlan",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.vlan"),
    			regex_type: "text",
    			value: "vlan",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "pool",
    		regex_page_url: "lua\/pool_details",
    		label: i18n("page_stats.source_def.pool"),
    		query: "host_pool",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.pool"),
    			sources_url: "lua/rest/v2/get/host/pools.lua",
    			value: "pool",
    			ui_type: ui_types$1.select,
    		}],
    	},
    	{
    		id: "system",
    		regex_page_url: "lua\/system_stats",
    		label: i18n("page_stats.source_def.system"),
    		query: "system",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "System", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		}],
    	},
    	{
    		id: "profile",
    		regex_page_url: "lua\/pro\/profile_details",
    		label: i18n("page_stats.source_def.profile"),
    		query: "profile",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.profile"),
    			regex_type: "text",
    			value: "profile",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "redis",
    		regex_page_url: "lua\/monitor\/redis_monitor.lua",
    		label: i18n("page_stats.source_def.redis"),
    		query: "redis",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "Redis", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		}],
    	},
    	{
    		id: "influx",
    		regex_page_url: "lua\/monitor\/influxdb_monitor.lua",
    		label: i18n("page_stats.source_def.influx"),
    		query: "influxdb",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "Influx", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		}],
    	},
    	{
    		id: "active_monitoring",
    		regex_page_url: "lua\/active_monitoring.lua",
    		label: i18n("page_stats.source_def.active_monitoring"),
    		query: "am",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.active_monitoring"),
    			sources_url: "lua/rest/v2/get/active_monitoring/timeseries_list.lua",
    			value: "host",
    			disable_tskey: true,
    			value_map_sources_res: "am_host",
    			ui_type: ui_types$1.select,
    		}],
    	},
    	{
    		id: "snmp_interface",
    		id_group: "snmp",
    		// disable_stats: true,
    		regex_page_url: "lua\/pro\/enterprise\/snmp_interface_details",
    		label: i18n("page_stats.source_def.snmp_interface"),
    		query: "snmp_interface",
    		f_map_ts_options: (ts_options, ts_group) => {
    			return ts_options;
    		},
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		}, {
    			label: i18n("page_stats.source_def.device"),
    			regex_type: "ip",
    			sources_url: "lua/pro/rest/v2/get/snmp/device/list.lua", // url to get sources list
    			value: "device",
    			value_url: "host",
    			// refresh_i18n: 'modal_timeseries.snmp_confirm_device',
    			// ui_type: ui_types.input_confirm,
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.snmp_interface"),
    			regex_type: "text",
    			refresh_on_sources_change: true,
    			sources_function: async (selected_source_value_array) => {
    				const device_host = selected_source_value_array[1];
    				let snmp_interfaces = await proxy_snmp.available_interfaces(device_host);
    				return snmp_interfaces.map((iface) => { return { label: iface.name, value: iface.id }; });
    			},
    			value: "if_index",
    			value_url: "snmp_port_idx",
    			ui_type: ui_types$1.select,
    		}],
    	},
    	{
    		id: "snmp_device",
    		id_group: "snmp",
    		// disable_stats: true,
    		regex_page_url: "lua\/pro\/enterprise\/snmp_device_details",
    		label: i18n("page_stats.source_def.snmp_device"),
    		query: "snmp_device",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.device"),
    			regex_type: "ip",
    			sources_url: "lua/pro/rest/v2/get/snmp/device/list.lua", // url to get sources list
    			value: "device",
    			value_url: "host",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "snmp_qos",
    		id_group: "snmp",
    		regex_page_url: "lua\/pro\/enterprise\/snmp_device_details",
    		label: i18n("page_stats.source_def.snmp_interface"),
    		query: "snmp_interface",
    		f_map_ts_options: (ts_options, ts_group) => {
    			return ts_options;
    		},
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_function: () => { return [{ label: "", value: -1 }] },
    			value: "ifid",
    			ui_type: ui_types$1.hide,
    		}, {
    			label: i18n("page_stats.source_def.device"),
    			regex_type: "ip",
    			sources_url: "lua/pro/rest/v2/get/snmp/device/list.lua", // url to get sources list
    			value: "device",
    			value_url: "host",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.snmp_interface"),
    			regex_type: "text",
    			refresh_on_sources_change: true,
    			sources_function: async (selected_source_value_array) => {
    				const device_host = selected_source_value_array[1];
    				let snmp_interfaces = await proxy_snmp.available_interfaces(device_host);
    				return snmp_interfaces.map((iface) => { return { label: iface.name, value: iface.id }; });
    			},
    			value: "if_index",
    			value_url: "snmp_port_idx",
    			ui_type: ui_types$1.select,
    		}, {
    			label: i18n("snmp.qos"),
    			regex_type: "text",
    			sources_url: "", 
    			value: "qos_class_id",
    			value_url: "qos_class_id",
    			ui_type: ui_types$1.hide,
    		}],
    	},
    	{
    		id: "flow_device",
    		regex_page_url: "lua\/pro\/enterprise\/exporters",
    		label: i18n("page_stats.source_def.flow_device"),
    		query: "flowdev",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.device"),
    			sources_url: "lua/pro/rest/v2/get/flowdevices/list.lua", // url to get sources list
    			regex_type: "ip",
    			value: "device",
    			value_url: "ip",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "flow_interface",
    		regex_page_url: "lua\/pro\/enterprise\/flowdevice_interface_details",
    		label: i18n("page_stats.source_def.flow_interface"),
    		query: "flowdev_port",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			label: i18n("page_stats.source_def.device"),
    			regex_type: "ip",
    			value: "device",
    			value_url: "ip",
    			ui_type: ui_types$1.input,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.port"),
    			regex_type: "port",
    			value: "port",
    			value_url: "snmp_port_idx",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "sflow_device",
    		regex_page_url: "lua\/pro\/enterprise\/sflowdevices_stats",
    		label: i18n("page_stats.source_def.sflow_device"),
    		query: "sflowdev",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.device"),
    			regex_type: "ip",
    			value: "device",
    			value_url: "ip",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "sflow_interface",
    		regex_page_url: "lua\/pro\/enterprise\/sflowdevice_interface_details",
    		label: i18n("page_stats.source_def.sflow_interface"),
    		query: "sflowdev_port",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			label: i18n("page_stats.source_def.device"),
    			regex_type: "ip",
    			value: "device",
    			value_url: "ip",
    			ui_type: ui_types$1.input,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.port"),
    			regex_type: "port",
    			value: "port",
    			value_url: "snmp_port_idx",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "observation_point",
    		regex_page_url: "lua\/pro\/enterprise\/observation_points",
    		label: i18n("page_stats.source_def.observation_point"),
    		query: "obs_point",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.observation_point"),
    			regex_type: "text",
    			value: "obs_point",
    			value_url: "observation_point",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		//todo_test
    		id: "pod",
    		regex_page_url: "lua\/pod_details",
    		label: i18n("page_stats.source_def.pod"),
    		query: "pod",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.pod"),
    			regex_type: "text",
    			value: "pod",
    			ui_type: ui_types$1.input,
    		}],
    	}, {
    		//todo_test
    		id: "container",
    		regex_page_url: "lua\/container_details",
    		label: i18n("page_stats.source_def.container"),
    		query: "container",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			sources_url: "lua/rest/v2/get/ntopng/interfaces.lua",
    			value: "ifid",
    			ui_type: ui_types$1.select,
    		}, {
    			main_source_def: true,
    			label: i18n("page_stats.source_def.container"),
    			regex_type: "text",
    			value: "container",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	{
    		id: "nedge",
    		regex_page_url: "lua\/pro\/nedge\/if_stats.lua",
    		label: i18n("page_stats.source_def.nedge"),
    		query: "nedge",
    		source_def_array: [{
    			label: i18n("page_stats.source_def.interface"),
    			regex_type: "text",
    			value: "ifid",
    			value_url: "ifname",
    			ui_type: ui_types$1.input,
    		}],
    	},
    	// {
    	// 	id: "n_edge_interface",
    	// 	regex_page_url: "lua\/pro\/nedge\/if_stats.lua",
    	// 	label: "Profile nEdge",
    	// 	value: "ifid",
    	// 	regex_type: "text",
    	// 	ui_type: ui_types.select_and_input,
    	// 	query: "iface:nedge",
    	// },
    ];

    const metricsConsts = function () {
    	return {
    		ui_types: ui_types$1,
    		sources_url_el_to_source: sources_url_el_to_source$1,
    		sources_types: sources_types$1,
    		sources_types_tables: sources_types_tables$1,
    	};
    }();

    /**
        (C) 2022 - ntop.org
    */

    const set_timeseries_groups_in_url = (timeseries_groups) => {
        let params_timeseries_groups = [];
        timeseries_groups.forEach((ts_group) => {
            let param = get_ts_group_url_param(ts_group);
            params_timeseries_groups.push(param);
        });
        let url_timeseries_groups = params_timeseries_groups.join(";;");
        ntopng_url_manager$1.set_key_to_url("timeseries_groups", url_timeseries_groups);
    };

    function get_ts_group_url_param(ts_group) {
        let timeseries = [];
        ts_group.timeseries.forEach((ts) => {
            timeseries.push(`${ts.id}=${ts.raw}:${ts.past}:${ts.avg}:${ts.perc_95}`);
        });
        let metric_schema_query = ts_group.metric.schema;
        if (ts_group.metric.query != null) {
            metric_schema_query = `${metric_schema_query}+${ts_group.metric.query}`;
        }
        let timeseries_param = timeseries.join("|");
        let source_value_array_query = ts_group.source_array.map((source) => source.value).join("+");
        let param = `${ts_group.source_type.id};${source_value_array_query};${metric_schema_query};${timeseries_param}`;
        return param;
    }

    const get_timeseries_groups_from_url = async (http_prefix, url_timeseries_groups) => {
        if (url_timeseries_groups == null) {
            url_timeseries_groups = ntopng_url_manager$1.get_url_entry("timeseries_groups");
        }
        if (url_timeseries_groups == null || url_timeseries_groups == "") {
            return null;
        }
        let groups = url_timeseries_groups.split(";;");
        groups.map(async (g) => {
            let ts_group = await get_url_param_from_ts_group(g);
            return ts_group;
        });
        let timeseries_groups = Promise.all(groups.map(async (g) => {
            let ts_group = await get_url_param_from_ts_group(g);
            return ts_group;
        }));
        return timeseries_groups;
    };

    const get_ts_group = (source_type, source_array, metric, customized_ts) => {
        let ts_config = {
            raw: true,
            past: true, /* By default enable the past serie */
            avg: false,
            perc_95: false,
        };

        if (customized_ts && customized_ts.raw != null) ts_config.raw = customized_ts.raw;
        if (customized_ts && customized_ts.past != null) ts_config.past = customized_ts.past;
        if (customized_ts && customized_ts.avg != null) ts_config.avg = customized_ts.avg;
        if (customized_ts && customized_ts.perc_95 != null) ts_config.perc_95 = customized_ts.perc_95;

        let id = get_ts_group_id(source_type, source_array, metric);
        let timeseries = [];
        for (let key in metric?.timeseries) {
            let ts = metric.timeseries[key];
            ts_config.id = key;
            ts_config.label = ts.label;
            timeseries.push(ts_config);
        }
        return {
            id, source_type, source_array, metric, timeseries,
        };
    };

    const get_default_timeseries_groups = async (http_prefix, metric_ts_schema, metric_query) => {
        let source_type = get_current_page_source_type();
        let source_array = await get_default_source_array(http_prefix, source_type);
        let metrics = await get_metrics(http_prefix, source_type, source_array);
        let metric = get_default_metric(metrics, metric_ts_schema, metric_query);
        let ts_group = get_ts_group(source_type, source_array, metric);
        return [ts_group];
    };

    async function get_url_param_from_ts_group(ts_group_url_param) {
        let g = ts_group_url_param;
        let info = g.split(";");
        let source_type_id = info[0];
        let source_value_query = info[1];
        let source_value_array = source_value_query.split("+");

        let metric_schema_query = info[2];
        let metric_schema_query_array = metric_schema_query.split("+");
        if (metric_schema_query_array.length < 2) {
            metric_schema_query_array.push(null);
        }

        let timeseries_url = info[3];

        let source_type = get_source_type_from_id(source_type_id);
        let source_array = await get_source_array_from_value_array(http_prefix, source_type, source_value_array);
        let metric = await get_metric_from_schema(http_prefix, source_type, source_array, metric_schema_query_array[0], metric_schema_query_array[1]);
        let timeseries = get_timeseries(timeseries_url, metric);
        return {
            id: get_ts_group_id(source_type, source_array, metric),
            source_type,
            source_array,
            metric,
            timeseries,
        };
    }

    const get_ts_group_id = (source_type, source_array, metric, enable_source_def_value_dict, set_source_type_id_group) => {
        let metric_id = "";
        if (metric != null) {
            metric_id = metric.schema;
            if (metric.query != null) {
                metric_id = `${metric_id} - ${metric.query}`;
            }
        }
        let source_def_array = source_type.source_def_array;
        let source_value_array = source_array.map((source, i) => {
            let source_def_value = source_def_array[i].value;
            if (enable_source_def_value_dict != null && !enable_source_def_value_dict[source_def_value]) { return null; }
            return source.value;
        }).filter((s) => s != null).join("_");
        let source_type_id = source_type.id;
        if (set_source_type_id_group && source_type.id_group != null) {
            source_type_id = source_type.id_group;
        }
        return `${source_type_id} - ${source_value_array} - ${metric_id}`;
    };

    function get_timeseries(timeseries_url, metric) {
        let ts_url_array = timeseries_url.split("|");
        let r = /(.+)=(.+):(.+):(.+):(.+)/;
        let timeseries = [];
        ts_url_array.forEach((ts_url) => {
            let values = r.exec(ts_url);
            if (values.length > 0) {
                let id = values[1];
                let label = metric.timeseries[id].label;
                let raw = JSON.parse(values[2]);
                let past = JSON.parse(values[3]);
                let avg = JSON.parse(values[4]);
                let perc_95 = JSON.parse(values[5]);
                timeseries.push({
                    id, label, raw, past, avg, perc_95,
                });
            }
        });
        return timeseries;
    }

    const ui_types = metricsConsts.ui_types;

    // dictionary of functions to convert an element of source_url rest result to a source ({label, value })
    const sources_url_el_to_source = metricsConsts.sources_url_el_to_source;

    const sources_types = metricsConsts.sources_types;
    const sources_types_tables = metricsConsts.sources_types_tables;

    const get_source_type_from_id = (source_type_id) => {
        return sources_types.find((st) => st.id == source_type_id);
    };

    const get_default_source_array = async (http_prefix, source_type) => {
        let source_value_array = get_default_source_value_array(source_type);
        let source_array = await get_source_array_from_value_array(http_prefix, source_type, source_value_array);
        return source_array;
    };

    async function get_source_array_from_value_dict(http_prefix, source_type, source_value_dict) {
        let source_value_array = source_type.source_def_array.map((source_def) => source_value_dict[source_def.value]);
        let source_array = await get_source_array_from_value_array(http_prefix, source_type, source_value_array);
        return source_array;
    }

    const get_source_array_from_value_array = async (http_prefix, source_type, source_value_array) => {
        if (source_type == null) {
            source_type = get_current_page_source_type();
        }
        let source_array = [];
        let source;
        for (let i = 0; i < source_value_array.length; i++) {
            let source_value = source_value_array[i];
            let source_def = source_type.source_def_array[i];
            if (source_def.sources_url || source_def.sources_function) {
                let sources = [];
                sources = await get_sources(http_prefix, source_type.id, source_def, source_value_array);
                source = sources.find((s) => s.value == source_value);
                if (source == null) {
                    source = sources[0];
                }
            } else {
                source = { label: source_value, value: source_value };
            }
            source_array.push(source);
        }
        return source_array;
    };

    let cache_sources = {};

    function get_source_def_key(id, source_def, selected_source_value_array) {
        let key = `${id}_${source_def.value}`;
        if (!source_def.refresh_on_sources_change) {
            return key;
        }
        const value_array = selected_source_value_array.join("_");
        key = `${key}_${value_array}`;
        return key;
    }

    const get_sources = async (http_prefix, id, source_def, selected_source_value_array) => {
        let key = get_source_def_key(id, source_def, selected_source_value_array);
        if (cache_sources[key] == null) {
            if (source_def.sources_url) {
                let url = `${http_prefix}/${source_def.sources_url}`;
                cache_sources[key] = ntopng_utility$1.http_request(url);
            } else if (source_def.sources_function) {
                cache_sources[key] = source_def.sources_function(selected_source_value_array);
            } else {
                return [];
            }
        }
        let sources = await cache_sources[key];
        if (source_def.sources_url) {
            let f_map_source_element = sources_url_el_to_source[source_def.value_map_sources_res];
            if (f_map_source_element == null) {
                f_map_source_element = sources_url_el_to_source[source_def.value];
            }
            if (f_map_source_element == null) {
                throw `:Error: metrics-manager.js, missing sources_url_to_source ${source_def.value} key`;
            }
            sources = sources.map((s) => f_map_source_element(s));
        }
        return sources.sort(NtopUtils$1.sortAlphabetically);
    };

    function set_source_value_object_in_url(source_type, source_value_object) {
        source_type.source_def_array.forEach((source_def) => {
            let source_value = source_value_object[source_def.value];
            if (source_value == null) { return; }
            if (source_def.f_set_value_url != null) {
                source_def.f_set_value_url();
            } else if (source_def.value_url != null) {
                ntopng_url_manager$1.set_key_to_url(source_def.value_url, source_value);
            } else {
                ntopng_url_manager$1.set_key_to_url(source_def.value, source_value);
            }
        });
    }

    const get_default_source_value_array = (source_type) => {
        if (source_type == null) {
            source_type = get_current_page_source_type();
        }
        let source_value_array = source_type.source_def_array.map((source_def) => {
            if (source_def.f_get_value_url != null) {
                return source_def.f_get_value_url();
            }
            let source_def_value = source_def.value_url;
            if (source_def_value == null) {
                source_def_value = source_def.value;
            }
            let source_value = ntopng_url_manager$1.get_url_entry(source_def_value);
            return source_value;
        });
        return source_value_array;
    };

    function get_metrics_url(http_prefix, source_type, source_array, epoch, include_empty_ts) {
        let params = source_type.source_def_array.map((source_def, i) => {
            if (source_array[i]) {
                return `${source_def.value}=${source_array[i].value}`;
            }
        }).join("&");
        let epoch_string = ``;
        let include_empty_ts_string = "";
        if (include_empty_ts) {
            include_empty_ts_string = "include_empty_ts=true";
        }
        if (epoch != null) {
            epoch_string = `epoch_end=${epoch.epoch_end}&epoch_begin=${epoch.epoch_begin}`;
        }
        let url = `${http_prefix}/lua/rest/v2/get/timeseries/type/consts.lua?query=${source_type.query}&${params}&${epoch_string}&${include_empty_ts_string}`;
        return url;
    }

    function get_metric_key(source_type, source_array) {
        let source_array_key = source_array.map((source) => source.value).join("_");
        let key = `${source_type.id}_${source_array_key}`;
        return key;
    }

    let cache_metrics = {};
    let last_metrics_time_interval = null;
    const get_metrics = async (http_prefix, source_type, source_array, status, include_empty_ts) => {
        let epoch_begin = status?.epoch_begin || ntopng_url_manager$1.get_url_entry("epoch_begin");
        let epoch_end = status?.epoch_end || ntopng_url_manager$1.get_url_entry("epoch_end");
        let current_last_metrics_time_interval = `${epoch_begin}_${epoch_end}`;
        if (source_type == null) {
            source_type = get_current_page_source_type();
        }
        if (source_array == null) {
            source_array = await get_default_source_array(http_prefix, source_type);
        }
        // let url = `${http_prefix}/lua/rest/v2/get/timeseries/type/consts.lua?query=${source_type.value}`;
        let url = get_metrics_url(http_prefix, source_type, source_array, { epoch_begin: epoch_begin, epoch_end: epoch_end }, include_empty_ts);
        let key = get_metric_key(source_type, source_array);
        if (current_last_metrics_time_interval != last_metrics_time_interval) {
            cache_metrics[key] = null;
            last_metrics_time_interval = current_last_metrics_time_interval;
        }
        if (cache_metrics[key] == null) {
            cache_metrics[key] = ntopng_utility$1.http_request(url);
        }
        let metrics = await cache_metrics[key];
        if (metrics == null || metrics.length == 0) { return [{}]; }
        if (metrics.some((m) => m.default_visible == true) == false) {
            metrics[0].default_visible = true;
        }
        return ntopng_utility$1.clone(metrics);
    };

    const get_current_page_source_type = () => {
        let pathname = window.location.pathname;
        for (let i = 0; i < sources_types.length; i += 1) {
            let regExp = new RegExp(sources_types[i].regex_page_url);
            if (regExp.test(pathname) == true) {
                return sources_types[i];
            }
        }
        throw `source_type not found for ${pathname}`;
    };

    const get_metric_from_schema = async (http_prefix, source_type, source_array, metric_schema, metric_query, status, include_empty_ts) => {
        let metrics = await get_metrics(http_prefix, source_type, source_array, status, include_empty_ts);

        if (metric_schema === 'top:iface:ndpi') {
            return metrics.find((m) => m.schema == metric_schema && m.query == metric_query) ||
                metrics.find((m) => m.schema == 'top:iface:ndpi_full' && m.query == metric_query)
        }
        const metric = metrics.find((m) => m.schema == metric_schema && m.query == metric_query);
        return metric
    };

    const get_metric_query_from_ts_query = (ts_query, source_type) => {
        if (source_type == null) {
            source_type = get_current_page_source_type();
        }
        let source_def_dict = {};
        source_type.source_def_array.forEach((s_def) => source_def_dict[s_def.value] = true);
        let metric_query = null;
        let ts_query_array = ts_query.split(",");
        for (let i = 0; i < ts_query_array.length; i += 1) {
            let ts_val_key = ts_query_array[i].split(":")[0];
            if (source_def_dict[ts_val_key] == null) {
                if (!metric_query) { metric_query = `${ts_query_array[i]}`; }
                else { metric_query = `${metric_query},${ts_query_array[i]}`; }
            }
        }
        return metric_query;
    };

    const get_default_metric = (metrics, metric_ts_schema, metric_query) => {
        let default_metric;
        if (metric_ts_schema != null) {
            default_metric = metrics.find((m) => m.schema == metric_ts_schema && (metric_query == null || m.query == metric_query));
        }
        if (default_metric == null) {
            default_metric = metrics.find((m) => m.default_visible == true);
        }
        if (default_metric != null) {
            return default_metric;
        }
        return metrics[0];
    };

    const metricsManager = function () {
        return {
            set_timeseries_groups_in_url,
            get_timeseries_groups_from_url,
            get_default_timeseries_groups,
            get_ts_group,
            get_ts_group_id,

            sources_types,
            sources_types_tables,
            get_source_type_from_id,
            get_current_page_source_type,

            get_sources,
            get_default_source_array,
            get_source_array_from_value_dict,
            get_source_array_from_value_array,
            get_default_source_value_array,

            get_metrics,
            get_metric_from_schema,
            get_metric_query_from_ts_query,
            get_default_metric,

            set_source_value_object_in_url,

            ui_types,
        };
    }();

    /**
    		(C) 2022 - ntop.org
    */

    function getSerieId(serie) {
    	return dygraphFormat.getSerieId(serie);
    }

    function getSerieName(name, id, tsGroup, useFullName) {
    	return dygraphFormat.getSerieName(name, id, tsGroup, useFullName);
    }

    function getYaxisId(metric) {
    	return `${metric.measure_unit}_${metric.scale}`;
    }

    const groupsOptionsModesEnum = {
    	'1_chart_x_metric': { value: "1_chart_x_metric", label: i18n('page_stats.layout_1_per_1') },
    	'1_chart_x_yaxis': { value: "1_chart_x_yaxis", label: i18n('page_stats.layout_1_per_y') },
    };

    function getGroupOptionMode(group_id) {
    	return groupsOptionsModesEnum[group_id] || null;
    }
    /* This function is going to translate the response sent from the server to the formatted data needed from the chart library */
    function tsArrayToOptionsArray(tsOptionsArray, tsGroupsArray, groupsOptionsMode, tsCompare) {
    	/* One chart per metric requested */
    	if (groupsOptionsMode.value == groupsOptionsModesEnum["1_chart_x_metric"].value) {
    		return tsArrayToOptionsArrayRaw(tsOptionsArray, tsGroupsArray, groupsOptionsMode, tsCompare);
    	}
    	let splittedTsArray = splitTsArrayStacked(tsOptionsArray, tsGroupsArray);
    	let DygraphOptionsStacked = tsArrayToOptionsArrayRaw(splittedTsArray.stacked.tsOptionsArray, splittedTsArray.stacked.tsGroupsArray, groupsOptionsMode, tsCompare);
    	let DygraphOptionsNotStacked = tsArrayToOptionsArrayRaw(splittedTsArray.not_stacked.tsOptionsArray, splittedTsArray.not_stacked.tsGroupsArray, groupsOptionsMode, tsCompare);
    	//console.log([...DygraphOptionsStacked, ...DygraphOptionsNotStacked])
    	return [...DygraphOptionsStacked, ...DygraphOptionsNotStacked];
    }

    function splitTsArrayStacked(tsOptionsArray, tsGroupsArray) {
    	let tsOptionsArrayStacked = [];
    	let tsGroupsArrayStacked = [];
    	let tsOptionsArrayNotStacked = [];
    	let tsGroupsArrayNotStacked = [];
    	tsGroupsArray.forEach((tsGroup, i) => {
    		if (tsGroup.metric?.draw_stacked == true) {
    			tsOptionsArrayStacked.push(tsOptionsArray[i]);
    			tsGroupsArrayStacked.push(tsGroup);
    		} else {
    			tsOptionsArrayNotStacked.push(tsOptionsArray[i]);
    			tsGroupsArrayNotStacked.push(tsGroup);
    		}
    	});
    	return {
    		stacked: {
    			tsOptionsArray: tsOptionsArrayStacked,
    			tsGroupsArray: tsGroupsArrayStacked,
    		},
    		not_stacked: {
    			tsOptionsArray: tsOptionsArrayNotStacked,
    			tsGroupsArray: tsGroupsArrayNotStacked,
    		},
    	};
    }

    function tsArrayToOptionsArrayRaw(tsOptionsArray, tsGroupsArray, groupsOptionsMode, tsCompare) {
    	let useFullName = false;
    	if (groupsOptionsMode.value == groupsOptionsModesEnum["1_chart_x_yaxis"].value) {
    		let tsDict = {};
    		tsGroupsArray.forEach((tsGroup, i) => {
    			let yaxisId = getYaxisId(tsGroup.metric);
    			let tsEl = { tsGroup, tsOptions: tsOptionsArray[i] };
    			if (tsDict[yaxisId] == null) {
    				tsDict[yaxisId] = [tsEl];
    			} else {
    				tsDict[yaxisId].push(tsEl);
    			}
    		});
    		useFullName = tsGroupsArray.length > 1 || (tsGroupsArray.length > 0
    			&& tsGroupsArray[0].source_type.display_full_name === true);
    		let DygraphOptionsArray = [];
    		for (let key in tsDict) {
    			let tsArray = tsDict[key];
    			let tsOptionsArray2 = tsArray.map((ts) => ts.tsOptions);
    			let tsGroupsArray2 = tsArray.map((ts) => ts.tsGroup);
    			let DygraphOptions = tsArrayToOptions(tsOptionsArray2, tsGroupsArray2, tsCompare, useFullName);
    			DygraphOptionsArray.push(DygraphOptions);
    		}
    		return DygraphOptionsArray;
    	} else if (groupsOptionsMode.value == groupsOptionsModesEnum["1_chart_x_metric"].value) {
    		useFullName = tsOptionsArray.length > 1 || (tsGroupsArray.length > 0
    			&& tsGroupsArray[0].source_type.display_full_name === true);
    		let optionsArray = [];
    		tsOptionsArray.forEach((tsOptions, i) => {
    			let options = tsArrayToOptions([tsOptions], [tsGroupsArray[i]], tsCompare, useFullName);
    			optionsArray.push(options);
    		});
    		return optionsArray;
    	}
    	return [];
    }

    /* *********************************************** */

    /* This function is used to format a simple timeseries given an array 
     */
    function formatSimpleSerie(data, serie_name, chart_type, formatters, value_range) {
    	return dygraphFormat.formatSimpleSerie(data, serie_name, chart_type, formatters, value_range);
    }

    /* *********************************************** */

    /* Given an array of timeseries, it compacts them into a single array 
     * and return the configuration for the timeserie with the data 
     */
    function tsArrayToOptions(tsOptionsArray, tsGroupsArray, tsCompare, useFullName) {
    	return dygraphFormat.formatSerie(tsOptionsArray, tsGroupsArray, tsCompare, useFullName);
    }

    /* *********************************************** */

    function getTsQuery(tsGroup, not_metric_query, enable_source_def_value_dict) {
    	let tsQuery = tsGroup.source_type.source_def_array.map((source_def, i) => {
    		if (enable_source_def_value_dict != null && !enable_source_def_value_dict[source_def.value]) { return null; }
    		let source_value = tsGroup.source_array[i].value;
    		return `${source_def.value}:${source_value}`;
    	}).filter((s) => s != null).join(",");

    	if (!not_metric_query && tsGroup.metric.query != null) {
    		tsQuery = `${tsQuery},${tsGroup.metric.query}`;
    	}
    	return tsQuery;
    }

    function getMainSourceDefIndex(tsGroup) {
    	let source_def_array = tsGroup.source_type.source_def_array;
    	for (let i = 0; i < source_def_array.length; i += 1) {
    		let source_def = source_def_array[i];
    		if (source_def.main_source_def == true) { return i; }
    	}
    	return 0;

    }

    function getTsChartsDataUrl(httpPrefix, epochStatus, tsCompare, timeseriesGroups, isPro) {
    	let paramsEpochObj = { epoch_begin: epochStatus.epoch_begin, epoch_end: epochStatus.epoch_end };
    	let url_and_body = {};
    	if (!isPro) {
    		let tsDataUrl = `${httpPrefix}/lua/rest/v2/get/timeseries/ts.lua`;
    		let paramsUrlRequest = `ts_compare=${tsCompare}&version=4&zoom=${tsCompare}&limit=180`;
    		let tsGroup = timeseriesGroups[0];
    		let main_source_index = getMainSourceDefIndex(tsGroup);
    		let tsQuery = getTsQuery(tsGroup);
    		let pObj = {
    			...paramsEpochObj,
    			ts_query: tsQuery,
    			ts_schema: `${tsGroup.metric.schema}`,
    		};
    		if (!tsGroup.source_type.source_def_array[main_source_index].disable_tskey) {
    			pObj.tskey = tsGroup.source_array[main_source_index].value;
    		}
    		let pUrlRequest = ntopng_url_manager$1.add_obj_to_url(pObj, paramsUrlRequest);
    		url_and_body.url = `${tsDataUrl}?${pUrlRequest}`;
    	} else {
    		let paramsChart = {
    			zoom: tsCompare,
    			limit: 180,
    			version: 4,
    			ts_compare: tsCompare,
    		};

    		let tsRequests = timeseriesGroups.map((tsGroup) => {
    			let main_source_index = getMainSourceDefIndex(tsGroup);
    			let tsQuery = getTsQuery(tsGroup);
    			let pObj = {
    				ts_query: tsQuery,
    				ts_schema: `${tsGroup.metric.schema}`,
    			};
    			if (!tsGroup.source_type.source_def_array[main_source_index].disable_tskey) {
    				pObj.tskey = tsGroup.source_array[main_source_index].value;
    			}
    			return pObj;
    		});			
    		url_and_body.url = `${httpPrefix}/lua/pro/rest/v2/get/timeseries/ts_multi.lua`;
    		url_and_body.body = { ts_requests: tsRequests, ...paramsEpochObj, ...paramsChart };
    	}

    	return url_and_body;
    }

    async function getTsChartsOptions(httpPrefix, epochStatus, tsCompare, timeseriesGroups, isPro) {

    	let tsChartsOptions;
    	const url_and_body = getTsChartsDataUrl(httpPrefix, epochStatus, tsCompare, timeseriesGroups, isPro);

    	if (!isPro) {
    		let tsChartOption = await ntopng_utility$1.http_request(url_and_body.url);
    		tsChartsOptions = [tsChartOption];
    	} else {
    		let headers = {
    			'Content-Type': 'application/json'
    		};
    		tsChartsOptions = await ntopng_utility$1.http_request(url_and_body.url, { method: 'post', headers, body: JSON.stringify(url_and_body.body) });
    	}
    	return tsChartsOptions;
    }

    const timeseriesUtils = function () {
    	return {
    		groupsOptionsModesEnum,
    		tsArrayToOptions,
    		formatSimpleSerie,
    		tsArrayToOptionsArray,
    		getGroupOptionMode,
    		getSerieId,
    		getSerieName,
    		getTsChartsOptions,
    		getTsChartsDataUrl,
    		getTsQuery,
    		getMainSourceDefIndex,
    	};
    }();

    const _hoisted_1$23 = { class: "nav nav-tabs" };
    const _hoisted_2$1S = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_3$1G = { class: "form-group row" };
    const _hoisted_4$1D = { class: "col-form-label col-sm-4" };
    const _hoisted_5$1n = { class: "col-sm-8" };
    const _hoisted_6$18 = {
      key: 0,
      class: "form-group ms-2 me-2 mb-2 mt-3 row"
    };
    const _hoisted_7$11 = { class: "form-group row" };
    const _hoisted_8$T = { class: "col-form-label col-sm-4" };
    const _hoisted_9$I = { class: "col-sm-8" };
    const _hoisted_10$C = ["title"];
    const _hoisted_11$z = {
      key: 0,
      class: "form-group mt-2 row"
    };
    const _hoisted_12$u = { class: "col-form-label col-sm-4" };
    const _hoisted_13$o = { class: "col-sm-8" };
    const _hoisted_14$m = {
      key: 1,
      class: "form-group mt-2 row"
    };
    const _hoisted_15$k = { class: "col-form-label col-sm-4" };
    const _hoisted_16$k = { class: "col-sm-8" };
    const _hoisted_17$i = ["onInput", "onUpdate:modelValue", "pattern"];
    const _hoisted_18$f = {
      key: 2,
      class: "form-group mt-2 row"
    };
    const _hoisted_19$e = { class: "col-form-label col-sm-4" };
    const _hoisted_20$e = { class: "col-sm-7" };
    const _hoisted_21$e = ["onInput", "onUpdate:modelValue", "pattern"];
    const _hoisted_22$e = { class: "col-sm-1" };
    const _hoisted_23$e = ["onClick", "title", "disabled"];
    const _hoisted_24$d = {
      class: "form-group row mt-2",
      style: {"text-align":"end"}
    };
    const _hoisted_25$c = { class: "col-sm-12" };
    const _hoisted_26$b = ["disabled"];
    const _hoisted_27$b = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_28$9 = { class: "form-group row" };
    const _hoisted_29$9 = { class: "col-form-label col-sm-4" };
    const _hoisted_30$9 = { class: "col-sm-8" };
    const _hoisted_31$8 = ["disabled"];


    var script$2g = {
      __name: 'modal-timeseries',
      props: {
        sources_types_enabled: Object,
    },
      emits: ['apply'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const props = __props;

    const modal_id = ref(null);
    const select_search_metrics = ref(null);

    const showed = () => {};

    const action = ref("select"); // add/select 

    let current_page_source_type = metricsManager.get_current_page_source_type();

    const sources_types = metricsManager.sources_types;
    const selected_source_type = ref(current_page_source_type);

    const ui_types = metricsManager.ui_types;
    const sources_array = ref([]); // array of sources, each element is the sources list of source_type.source_def_array[i]
    const selected_source_array = ref([]);
    const selected_source_text_array = ref([]); // array of source_value binding with input text
    // const sub_sources = ref([]);
    // const selected_sub_source = ref({});
    const selected_sources_union_label = ref("");
    const source_text_validation_array = ref ([]);

    const is_selected_source_changed = ref(false);
    const enable_apply_source = computed$1(() => {
        if (is_selected_source_changed.value == false) {
    	return false;
        }
        for (let i = 0; i < source_text_validation_array.value.length; i += 1) {
    	let rg_text = source_text_validation_array.value[i];
    	if (rg_text == null) { continue; }
    	let regex = new RegExp(rg_text);
    	let source_text = selected_source_text_array.value[i];
    	if (regex.test(source_text) == false) {
    	    return false;
    	}	
        }
        return true;
    });
    const hide_sources = ref(false);

    const metrics = ref([]);
    const selected_metric = ref({});

    const timeseries_groups_added = ref([]);

    const timeseries_to_add = ref([]);

    const emit = __emit;

    let wait_init = null;

    onBeforeMount(() => {
        sources_types.forEach((source_type) => {
    	props.sources_types_enabled[source_type.id];
        });    
    });

    onMounted(async () => {
        wait_init = init();
    });

    const show = async (timeseries_groups) => {
        timeseries_groups_added.value = timeseries_groups;
        await wait_init;
        action.value = "select";
        modal_id.value.show();
    };

    function change_action(a) {
        action.value = a;
    }

    async function change_source_type() {
        is_selected_source_changed.value = false;
        set_regex();
        set_hide_sources();
        await set_sources_array();
        await set_metrics();
    }

    function set_hide_sources() {
        let source_type = selected_source_type.value;
        hide_sources.value = source_type.source_def_array.map((sd) => sd.ui_type == ui_types.hide).every((hide) => hide == true);
    }

    async function apply_source_array() {
        is_selected_source_changed.value = false;
        selected_source_text_array.value.forEach((source_value, i) => {
    	let source_def = selected_source_type.value.source_def_array[i];
    	if (source_def.ui_type == ui_types.input) {
    	    let source = selected_source_array.value[i];
                set_source_input(source, source_value);
    	}
        });    
        await change_source_array();
        set_selected_sources_union_label();
    }

    function set_source_input(source, source_value) {
        source.value = source_value;
        source.label = source_value;
    }

    async function change_source_array() {
        // reload metrics 
        await set_metrics();
    }

    function change_selected_source(source_def, source_def_index, force_reload_sources) {
        is_selected_source_changed.value = true;
        if (force_reload_sources == true) {
            reload_sources(source_def, source_def_index);
        }
    }

    // reload all sources for each source_def with refresh_on_sources_change == true
    async function reload_sources(source_def, source_def_index) {
        if (source_def.ui_type == ui_types.input_confirm) {
            set_source_input(selected_source_array.value[source_def_index], selected_source_text_array.value[source_def_index]);
        }
        let source_def_array = selected_source_type.value.source_def_array;
        let source_value_array = selected_source_array.value.map((s) => s.value);
        for (let i = source_def_index + 1; i < source_def_array.length; i += 1) {
            const source_def = source_def_array[i];
            if (!source_def.refresh_on_sources_change) { continue; }

            let sources = await metricsManager.get_sources(http_prefix, selected_source_type.value.id, source_def, source_value_array);
            sources_array.value[i] = sources;
            if (sources.length > 0) {
                selected_source_array.value[i] = sources[0];
            } else {
                selected_source_array.value[i] = { label: "", value: "" };
                console.warn(`No sources availables to select for ${selected_source_type.value.id} sorce_def`);
            }
        }
    }

    function set_regex() {
        let regex_source_array = selected_source_type.value?.source_def_array.map((source_def) => source_def.regex_type);
        if (regex_source_array == null) { regex_source_array = []; }
        source_text_validation_array.value = regex_source_array.map((regex_source) => {
    	if (regex_source == null) { return  null; }
    	return regexValidation.get_data_pattern(regex_source);
        });
    }

    function get_selected_sources_union_label() {
        let source_label_array = selected_source_array.value.filter((source) => source.label != null && source.label != "").map((source) => source.label);
        let label = source_label_array.join(" - ");
        return `${label}`;
    }

    function set_selected_sources_union_label() {
        selected_sources_union_label.value = get_selected_sources_union_label();
    }

    async function set_sources_array() {
        let source_def_array = selected_source_type.value.source_def_array;
        let sources_array_temp = [];
        let default_source_array = await metricsManager.get_default_source_array(http_prefix, selected_source_type.value);
        let default_source_value_array = default_source_array.map((s) => s.value);
        for (let i = 0; i < source_def_array.length; i += 1) {
    	let sources = await metricsManager.get_sources(http_prefix, selected_source_type.value.id, source_def_array[i], default_source_value_array);
    	sources_array_temp.push(sources);
        }
        selected_source_array.value = default_source_array;
        sources_array.value = sources_array_temp;
        selected_source_text_array.value = default_source_value_array;
        set_selected_sources_union_label();
    }

    async function set_metrics() {
        metrics.value = await metricsManager.get_metrics(http_prefix, selected_source_type.value, selected_source_array.value);
        metrics.value.sort(NtopUtils.sortAlphabetically);
        selected_metric.value = metricsManager.get_default_metric(metrics.value);
        update_timeseries_to_add();    
    }

    async function init() {
        await change_source_type();
        // take default visible
        update_timeseries_to_add();
        //emit('apply', timeseries_groups_added.value);
    }

    function update_timeseries_to_add(default_config) {
        timeseries_to_add.value = [];
        let timeseries = selected_metric.value.timeseries;
        for (let ts_id in timeseries) {
        	timeseries_to_add.value.push({
        	    id: ts_id,
        	    label: timeseries[ts_id].label,
        	    raw: true,
    	    past: false,
        	    avg: false,
        	    perc_95: false,
        	});
        }
    }

    function get_timeseries_group_name(ts_group) {
        let source_type_name = ts_group.source_type.label;
        let source_def_index = timeseriesUtils.getMainSourceDefIndex(ts_group);
        let source = ts_group.source_array[source_def_index];
        let source_name = source.label;
        let metric_name = ts_group.metric.label;
        return `${source_type_name} - ${source_name} - ${metric_name}`;
    }

    function get_timeseries_group_id(ts_group) {
        let source_type, source_array, metric;
        if (ts_group == null) {
    	source_type = selected_source_type.value;
    	source_array = selected_source_array.value;
    	metric = selected_metric.value;
        } else {
    	source_type = ts_group.source_type;
    	source_array = ts_group.source_array;
    	metric = ts_group.metric;
        }
        let id = metricsManager.get_ts_group_id(source_type, source_array, metric);
        return id;
    }

    const delete_ts = (ts_group_id) => {
        timeseries_groups_added.value = timeseries_groups_added.value.filter((ts_group) => get_timeseries_group_id(ts_group) != ts_group_id);
    };

    const set_timeseries_groups = (timeseries_groups, emit_apply) => {
        timeseries_groups_added.value = timeseries_groups;
        if (emit_apply) {
    	emit('apply', timeseries_groups_added.value);
        }
    };

    const add_ts_group = (ts_group_to_add, emit_apply) => {
        let ts_group_index = timeseries_groups_added.value.findIndex((ts_group) => ts_group.id == ts_group_to_add.id);
        if (ts_group_index < 0) {
    	timeseries_groups_added.value.push(ts_group_to_add);
        } else {
    	timeseries_groups_added.value[ts_group_index] = ts_group_to_add;
        }

        if (emit_apply) {
    	emit('apply', timeseries_groups_added.value);
        }
    };

    const apply = () => {
        if (action.value == "add") {
    	let ts_group_id = get_timeseries_group_id();
    	let ts_group = {
    	    id: ts_group_id,
    	    source_type: selected_source_type.value,
    	    source_array: ntopng_utility$1.clone(selected_source_array.value),
    	    metric: selected_metric.value,
    	    timeseries: ntopng_utility$1.clone(timeseries_to_add.value),
    	};
    	add_ts_group(ts_group);
        }
        emit('apply', timeseries_groups_added.value);
        close();
    };

    const close = () => {
        modal_id.value.close();
    };
    const _i18n = (t) => i18n(t);

    __expose({ show, close, add_ts_group, set_timeseries_groups });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[8] || (_cache[8] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("modal_timeseries.title")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("ul", _hoisted_1$23, [
            createBaseVNode("li", {
              class: "nav-item",
              onClick: _cache[0] || (_cache[0] = $event => (change_action('add')))
            }, [
              createBaseVNode("a", {
                class: normalizeClass(["nav-link", {'active': action.value == 'add'}]),
                href: "#"
              }, toDisplayString(_i18n("modal_timeseries.add_timeseries")), 3 /* TEXT, CLASS */)
            ]),
            createBaseVNode("li", {
              class: "nav-item",
              onClick: _cache[1] || (_cache[1] = $event => (change_action('select')))
            }, [
              createBaseVNode("a", {
                class: normalizeClass(["nav-link", {'active': action.value == 'select' }]),
                href: "#"
              }, toDisplayString(_i18n("modal_timeseries.manage_timeseries")), 3 /* TEXT, CLASS */)
            ])
          ]),
          createCommentVNode(" action add "),
          (action.value == 'add')
            ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                createCommentVNode(" Sources Types "),
                createBaseVNode("div", _hoisted_2$1S, [
                  createBaseVNode("div", _hoisted_3$1G, [
                    createBaseVNode("label", _hoisted_4$1D, [
                      createBaseVNode("b", null, toDisplayString(_i18n("modal_timeseries.source_type")), 1 /* TEXT */)
                    ]),
                    createBaseVNode("div", _hoisted_5$1n, [
                      createVNode(script$2H, {
                        selected_option: selected_source_type.value,
                        "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((selected_source_type).value = $event)),
                        onSelect_option: _cache[3] || (_cache[3] = $event => (change_source_type())),
                        options: unref(sources_types)
                      }, null, 8 /* PROPS */, ["selected_option", "options"])
                    ])
                  ])
                ]),
                createCommentVNode(" Sources "),
                (!hide_sources.value)
                  ? (openBlock(), createElementBlock("div", _hoisted_6$18, [
                      createBaseVNode("div", _hoisted_7$11, [
                        createBaseVNode("label", _hoisted_8$T, [
                          createBaseVNode("b", null, toDisplayString(_i18n("modal_timeseries.source")), 1 /* TEXT */)
                        ]),
                        createBaseVNode("div", _hoisted_9$I, [
                          withDirectives(createBaseVNode("input", {
                            class: "form-control",
                            "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((selected_sources_union_label).value = $event)),
                            title: selected_sources_union_label.value,
                            style: {"overflow":"hidden","text-overflow":"ellipsis","white-space":"nowrap"},
                            type: "text",
                            disabled: ""
                          }, null, 8 /* PROPS */, _hoisted_10$C), [
                            [vModelText, selected_sources_union_label.value]
                          ])
                        ])
                      ]),
                      (openBlock(true), createElementBlock(Fragment, null, renderList(selected_source_type.value.source_def_array, (source_def, source_def_index) => {
                        return (openBlock(), createElementBlock(Fragment, null, [
                          createCommentVNode(" select "),
                          (source_def.ui_type == unref(ui_types).select)
                            ? (openBlock(), createElementBlock("div", _hoisted_11$z, [
                                createBaseVNode("label", _hoisted_12$u, [
                                  createBaseVNode("b", null, toDisplayString(source_def.label), 1 /* TEXT */)
                                ]),
                                createBaseVNode("div", _hoisted_13$o, [
                                  createVNode(script$2H, {
                                    selected_option: selected_source_array.value[source_def_index],
                                    "onUpdate:selected_option": $event => ((selected_source_array.value[source_def_index]) = $event),
                                    onSelect_option: $event => (change_selected_source(source_def, source_def_index, true)),
                                    options: sources_array.value[source_def_index]
                                  }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "onSelect_option", "options"])
                                ])
                              ]))
                            : createCommentVNode("v-if", true),
                          createCommentVNode(" end select "),
                          createCommentVNode(" input text "),
                          (source_def.ui_type == unref(ui_types).input)
                            ? (openBlock(), createElementBlock("div", _hoisted_14$m, [
                                createBaseVNode("label", _hoisted_15$k, [
                                  createBaseVNode("b", null, toDisplayString(source_def.label), 1 /* TEXT */)
                                ]),
                                createBaseVNode("div", _hoisted_16$k, [
                                  withDirectives(createBaseVNode("input", {
                                    class: "form-control",
                                    onInput: $event => (change_selected_source(source_def, source_def_index)),
                                    "onUpdate:modelValue": $event => ((selected_source_text_array.value[source_def_index]) = $event),
                                    pattern: source_text_validation_array.value[source_def_index],
                                    required: "",
                                    type: "text",
                                    placeholder: ""
                                  }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_17$i), [
                                    [vModelText, selected_source_text_array.value[source_def_index]]
                                  ])
                                ])
                              ]))
                            : createCommentVNode("v-if", true),
                          createCommentVNode(" input text "),
                          createCommentVNode(" input confirm text "),
                          (source_def.ui_type == unref(ui_types).input_confirm)
                            ? (openBlock(), createElementBlock("div", _hoisted_18$f, [
                                createBaseVNode("label", _hoisted_19$e, [
                                  createBaseVNode("b", null, toDisplayString(source_def.label), 1 /* TEXT */)
                                ]),
                                createBaseVNode("div", _hoisted_20$e, [
                                  withDirectives(createBaseVNode("input", {
                                    class: "form-control",
                                    onInput: $event => (change_selected_source(source_def, source_def_index)),
                                    "onUpdate:modelValue": $event => ((selected_source_text_array.value[source_def_index]) = $event),
                                    pattern: source_text_validation_array.value[source_def_index],
                                    required: "",
                                    type: "text",
                                    placeholder: ""
                                  }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_21$e), [
                                    [vModelText, selected_source_text_array.value[source_def_index]]
                                  ])
                                ]),
                                createBaseVNode("div", _hoisted_22$e, [
                                  createBaseVNode("button", {
                                    type: "button",
                                    class: "btn btn-link btn-sm",
                                    onClick: $event => (reload_sources(source_def, source_def_index)),
                                    title: _i18n(source_def.refresh_i18n),
                                    disabled: !enable_apply_source.value
                                  }, [...(_cache[9] || (_cache[9] = [
                                    createBaseVNode("i", { class: "fas fa-refresh" }, null, -1 /* CACHED */)
                                  ]))], 8 /* PROPS */, _hoisted_23$e)
                                ])
                              ]))
                            : createCommentVNode("v-if", true),
                          createCommentVNode(" input confirm text ")
                        ], 64 /* STABLE_FRAGMENT */))
                      }), 256 /* UNKEYED_FRAGMENT */)),
                      withDirectives(createBaseVNode("div", _hoisted_24$d, [
                        createBaseVNode("div", _hoisted_25$c, [
                          createBaseVNode("button", {
                            type: "button",
                            onClick: apply_source_array,
                            disabled: enable_apply_source.value == false,
                            class: "btn btn-primary"
                          }, toDisplayString(_i18n("modal_timeseries.apply_source")), 9 /* TEXT, PROPS */, _hoisted_26$b)
                        ])
                      ], 512 /* NEED_PATCH */), [
                        [vShow, enable_apply_source.value]
                      ])
                    ]))
                  : createCommentVNode("v-if", true),
                createCommentVNode(" end Sources "),
                createCommentVNode(" Metrics "),
                createBaseVNode("div", _hoisted_27$b, [
                  createBaseVNode("div", _hoisted_28$9, [
                    createBaseVNode("label", _hoisted_29$9, [
                      createBaseVNode("b", null, toDisplayString(_i18n("modal_timeseries.metric")), 1 /* TEXT */)
                    ]),
                    createBaseVNode("div", _hoisted_30$9, [
                      createVNode(script$2H, {
                        ref_key: "select_search_metrics",
                        ref: select_search_metrics,
                        onSelect_option: _cache[5] || (_cache[5] = $event => (update_timeseries_to_add())),
                        selected_option: selected_metric.value,
                        "onUpdate:selected_option": _cache[6] || (_cache[6] = $event => ((selected_metric).value = $event)),
                        options: metrics.value
                      }, null, 8 /* PROPS */, ["selected_option", "options"])
                    ])
                  ])
                ]),
                createVNode(script$2h, {
                  title: _i18n('modal_timeseries.timeseries_list'),
                  timeseries: timeseries_to_add.value,
                  "onUpdate:timeseries": _cache[7] || (_cache[7] = $event => ((timeseries_to_add).value = $event))
                }, null, 8 /* PROPS */, ["title", "timeseries"])
              ], 64 /* STABLE_FRAGMENT */))
            : createCommentVNode("v-if", true),
          createCommentVNode(" action == add "),
          createCommentVNode(" action select"),
          (action.value == 'select')
            ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                (openBlock(true), createElementBlock(Fragment, null, renderList(timeseries_groups_added.value, (item) => {
                  return (openBlock(), createBlock(script$2h, {
                    id: get_timeseries_group_id(item),
                    title: get_timeseries_group_name(item),
                    timeseries: item.timeseries,
                    "onUpdate:timeseries": $event => ((item.timeseries) = $event),
                    show_delete_button: timeseries_groups_added.value.length > 1,
                    onDelete_ts: delete_ts
                  }, null, 8 /* PROPS */, ["id", "title", "timeseries", "onUpdate:timeseries", "show_delete_button"]))
                }), 256 /* UNKEYED_FRAGMENT */)),
                createCommentVNode(" v-for timeseries_groups_added ")
              ], 64 /* STABLE_FRAGMENT */))
            : createCommentVNode("v-if", true),
          createCommentVNode(" action == select ")
        ]),
        footer: withCtx(() => [
          withDirectives(createBaseVNode("button", {
            type: "button",
            onClick: apply,
            disabled: enable_apply_source.value,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("modal_timeseries.add")), 9 /* TEXT, PROPS */, _hoisted_31$8), [
            [vShow, action.value == 'add']
          ]),
          withDirectives(createBaseVNode("button", {
            type: "button",
            onClick: apply,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("modal_timeseries.apply")), 513 /* TEXT, NEED_PATCH */), [
            [vShow, action.value == 'select']
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$r = "\ninput[data-v-103cd911]:invalid {\n  border-color: #ff0000;\n}\n.custom-margin[data-v-103cd911] {\nmargin-left: -0.4rem;\n}\n.warn[data-v-103cd911] {\nborder-color: #ffd500;\nborder-style: solid;\n}\n";
    styleInject(css_248z$r);

    script$2g.__scopeId = "data-v-103cd911";
    script$2g.__file = "http_src/vue/modal-timeseries.vue";

    const _hoisted_1$22 = { class: "form-group mt-2 row" };
    const _hoisted_2$1R = { class: "col-form-label col-sm-4" };
    const _hoisted_3$1F = { class: "col-sm-6" };
    const _hoisted_4$1C = { class: "col-form-label col-sm-2" };
    const _hoisted_5$1m = ["disabled"];

    var script$2f = {
      __name: 'modal-download-file',
      props: {
      title: String,
      ext: String,
    },
      emits: ["download"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const filename = ref("");

    //const filename_validation = `[\`~!@#$%^&*_|+-=?;:'",.<>{}[]\\/]`;
    const filename_validation = String.raw`^[a-zA-Z_\-1-9]*$`;

    const enable_download = computed$1(() => {
      let rg_text = filename_validation;
      let regex = new RegExp(rg_text);
      return regex.test(filename.value);
    });


    const props = __props;

    const emit = __emit;

    const show = (name) => {
      if (name == null) { name = ""; }
      /* Replace all characters with _ for EXCEPT number and letters */
      name = name.replaceAll(/[^a-zA-Z0-9]/g, '_');
      filename.value = name;
      modal_id.value.show();
    };

    function download() {
      let name = `${filename.value}.${props.ext}`;
      emit('download', name);
      close();
    }

    const close = () => {
      modal_id.value.close();
    };

    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(props.title), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$22, [
            createBaseVNode("label", _hoisted_2$1R, [
              createBaseVNode("b", null, toDisplayString(_i18n("modal_download_file.filename")) + ":", 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$1F, [
              withDirectives(createBaseVNode("input", {
                class: "form-control",
                pattern: filename_validation,
                "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((filename).value = $event)),
                type: "text",
                required: ""
              }, null, 512 /* NEED_PATCH */), [
                [vModelText, filename.value]
              ])
            ]),
            createBaseVNode("label", _hoisted_4$1C, " ." + toDisplayString(props.ext), 1 /* TEXT */)
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: download,
            class: "btn btn-primary",
            disabled: enable_download.value == false
          }, toDisplayString(_i18n("modal_download_file.download")), 9 /* TEXT, PROPS */, _hoisted_5$1m)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$q = "\ninput[data-v-59120eb8]:invalid {\n  border-color: #ff0000;\n}\n.not-allowed[data-v-59120eb8] {\n  cursor: not-allowed;\n}\n";
    styleInject(css_248z$q);

    script$2f.__scopeId = "data-v-59120eb8";
    script$2f.__file = "http_src/vue/modal-download-file.vue";

    const _hoisted_1$21 = { key: 0 };
    const _hoisted_2$1Q = { class: "text-center" };


    var script$2e = {
      __name: 'datatable',
      props: {
    	id: String,
    	table_buttons: Array,
    	columns_config: Array,
    	data_url: String,
    	enable_search: Boolean,
    	filter_buttons: {
    type: Array,
    required: false,
    	},
    	table_config: {
    type: Object,
    required: false,
    	},
    	base_url: String,
    	base_params: Object, 
    },
      emits: ['drawed'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const instance = getCurrentInstance();

    const slots = useSlots();
    const props = __props;

    const emit = __emit;

    /* Add last sorting preferences to the browser cache in order to reload it the next time */
    const save_last_sort = (last_sorting, id) => {
    	if(id || props.id) {
    		/* Do not save the sorting if the direction is undefined */
    		if(last_sorting.dir != undefined) {
    			const sorting_pref = [last_sorting.col, last_sorting.dir];
    			const sorting_key = `${id || props.id}_sorting_preferences`;
    			
    			/* Use the local storage for the sorting preferences */
    			localStorage.setItem(sorting_key, sorting_pref);
    		}
    	}
    };

    const load_last_sort = (id) => {
    	let sorting_pref = null;
    	if(id || props.id) {
    		const sorting_key = `${id || props.id}_sorting_preferences`;

    		/* Use the local storage for the sorting preferences */
    		const unformatted_pref = localStorage.getItem(sorting_key);
    		if(unformatted_pref) {
    			const splitted_pref = unformatted_pref.split(',');
    			if(splitted_pref.length == 2) {
    				sorting_pref = [ Number(splitted_pref[0]), splitted_pref[1] ];
    			}
    		}
    	}

    	return sorting_pref;
    };

    let new_params = props.base_params;
    const table_id = ref(null);
    /* In case no sorting is provided use the default_sorting */
    const last_sorting = load_last_sort() || [0, "asc"];

    function loadDatatable() {
        let updated = false;
        /* Create a datatable with the buttons */
        let extend_config = {
    	serverSide: false,
    	scrollX: false,
    	destroy: true,
    	searching: props.enable_search,
    	order: [last_sorting],
    	pagingType: 'full_numbers',
    	//columnDefs: props.columns_config,
    	columns: props.columns_config,
    	autoWidth: false,
    	responsive: true,
    	ajax: {
    	    method: 'get',
    	    url: props.data_url,
    	    dataSrc: 'rsp',
    	    data: (data, settings) => {
    		if(Object.keys(data).length == 0) {
    		    return;
    		}
    		
    		const tableApi = settings.oInstance.api();
    		const orderColumnIndex = data.order[0].column;
    		const orderColumnName = tableApi.column(orderColumnIndex).name() || undefined;
    		
    		if (data.order) {
    		    data.order = data.order[0].dir;
    		    data.sort = orderColumnName;
    		}
    		
    		if (data.columns !== undefined) {
    		    delete data.columns;
    		}
    		
    		if (data.search !== undefined) {
    		    data.map_search = data.search.value;
    		    delete data.search;
    		}
    		
    		return data;
    	    },
    	    beforeSend: function() {
    		NtopUtils.showOverlays();
    	    },
    	},
    	drawCallback: function (settings) {
    	    NtopUtils.hideOverlays();
    	    emit('drawed');
    	    ntopng_events_manager.emit_custom_event(ntopng_custom_events.DATATABLE_LOADED);		 
    		 save_last_sort(settings.aLastSort[0]);
    	}
        };
        for (const item in (props.table_config || {})) {
    	extend_config[item] = props.table_config[item];
        }
        
        let config = DataTableUtils.getStdDatatableConfig(props.table_buttons);
        config = DataTableUtils.extendConfig(config, extend_config);
        table = $(table_id.value).DataTable(config);
    	 load_table_menu();
        for (const filter of (props.filter_buttons || [])) {
    	/* Set filters to active if available in the url */
    	const curr_value = ntopng_url_manager$1.get_url_entry(filter.filterMenuKey);
    	if(curr_value && curr_value != '') {
    	    let num_non_active_entries = 0;
    	    filter.filters.forEach((i) => {
    		i.currently_active = false;
    		num_non_active_entries += 1;
    		if(i.id == curr_value) {
    		    i.currently_active = true;
    		    num_non_active_entries -= 1;
    		}
    	    });
    	    
    	    if(num_non_active_entries == filter.filters.length) {
    		ntopng_url_manager$1.set_key_to_url(filter.filterMenuKey, '');
    		updated = true;
    	    }
    	}
    	
    	new DataTableFiltersMenu({
    	    filterTitle: filter.filterTitle,
    	    tableAPI: table,
    	    filters: filter.filters,
    	    filterMenuKey: filter.filterMenuKey,
    	    columnIndex: filter.columnIndex,
    	    url: props.data_url,
    	    id: filter.id,
    	    removeAllEntry: filter.removeAllEntry,
    	    callbackFunction: filter.callbackFunction
    	}).init();
        }
        
        if(updated && props.base_params) {
    	const entries = ntopng_url_manager$1.get_url_entries();
    	for(const [key, value] of (entries)) {
    	    new_params[key] = value;
    	}
    	table.ajax.url(NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, new_params));
    	reload();
        }
    }

    let table = null;
    onMounted(() => {
        loadDatatable();
    });

    function get_table_default_menu() {
        if (table == null) { return; }
        let table_wrapper = $(table.context[0].nTableWrapper);
        return $($(".row .text-end", table_wrapper).children()[0]);}

    function search_value(value_to_search) {
    	if (table == null) { return; }
    	table.search(value_to_search).draw();
    }

    let table_default_menu = null;
    function load_table_menu() {
        if (table_default_menu == null) {
    	table_default_menu = get_table_default_menu();
        }
        if (slots == null || slots.menu == null) { return; }
        let menu_array = slots.menu();
        if (menu_array == null || menu_array.length == 0) { return; }
        let node = slots.menu()[0];
        let element = $("<div class='d-inline-block'></div>")[0];
        const { vNode, el } = render_component(node, { app:  instance?.appContext?.app, element });
        // const { vNode, el } = render_component(Test, { app:  instance.appContext.app });
        let table_wrapper = $(table.context[0].nTableWrapper);
        $($(".row .text-end", table_wrapper).children()[0]).append(el);
    }

    const reload = () => {
        if (table == null) { return; }
        table.ajax.reload();
    };

    const update_url = (new_url) => {
        if (table == null) { return; }
        table.ajax.url(new_url);
    };

    const delete_button_handlers = (handlerId) => {
        DataTableUtils.deleteButtonHandlers(handlerId);
    };

    let is_destroyed = false;

    const destroy_table = () => {
        table.clear();
        table.destroy(true);
        is_destroyed = true;
        props.columns_config.filter((config) => config.handlerId != null).forEach((config) => {
    	delete_button_handlers(config.handlerId);
        });
    };

    const refresh_menu = () => {
        let table_wrapper = $(table.context[0].nTableWrapper);
        $($(".row .text-end", table_wrapper).children()[0]).html("");
        load_table_menu();
    };

    const is_last_sorting_available = (id) => {
    	return load_last_sort(id) != null;
    };

    __expose({ reload, delete_button_handlers, destroy_table, update_url, refresh_menu, is_last_sorting_available, load_last_sort, save_last_sort, search_value });

    onBeforeUnmount(() => {
        if (is_destroyed == true) { return; }
        destroy_table();
        // table.destroy(true);
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        (openBlock(), createElementBlock("div", _hoisted_1$21, [
              createCommentVNode(" <slot name=\"menu\"></slot> ")
            ]))
          ,
        createBaseVNode("div", null, [
          createBaseVNode("table", {
            ref_key: "table_id",
            ref: table_id,
            class: "table w-100 table-striped table-hover table-bordered"
          }, [
            createBaseVNode("thead", null, [
              createBaseVNode("tr", null, [
                (openBlock(true), createElementBlock(Fragment, null, renderList(__props.columns_config, (item) => {
                  return (openBlock(), createElementBlock("th", _hoisted_2$1Q, toDisplayString(item.columnName), 1 /* TEXT */))
                }), 256 /* UNKEYED_FRAGMENT */))
              ])
            ]),
            _cache[0] || (_cache[0] = createBaseVNode("tbody", null, null, -1 /* CACHED */))
          ], 512 /* NEED_PATCH */)
        ])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$2e.__file = "http_src/vue/datatable.vue";

    const _hoisted_1$20 = {
      key: 0,
      class: "table table-bordered table-striped"
    };
    const _hoisted_2$1P = { key: 0 };
    const _hoisted_3$1E = ["innerHTML"];
    const _hoisted_4$1B = ["innerHTML"];
    const _hoisted_5$1l = ["innerHTML"];
    const _hoisted_6$17 = {
      key: 1,
      class: "table table-bordered table-striped"
    };
    const _hoisted_7$10 = ["innerHTML"];
    const _hoisted_8$S = ["innerHTML"];
    const _hoisted_9$H = ["colspan", "innerHTML"];


    var script$2d = {
      __name: 'bootstrap-table',
      props: {
      id: String,
      columns: Array,
      rows: Array,
      print_html_column: Function,
      print_html_row: Function,
      print_html_title: Function,
      horizontal: Boolean,
      wrap_columns: Boolean,
      hide_head: Boolean,
      no_background: Boolean,
      head_width: Number,
      row_width: Number,
      text_align: String
    },
      setup(__props) {

    const row_class = ref();
    const props = __props;

    onBeforeMount(() => {
      let classes = '';
      if (props.row_width) {
        classes = classes + ' col-' + props.row_width;
      }
      if (props.text_align) {
        classes = classes + ' ' + props.text_align;
      }
      row_class.value = classes;
    });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createCommentVNode(" Normal table "),
        (!(__props.horizontal) || (__props.horizontal == false))
          ? (openBlock(), createElementBlock("table", _hoisted_1$20, [
              (!__props.hide_head)
                ? (openBlock(), createElementBlock("thead", _hoisted_2$1P, [
                    createBaseVNode("tr", null, [
                      (openBlock(true), createElementBlock(Fragment, null, renderList(__props.columns, (col) => {
                        return (openBlock(), createElementBlock("th", {
                          scope: "col",
                          class: normalizeClass(col.class),
                          innerHTML: __props.print_html_column(col)
                        }, null, 10 /* CLASS, PROPS */, _hoisted_3$1E))
                      }), 256 /* UNKEYED_FRAGMENT */))
                    ])
                  ]))
                : createCommentVNode("v-if", true),
              createBaseVNode("tbody", null, [
                (openBlock(true), createElementBlock(Fragment, null, renderList(__props.rows, (row) => {
                  return (openBlock(), createElementBlock("tr", null, [
                    (__props.wrap_columns)
                      ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(__props.columns, (col) => {
                          return (openBlock(), createElementBlock("td", {
                            scope: "col",
                            class: normalizeClass(col.class)
                          }, [
                            createBaseVNode("div", {
                              class: "wrap-column",
                              style: normalizeStyle(col.style),
                              innerHTML: __props.print_html_row(col, row)
                            }, null, 12 /* STYLE, PROPS */, _hoisted_4$1B)
                          ], 2 /* CLASS */))
                        }), 256 /* UNKEYED_FRAGMENT */))
                      : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(__props.columns, (col) => {
                          return (openBlock(), createElementBlock("td", {
                            scope: "col",
                            class: normalizeClass([col.class, "wrap-column"]),
                            style: normalizeStyle(col.style),
                            innerHTML: __props.print_html_row(col, row)
                          }, null, 14 /* CLASS, STYLE, PROPS */, _hoisted_5$1l))
                        }), 256 /* UNKEYED_FRAGMENT */))
                  ]))
                }), 256 /* UNKEYED_FRAGMENT */))
              ])
            ]))
          : (openBlock(), createElementBlock("table", _hoisted_6$17, [
              createBaseVNode("tbody", null, [
                (openBlock(true), createElementBlock(Fragment, null, renderList(__props.rows, (row) => {
                  return (openBlock(), createElementBlock("tr", null, [
                    (__props.head_width)
                      ? (openBlock(), createElementBlock("th", {
                          key: 0,
                          class: normalizeClass('col-' + __props.head_width),
                          innerHTML: __props.print_html_title(row.name)
                        }, null, 10 /* CLASS, PROPS */, _hoisted_7$10))
                      : (openBlock(), createElementBlock("th", {
                          key: 1,
                          class: "col-2",
                          innerHTML: __props.print_html_title(row.name)
                        }, null, 8 /* PROPS */, _hoisted_8$S)),
                    (openBlock(true), createElementBlock(Fragment, null, renderList(row.values, (value) => {
                      return (openBlock(), createElementBlock("td", {
                        class: normalizeClass(row_class.value),
                        style: {"overflow-wrap":"anywhere","max-width":"500px"},
                        colspan: [(row.values.length <= 1) ? 2 : 1],
                        innerHTML: __props.print_html_row(value)
                      }, null, 10 /* CLASS, PROPS */, _hoisted_9$H))
                    }), 256 /* UNKEYED_FRAGMENT */))
                  ]))
                }), 256 /* UNKEYED_FRAGMENT */))
              ])
            ]))
      ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
    }
    }

    };

    script$2d.__file = "http_src/vue/bootstrap-table.vue";

    const _hoisted_1$1$ = { class: "col-12 mb-2 mt-2" };
    const _hoisted_2$1O = {
      class: "card h-100 overflow-hidden",
      style: {"position":"relative"}
    };
    const _hoisted_3$1D = ["title"];
    const _hoisted_4$1A = ["title"];
    const _hoisted_5$1k = ["disabled", "title"];
    const _hoisted_6$16 = ["title"];
    const _hoisted_7$$ = { class: "form-group ms-1 mt-2 d-flex align-items-center" };
    const _hoisted_8$R = { class: "inline select2-size me-2" };
    const _hoisted_9$G = { class: "inline select2-size me-2" };
    const _hoisted_10$B = { class: "mt-4 card card-shadow" };
    const _hoisted_11$y = {
      key: 0,
      class: "card-body"
    };
    const _hoisted_12$t = {
      key: 0,
      class: "mt-4 card card-shadow"
    };
    const _hoisted_13$n = {
      key: 0,
      class: "card-body"
    };
    const _hoisted_14$l = { class: "inline select2-size me-2 mt-2" };

    const page_snapshots = "timeseries";


    var script$2c = {
      __name: 'page-stats',
      props: {
        csrf: String,
        is_ntop_pro: Boolean,
        source_value_object: Object,
        sources_types_enabled: Object,
        sources_types_top_enabled: Object,
        enable_snapshots: Boolean,
        is_history_enabled: Boolean,
        traffic_extraction_permitted: Boolean,
        is_dark_mode: Boolean,
    },
      setup(__props) {

    const props = __props;

    //ntopng_utility.check_and_set_default_time_interval();

    const _i18n = (t) => i18n(t);
    let id_chart = "chart";
    let id_date_time_picker = "date_time_picker";
    let chart_type = ntopChartApex.typeChart.TS_LINE;
    ref({});
    ref(false);
    const charts = ref([]);
    const date_time_picker = ref(null);
    const top_table_ref = ref(null);
    const modal_timeseries = ref(null);
    const modal_snapshot = ref(null);
    const modal_download_file = ref(null);
    const loading = ref(true);

    const is_safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
    const image_button_title = is_safari ? _i18n('page_stats.download_image_disabled') : _i18n('page_stats.title_modal_download_file');

    const min_time_interval_id = ref(null);
    const metrics = ref([]);
    const selected_metric = ref({});
    metricsManager.get_current_page_source_type();

    const enable_stats_table = ref(false);
    ref(false);

    /**
     * { key: identifier of Chart component, if change Chart will be destroyed and recreated,
     *  chart_options: chart options }[]
     **/
    const charts_options_items = ref([]);

    /**
     * Modes that represent how it's possible display timeseries.
     */
    const groups_options_modes = ntopng_utility$1.object_to_array(timeseriesUtils.groupsOptionsModesEnum);
    /**
     * Current display timeseries mode.
     */
    const current_groups_options_mode = ref(init_groups_option_mode());

    let last_timeseries_groups_loaded = null;

    const custom_metric = { label: i18n('page_stats.custom_metrics'), currently_active: false };

    const ts_menu_ready = ref(false);
    const time_preset_list = [
        { value: "10_min", label: i18n('show_alerts.presets.10_min'), currently_active: false },
        { value: "30_min", label: i18n('show_alerts.presets.30_min'), currently_active: true },
        { value: "hour", label: i18n('show_alerts.presets.hour'), currently_active: false },
        { value: "2_hours", label: i18n('show_alerts.presets.2_hours'), currently_active: false },
        { value: "6_hours", label: i18n('show_alerts.presets.6_hours'), currently_active: false },
        { value: "12_hours", label: i18n('show_alerts.presets.12_hours'), currently_active: false },
        { value: "day", label: i18n('show_alerts.presets.day'), currently_active: false },
        { value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
        { value: "month", label: i18n('show_alerts.presets.month'), currently_active: false },
        { value: "year", label: i18n('show_alerts.presets.year'), currently_active: false },
        { value: "custom", label: i18n('show_alerts.presets.custom'), currently_active: false, disabled: true, },
    ];

    function init_groups_option_mode() {
        let groups_mode = ntopng_url_manager$1.get_url_entry("timeseries_groups_mode");
        if (groups_mode != null && groups_mode != "") {
            return timeseriesUtils.getGroupOptionMode(groups_mode);
        }
        return groups_options_modes[0];
    }

    function set_default_source_object_in_url() {
        if (props.source_value_object == null) { return; }
        let source_type = metricsManager.get_current_page_source_type();
        metricsManager.set_source_value_object_in_url(source_type, props.source_value_object);
    }

    /* This function retrieves the global status (epoch_begin and epoch_end),
     * in case it's empty format it and update the status
     */
    function retrieveGlobalStatus() {
        // Retrieve the status
        let status = ntopng_status_manager$1.get_status();
        let update_status = false;
        // In case it is empty or missing something, update it
        if (!status) {
            update_status = true;
            status = status = {};
        }
        if (!status.epoch_begin) {
            update_status = true;
            status.epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
        }
        if (!status.epoch_end) {
            update_status = true;
            status.epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");
        }
        // In case some update was done, update the global status
        if (update_status) {
            ntopng_status_manager$1.replace_status(status);
        }

        return status;
    }

    onBeforeMount(async () => {

        if (ntopng_url_manager$1.get_url_entry("page") == "va_historical") {
            let columns_tmp = [];
            stats_columns.forEach((item) => {
                if (item.va) {
                    columns_tmp.push(item);
                }
            });

            stats_columns = columns_tmp;
        }

        if (props.source_value_object.is_va) {
            min_time_interval_id.value = "hour";
            ntopng_utility$1.check_and_set_default_time_interval("day");
        }
        set_default_source_object_in_url();
    });

    onMounted(async () => {
        init();
        await Promise.all([
            ntopng_sync.on_ready(id_date_time_picker),
        ]);
    });

    async function init() {
        //get_default_timeseries_groups
        let push_custom_metric = true;
        let timeseries_groups = await metricsManager.get_timeseries_groups_from_url(http_prefix);
        let metric_ts_schema;
        let metric_query;
        if (timeseries_groups == null) {
            push_custom_metric = false;
            metric_ts_schema = ntopng_url_manager$1.get_url_entry("ts_schema");
            let ts_query = ntopng_url_manager$1.get_url_entry("ts_query");
            if (ts_query != null && ts_query != "") {
                metric_query = metricsManager.get_metric_query_from_ts_query(ts_query);
            }
            if (metric_ts_schema == "") { metric_ts_schema = null; }
            timeseries_groups = await metricsManager.get_default_timeseries_groups(http_prefix, metric_ts_schema, metric_query);
        }
        metrics.value = await get_metrics(push_custom_metric);
        if (timeseries_groups.length == 1) {
            push_custom_metric = false;
            metric_ts_schema = timeseries_groups[0]?.metric?.schema;
            metric_query =  timeseries_groups[0]?.metric?.query;
        }

        if (push_custom_metric == true) {
            selected_metric.value = custom_metric;
        } else {
            selected_metric.value = metricsManager.get_default_metric(metrics.value, metric_ts_schema, metric_query);
        }
        ts_menu_ready.value = true;
        await load_page_stats_data(timeseries_groups, true, true);
    }

    let last_push_custom_metric = null;
    async function get_metrics(push_custom_metric, force_refresh) {
        let metrics = await metricsManager.get_metrics(http_prefix);
        if (!force_refresh && last_push_custom_metric == push_custom_metric) { return metrics.value; }

        if (push_custom_metric) {
            metrics.push(custom_metric);
        }
        if (cache_snapshots == null || force_refresh) {
            cache_snapshots = await get_snapshots_metrics();
        }
        if (props.enable_snapshots) {
            let snapshots_metrics = cache_snapshots;
            snapshots_metrics.forEach((sm) => metrics.push(sm));
        }
        /* Order Metrics */
        if (metrics.length > 0)
            metrics.sort(NtopUtils$1.sortAlphabetically);

        return metrics;
    }

    async function get_snapshots_metrics() {
        if (!props.enable_snapshots) { return; }
        let url = `${http_prefix}/lua/pro/rest/v2/get/filters/snapshots.lua?page=${page_snapshots}`;

        let snapshots_obj = await ntopng_utility$1.http_request(url);
        let snapshots = ntopng_utility$1.object_to_array(snapshots_obj);
        let metrics_snapshots = snapshots.map((s) => {
            return {
                ...s,
                is_snapshot: true,
                label: `${s.name}`,
                group: "Snapshots",
            };
        });
        return metrics_snapshots;
    }

    async function get_selected_timeseries_groups() {
        let metric = selected_metric.value;
        return get_timeseries_groups_from_metric(metric);
    }

    async function get_timeseries_groups_from_metric(metric) {
        let source_type = metricsManager.get_current_page_source_type();
        let source_array = await metricsManager.get_default_source_array(http_prefix, source_type);
        let ts_group = metricsManager.get_ts_group(source_type, source_array, metric);
        let timeseries_groups = [ts_group];
        return timeseries_groups;
    }

    const add_ts_group_from_source_value_dict = async (source_type_id, source_value_dict, metric_schema) => {
        let source_type = metricsManager.get_source_type_from_id(source_type_id);
        let source_array = await metricsManager.get_source_array_from_value_dict(http_prefix, source_type, source_value_dict);
        let metric = await metricsManager.get_metric_from_schema(http_prefix, source_type, source_array, metric_schema);
        let ts_group = metricsManager.get_ts_group(source_type, source_array, metric);
        add_ts_group(ts_group);
    };

    const add_metric_from_metric_schema = async (metric_schema, metric_query) => {
        let metric = metrics.value.find((m) => m.schema == metric_schema && m.query == metric_query);
        if (metric == null) {
            console.error(`metric = ${metric_schema}, query = ${metric_query} not found.`);
            return;
        }
        let timeseries_groups = await get_timeseries_groups_from_metric(metric);
        // modal_timeseries.value.set_timeseries_groups(last_timeseries_groups_loaded);
        // modal_timeseries.value.add_ts_group(timeseries_groups[0], true);
        add_ts_group(timeseries_groups[0]);
    };

    function add_ts_group(ts_group) {
        modal_timeseries.value.set_timeseries_groups(last_timeseries_groups_loaded);
        modal_timeseries.value.add_ts_group(ts_group, true);
    }

    async function select_metric(metric) {
        if (metric.is_snapshot == true) {
            let url_parameters = metric.filters;
            let timeseries_url_params = ntopng_url_manager$1.get_url_entry("timeseries_groups", url_parameters);
            let timeseries_groups = await metricsManager.get_timeseries_groups_from_url(http_prefix, timeseries_url_params);
            current_groups_options_mode.value = timeseriesUtils.getGroupOptionMode(ntopng_url_manager$1.get_url_entry("timeseries_groups_mode", url_parameters));
            await load_page_stats_data(timeseries_groups, true, false);
        } else {
            await load_selected_metric_page_stats_data();
            refresh_metrics(false);
        }
    }

    async function load_selected_metric_page_stats_data() {
        let timeseries_groups = await get_selected_timeseries_groups();
        await load_page_stats_data(timeseries_groups, true, false);
    }

    function epoch_change(new_epoch) {
        let push_custom_metric = selected_metric.value.label == custom_metric.label;
        load_page_stats_data(last_timeseries_groups_loaded, true, false, new_epoch.refresh_data);
        refresh_top_table();
        refresh_metrics(push_custom_metric, true);
    }

    function chart_reloaded(chart_options) {
    }

    function show_modal_snapshot() {
        modal_snapshot.value.show();
    }

    function show_manage_timeseries() {
        if (last_timeseries_groups_loaded == null) { return; }
        modal_timeseries.value.show(last_timeseries_groups_loaded);
    }
    /**
     * Function called by Chart component to draw or update that return chart options.
     **/
    function get_f_get_custom_chart_options(chart_index) {
        return async (url) => {
            return charts_options_items.value[chart_index].chart_options;
        }
    }

    let cache_snapshots = null;
    function refresh_snapshots() {
        let push_custom_metric = selected_metric.value.label == custom_metric.label;
        refresh_metrics(push_custom_metric, true);
    }

    async function refresh_metrics(push_custom_metric, force_refresh) {
        metrics.value = await get_metrics(push_custom_metric, force_refresh);
        if (push_custom_metric) {
            selected_metric.value = custom_metric;
        }
    }

    async function apply_modal_timeseries(timeseries_groups) {
        refresh_metrics(true);
        await load_page_stats_data(timeseries_groups, true, true);
    }

    function change_groups_options_mode() {
        load_page_stats_data(last_timeseries_groups_loaded, false, false);
    }

    let ts_charts_options;
    /* This function load the chart data and options, doing the request and then setting the options */
    async function load_page_stats_data(timeseries_groups, reload_charts_data, reload_top_table_options, refreshed_time_interval) {
        /* Get the information necessary for the request, like epoch ecc. */
        loading.value = true;
        const status = retrieveGlobalStatus();
        let ts_compare = get_ts_compare(status);
        if (reload_charts_data) {
            /* Do the request to the backend; the answer is formatted as
             *  [  
             *      {   
             *          metadata: { ... }       // Containing various info regarding the series returned
             *          series: { ... }         // Containing the series with the data, labels and statistics
             *      }
             *  ]
             */
            if (timeseries_groups == null) {
                timeseries_groups = [];
                console.warn("Empty timeseries_groups request");
                return;
            }
            ts_charts_options = await timeseriesUtils.getTsChartsOptions(http_prefix, status, ts_compare, timeseries_groups, props.is_ntop_pro);
        }

        /* Update timeseries label to display */
        set_timeseries_groups_source_label(timeseries_groups, ts_charts_options);

        /* Format the options for the timeseries library */
        let charts_options = timeseriesUtils.tsArrayToOptionsArray(ts_charts_options, timeseries_groups, current_groups_options_mode.value, ts_compare);
        if (refreshed_time_interval) {
            update_charts(charts_options);
        } else {
            set_charts_options_items(charts_options);
        }
        set_stats_rows(ts_charts_options, timeseries_groups);
        if (reload_top_table_options) {
            set_top_table_options(timeseries_groups, status);
        }
        // set last_timeseries_groupd_loaded
        last_timeseries_groups_loaded = timeseries_groups;
        // update url params
        update_url_params();
        loading.value = false;
    }

    /* This function returns set the label of the timeseries; if available it should be
     * found in response.metadata.label field
     */
    function set_timeseries_groups_source_label(timeseries_groups, ts_charts_options) {
        timeseries_groups.forEach((ts_group, i) => {
            let ts_options = ts_charts_options[i];
            let label = ts_options?.metadata?.label;
            if (label != null) {
                let source_index = timeseriesUtils.getMainSourceDefIndex(ts_group);
                let source = ts_group.source_array[source_index];
                source.label = label;
            }
        });
    }

    function update_url_params() {
        ntopng_url_manager$1.set_key_to_url("timeseries_groups_mode", current_groups_options_mode.value.value);
        metricsManager.set_timeseries_groups_in_url(last_timeseries_groups_loaded);
        const ts_query = timeseriesUtils.getTsQuery(last_timeseries_groups_loaded[0]);
        const ts_schema = last_timeseries_groups_loaded[0]?.metric?.schema;
        if (ts_query) {
            ntopng_url_manager$1.set_key_to_url("ts_query", ts_query);
        }
        if (ts_schema) {
            ntopng_url_manager$1.set_key_to_url("ts_schema", ts_schema);
        }
    }

    function update_charts(charts_options) {
        charts_options.forEach((options, i) => {
            // charts.value[i].update_chart_options({ yaxis: options.yaxis });
            charts.value[i].update_chart_series(options?.serie);
        });
    }

    function set_charts_options_items(charts_options) {
        charts_options_items.value = charts_options.map((options, i) => {
            return {
                key: ntopng_utility$1.get_random_string(),
                chart_options: options,
            };
        });
    }

    function get_ts_compare(status) {
        // 5m, 30m, 1h, 1d, 1w, 1M, 1Y
        let r = Number.parseInt((status.epoch_end - status.epoch_begin) / 60);
        if (r <= 5) {
            return "5m";
        } else if (r <= 10) {
            return "10m";
        } else if (r <= 30) {
            return "30m";
        } else if (r <= 60) {
            return "1h";
        } else if (r <= 60 * 2) {
            return "2h";
        } else if (r <= 60 * 6) {
            return "6h";
        } else if (r <= 60 * 12) {
            return "12h";
        } else if (r <= 60 * 24) {
            return "1d";
        } else if (r <= 60 * 24 * 7) {
            return "1w";
        } else if (r <= 60 * 24 * 31) {
            return "1M";
        } else {
            return "1Y";
        }
    }

    function get_top_table_url(ts_group, table_value, table_view, table_source_def_value_dict, status) {
        if (!status) {
            status = retrieveGlobalStatus();
        }
        let ts_query = timeseriesUtils.getTsQuery(ts_group, true, table_source_def_value_dict);
        let v = table_value;
        let data_url = `${http_prefix}/lua/pro/rest/v2/get/${v}/top/ts_stats.lua`;
        //todo: get ts_query
        let p_obj = {
            zoom: '5m',
            ts_query,
            // ts_query: `ifid:${ntopng_url_manager.get_url_entry('ifid')}`,
            epoch_begin: `${status.epoch_begin}`,
            epoch_end: `${status.epoch_end}`,
            detail_view: `${table_view}`,
            new_charts: `true`
        };

        let p_url_request = ntopng_url_manager$1.add_obj_to_url(p_obj, '');
        return `${data_url}?${p_url_request}`;
    }

    async function refresh_top_table() {
        if (!props.is_ntop_pro) { return; }
        let table_config = selected_top_table.value?.table_config_def;
        if (table_config == null) { return; }
        // NtopUtils.showOverlays();
        let data_url = get_top_table_url(table_config.ts_group, table_config.table_def.table_value, table_config.table_def.view, table_config.table_source_def_value_dict);
        top_table_ref.value.update_url(data_url);
        top_table_ref.value.reload();
        // NtopUtils.hideOverlays();
    }

    const top_table_options = ref([]);
    const selected_top_table = ref({});
    function set_top_table_options(timeseries_groups, status) {
        if (!props.is_ntop_pro) { return; }
        if (timeseries_groups == null) {
            timeseries_groups = last_timeseries_groups_loaded;
        }
        if (!status) {
            status = retrieveGlobalStatus();
        }

        let sources_types_tables = metricsManager.sources_types_tables;
        let ts_group_dict = {}; // dictionary with 1 ts_group for each (source_type, source_array)
        timeseries_groups.forEach((ts_group) => {
            ts_group.source_type;
            // let source_type_tables = sources_types_tables[source_type.id];
            // let table_source_def_value_dict = source_type_tables.table_source_def_value_dict

            let id = metricsManager.get_ts_group_id(ts_group.source_type, ts_group.source_array);
            ts_group_dict[id] = ts_group;
        });
        let top_table_id_dict = {};
        top_table_options.value = [];
        for (let id in ts_group_dict) {
            let ts_group = ts_group_dict[id];
            let main_source_index = timeseriesUtils.getMainSourceDefIndex(ts_group);
            let main_source = ts_group.source_array[main_source_index];
            let source_type = ts_group.source_type;
            let source_type_tables = sources_types_tables[source_type.id];
            if (source_type_tables == null) { continue; }

            source_type_tables.forEach((table_def) => {
                let enables_table_value = props.sources_types_top_enabled[table_def.table_value];
                if (enables_table_value == null) { return; }
                let enable_table_def = enables_table_value[table_def.view];
                if (!enable_table_def) { return; }
                let table_source_def_value_dict = table_def.table_source_def_value_dict;

                let data_url = get_top_table_url(ts_group, table_def.table_value, table_def.view, table_source_def_value_dict, status);
                let table_id = metricsManager.get_ts_group_id(ts_group.source_type, ts_group.source_array, null, table_source_def_value_dict, true);
                table_id = `${table_id}_${table_def.view}`;
                if (top_table_id_dict[table_id] != null) { return; }
                top_table_id_dict[table_id] = true;

                let value = `${table_def.table_value}_${table_def.view}_${table_id}`;
                let label;
                if (table_def.f_get_label == null) {
                    label = `${table_def.title} - ${source_type.label} ${main_source.label}`;
                } else {
                    label = table_def.f_get_label(ts_group);
                }
                const table_config_def = {
                    ts_group,
                    table_def,
                    // table_value: table_def.table_value,
                    // table_view: table_def.view,

                    table_buttons: [],
                    data_url,
                    enable_search: true,
                    table_config: {
                        serverSide: false,
                        order: [[table_def.default_sorting_columns, 'desc']],
                        columnDefs: table_def.columnDefs || [],
                    }
                };
                // it should be here in this instance the vuetify object with its properties
                table_config_def.columns_config = table_def.columns.map((column) => {
                    let render_if_context = {
                        is_history_enabled: props.is_history_enabled
                    };
                    let c = {
                        visible: !column.render_if || column.render_if(render_if_context),
                        ...column,
                    };
                    if (c.className == null) { c.className = "text-nowrap"; }
                    if (c.responsivePriority == null) { c.responsivePriority = 1; }
                    c.render = column.render.bind({
                        add_metric_from_metric_schema,
                        add_ts_group_from_source_value_dict,
                        sources_types_enabled: props.sources_types_enabled,
                        status, source_type, source_array: ts_group.source_array,
                    });
                    return c;
                });
                let option = { value, label, table_config_def };
                top_table_options.value.push(option);
            });
        }
        if (selected_top_table.value != null && top_table_options.value.find((option) => option.value == selected_top_table.value.value)) {
            return;
        }

        selected_top_table.value = top_table_options.value.find((option) => option.table_config_def.default == true);
        if (selected_top_table.value == null) {
            selected_top_table.value = top_table_options.value[0];
        }
    }

    let stats_columns = [
        { id: "metric", label: _i18n("page_stats.metric"), va: true },
        { id: "avg", label: _i18n("page_stats.average"), class: "text-end", va: true },
        { id: "perc_95", label: _i18n("page_stats.95_perc"), class: "text-end", va: true },
        { id: "max", label: _i18n("page_stats.max"), class: "text-end", va: true },
        { id: "min", label: _i18n("page_stats.min"), class: "text-end", va: true },
        { id: "total", label: _i18n("page_stats.total"), class: "text-end", va: false },
    ];

    const stats_rows = ref([]);

    function set_stats_rows(ts_charts_options, timeseries_groups) {
        const extend_serie_name = ts_charts_options.length > 1;
        enable_stats_table.value = timeseries_groups.map((ts_group) => !ts_group.source_type.disable_stats).reduce((res, el) => res | el, false);
        if (!enable_stats_table.value) { return; }
        const f_get_total_formatter_type = (type) => {
            let map_type = {
                "bps": "bytes",
                "fps": "flows",
                "alertps": "alerts",
                "hitss": "hits",
                "pps": "packets",
            };
            if (map_type[type] != null) {
                return map_type[type];
            }
            return type;
        };
        stats_rows.value = [];
        ts_charts_options.forEach((options, i) => {
            let ts_group = timeseries_groups[i];
            if (ts_group.source_type.disable_stats == true) { return; }
            options.series?.forEach((s, j) => {
                let ts_id = timeseriesUtils.getSerieId(s);
                let s_metadata = ts_group.metric.timeseries[ts_id];
                let formatter = formatterUtils.getFormatter(ts_group.metric.measure_unit);
                let ts_stats;
                let name = s_metadata.label;
                if (s_metadata.hidden) {
                    /* Skip in case it's requested to hide the Timeserie */
                    return;
                }
                if (s_metadata.use_serie_name == true) {
                    name = s.name;
                }
                if (s?.data.length > j) {
                    ts_stats = s.statistics;
                }
                if (ts_stats == null) {
                    return;
                }
                if (s.ext_label) {
                    name = s.ext_label;
                }
                if (s.label) {
                    name = s.label;
                }
                name = timeseriesUtils.getSerieName(name, ts_id, ts_group, extend_serie_name);
                let total_formatter_type = f_get_total_formatter_type(ts_group.metric.measure_unit);
                let total_formatter = formatterUtils.getFormatter(total_formatter_type);
                let row = {
                    metric: name,
                    total: total_formatter(ts_stats.total),
                    perc_95: formatter(ts_stats["95th_percentile"]),
                    avg: formatter(ts_stats.average),
                    max: formatter(ts_stats.max_val),
                    min: formatter(ts_stats.min_val),
                };
                stats_rows.value.push(row);
            });
        });
    }

    function print_stats_column(col) {
        return col.label;
    }

    function print_stats_row(col, row) {
        let label = row[col.id];
        const historical_filter = props.source_value_object.historical_link_to_table;
        if (historical_filter && historical_filter.add_historical_link_to_table && historical_filter.historical_link_col_id == col.id) {
            const element = historical_filter.historical_filters.find((el) => { return el.table_label === label });
            if (element) {
                let params = create_historical_params();
                params[element.historical_filter] = params[historical_filter.historical_filter_to_change];
                delete params[historical_filter.historical_filter_to_change];
                params["aggregated"] = false;
                params["query_preset"] = "";
                params["count"] = "THROUGHPUT";
                if (!params["ifid"]) {
                    params["ifid"] = historical_filter.historical_interface_id;
                }
                let url_params = ntopng_url_manager$1.obj_to_url_params(params);
                label = `<a href="${http_prefix}/lua/pro/db_search.lua?${url_params}">${label}</a>`;
            }
        }
        return label;
    }

    function create_historical_params() {
        let status = retrieveGlobalStatus();
        let params = { epoch_begin: status.epoch_begin, epoch_end: status.epoch_end };
        /* Add the source elements to the redirect, like host, snmp, ecc. */
        if (last_timeseries_groups_loaded && last_timeseries_groups_loaded.length > 0) {
            const source_array = last_timeseries_groups_loaded[0].source_array;
            const source_def = last_timeseries_groups_loaded[0].source_type.source_def_array;
            /* Use the first element */
            if (source_array) {
                let probe = '';
                source_array.forEach((elem, i) => {
                    if (!dataUtils.isEmptyOrNull(elem.label) && source_def[i]) {
                        const value = source_def[i].value;
                        switch (value) {
                            case 'device':
                                probe = elem.value;
                                params["probe_ip"] = `${elem.value};eq`;
                                break;
                            case 'port':
                            case 'if_index':
                                params["snmp_interface"] = `${probe}_${elem.value};eq`;
                                break;
                            case 'host':
                                params["ip"] = `${elem.value};eq`;
                                break;
                            case 'mac':
                                params["mac"] = `${elem.value};eq`;
                                break;
                            case 'subnet':
                                params["network"] = `${elem.value};eq`;
                                break;
                            case 'asn':
                                params["asn"] = `${elem.value};eq`;
                                break;
                            case 'country':
                                params["country"] = `${elem.value};eq`;
                                break;
                            case 'vlan':
                                params["vlan_id"] = `${elem.value};eq`;
                                break;
                            case 'pool':
                                params["host_pool_id"] = `${elem.value};eq`;
                                break;
                        }
                    }
                });
            }
        }

        return params
    }

    function jump_to_historical_flows() {
        const params = create_historical_params();
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        const historical_url = `${http_prefix}/lua/pro/db_search.lua?${url_params}`;
        ntopng_url_manager$1.go_to_url(historical_url);
    }

    const modal_traffic_extraction = ref(null);
    function show_modal_traffic_extraction() {
        modal_traffic_extraction.value.show();
    }

    function show_modal_download_file() {
        if (!ts_charts_options?.length) { return; }
        let ts_group = last_timeseries_groups_loaded[0];
        let filename = timeseriesUtils.getSerieName(null, null, ts_group);
        modal_download_file.value.show(filename);
    }

    async function download_chart_png(filename) {
        let chart_image_array_promise = charts.value.map(async (chart) => {
            let canvas = new Image();
            chart.get_image(canvas);
            return new Promise(async (resolve, reject) => {
                canvas.onload = function () {
                    resolve(canvas);
                };
            });
        });
        let height = 0;
        let chart_image_array = await Promise.all(chart_image_array_promise);
        chart_image_array.forEach((image) => {
            height += image.height;
        });
        let canvas = document.createElement('canvas');
        let canvas_context = canvas.getContext('2d');
        canvas.width = chart_image_array[0].width;
        canvas.height = height;
        height = 0;
        chart_image_array.forEach((image) => {
            canvas_context.drawImage(image, 0, height, image.width, image.height);
            height += image.height;
        });
        ntopng_utility$1.download_URI(canvas.toDataURL(), filename);
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$1$, [
          createVNode(script$2J),
          createBaseVNode("div", _hoisted_2$1O, [
            createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
            createVNode(script$2G, {
              style: {"margin-top":"0.5rem"},
              class: "ms-1",
              id: unref(id_date_time_picker),
              enable_refresh: true,
              ref_key: "date_time_picker",
              ref: date_time_picker,
              onEpoch_change: epoch_change,
              min_time_interval_id: min_time_interval_id.value,
              custom_time_interval_list: time_preset_list
            }, {
              begin: withCtx(() => [...(_cache[3] || (_cache[3] = []))]),
              extra_buttons: withCtx(() => [
                (__props.enable_snapshots)
                  ? (openBlock(), createElementBlock("button", {
                      key: 0,
                      class: "btn btn-link btn-sm",
                      onClick: show_modal_snapshot,
                      title: _i18n('page_stats.manage_snapshots_btn')
                    }, [...(_cache[4] || (_cache[4] = [
                      createBaseVNode("i", { class: "fas fa-lg fa-camera-retro" }, null, -1 /* CACHED */)
                    ]))], 8 /* PROPS */, _hoisted_3$1D))
                  : createCommentVNode("v-if", true),
                (__props.traffic_extraction_permitted)
                  ? (openBlock(), createElementBlock("button", {
                      key: 1,
                      class: "btn btn-link btn-sm",
                      onClick: show_modal_traffic_extraction,
                      title: _i18n('traffic_recording.pcap_download')
                    }, [...(_cache[5] || (_cache[5] = [
                      createBaseVNode("i", { class: "fas fa-lg fa-download" }, null, -1 /* CACHED */)
                    ]))], 8 /* PROPS */, _hoisted_4$1A))
                  : createCommentVNode("v-if", true),
                createBaseVNode("button", {
                  disabled: unref(is_safari),
                  class: "btn btn-link btn-sm",
                  onClick: show_modal_download_file,
                  title: unref(image_button_title)
                }, [...(_cache[6] || (_cache[6] = [
                  createBaseVNode("i", { class: "fas fa-lg fa-file-image" }, null, -1 /* CACHED */)
                ]))], 8 /* PROPS */, _hoisted_5$1k),
                (__props.is_history_enabled)
                  ? (openBlock(), createElementBlock("button", {
                      key: 2,
                      class: "btn btn-link btn-sm",
                      onClick: jump_to_historical_flows,
                      title: _i18n('page_stats.historical_flows')
                    }, [...(_cache[7] || (_cache[7] = [
                      createBaseVNode("i", { class: "fas fa-search-plus" }, null, -1 /* CACHED */)
                    ]))], 8 /* PROPS */, _hoisted_6$16))
                  : createCommentVNode("v-if", true)
              ]),
              _: 1 /* STABLE */
            }, 8 /* PROPS */, ["id", "min_time_interval_id"]),
            createCommentVNode(" select metric "),
            withDirectives(createBaseVNode("div", _hoisted_7$$, [
              createBaseVNode("div", _hoisted_8$R, [
                createVNode(script$2H, {
                  selected_option: selected_metric.value,
                  "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_metric).value = $event)),
                  options: metrics.value,
                  onSelect_option: select_metric
                }, null, 8 /* PROPS */, ["selected_option", "options"])
              ]),
              createBaseVNode("div", _hoisted_9$G, [
                createVNode(script$2H, {
                  selected_option: current_groups_options_mode.value,
                  "onUpdate:selected_option": _cache[1] || (_cache[1] = $event => ((current_groups_options_mode).value = $event)),
                  options: unref(groups_options_modes),
                  onSelect_option: change_groups_options_mode
                }, null, 8 /* PROPS */, ["selected_option", "options"])
              ]),
              (__props.is_ntop_pro)
                ? (openBlock(), createElementBlock("button", {
                    key: 0,
                    type: "button",
                    onClick: show_manage_timeseries,
                    class: "btn btn-sm btn-primary inline",
                    style: {"vertical-align":"super"}
                  }, " Manage Timeseries "))
                : createCommentVNode("v-if", true)
            ], 512 /* NEED_PATCH */), [
              [vShow, ts_menu_ready.value]
            ]),
            createVNode(TransitionGroup, {
              name: "list",
              mode: "out-in"
            }, {
              default: withCtx(() => [
                (openBlock(true), createElementBlock(Fragment, null, renderList(charts_options_items.value, (item, i) => {
                  return (openBlock(), createElementBlock("div", {
                    key: item.key
                  }, [
                    createVNode(script$2i, {
                      id: unref(id_chart) + i,
                      ref_for: true,
                      ref: el => { charts.value[i] = el; },
                      chart_type: unref(chart_type),
                      register_on_status_change: false,
                      get_custom_chart_options: get_f_get_custom_chart_options(i),
                      onZoom: epoch_change,
                      onChart_reloaded: chart_reloaded
                    }, null, 8 /* PROPS */, ["id", "chart_type", "get_custom_chart_options"])
                  ]))
                }), 128 /* KEYED_FRAGMENT */))
              ]),
              _: 1 /* STABLE */
            })
          ]),
          createBaseVNode("div", _hoisted_10$B, [
            createVNode(Transition, {
              name: "list",
              mode: "out-in"
            }, {
              default: withCtx(() => [
                (enable_stats_table.value)
                  ? (openBlock(), createElementBlock("div", _hoisted_11$y, [
                      createVNode(script$2d, {
                        id: "page_stats_bootstrap_table",
                        columns: unref(stats_columns),
                        rows: stats_rows.value,
                        print_html_column: (col) => print_stats_column(col),
                        print_html_row: (col, row) => print_stats_row(col, row)
                      }, null, 8 /* PROPS */, ["columns", "rows", "print_html_column", "print_html_row"])
                    ]))
                  : createCommentVNode("v-if", true)
              ]),
              _: 1 /* STABLE */
            })
          ]),
          (__props.is_ntop_pro)
            ? (openBlock(), createElementBlock("div", _hoisted_12$t, [
                createVNode(Transition, {
                  name: "list",
                  mode: "out-in"
                }, {
                  default: withCtx(() => [
                    (selected_top_table.value?.table_config_def)
                      ? (openBlock(), createElementBlock("div", _hoisted_13$n, [
                          createBaseVNode("div", _hoisted_14$l, [
                            createVNode(script$2H, {
                              selected_option: selected_top_table.value,
                              "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((selected_top_table).value = $event)),
                              options: top_table_options.value
                            }, null, 8 /* PROPS */, ["selected_option", "options"])
                          ]),
                          (openBlock(), createBlock(script$2e, {
                            key: selected_top_table.value?.value,
                            ref_key: "top_table_ref",
                            ref: top_table_ref,
                            table_buttons: selected_top_table.value.table_config_def.table_button,
                            columns_config: selected_top_table.value.table_config_def.columns_config,
                            data_url: selected_top_table.value.table_config_def.data_url,
                            enable_search: selected_top_table.value.table_config_def.enable_search,
                            table_config: selected_top_table.value.table_config_def.table_config
                          }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "table_config"]))
                        ]))
                      : createCommentVNode("v-if", true)
                  ]),
                  _: 1 /* STABLE */
                })
              ]))
            : createCommentVNode("v-if", true)
        ]),
        (__props.enable_snapshots)
          ? (openBlock(), createBlock(script$2s, {
              key: 0,
              ref_key: "modal_snapshot",
              ref: modal_snapshot,
              csrf: __props.csrf,
              page: page_snapshots,
              onAdded_snapshot: refresh_snapshots,
              onDeleted_snapshots: refresh_snapshots,
              onDeleted_all_snapshots: refresh_snapshots
            }, null, 8 /* PROPS */, ["csrf"]))
          : createCommentVNode("v-if", true),
        (__props.is_ntop_pro)
          ? (openBlock(), createBlock(script$2g, {
              key: 1,
              ref_key: "modal_timeseries",
              ref: modal_timeseries,
              sources_types_enabled: __props.sources_types_enabled,
              onApply: apply_modal_timeseries
            }, null, 8 /* PROPS */, ["sources_types_enabled"]))
          : createCommentVNode("v-if", true),
        createVNode(script$2t, {
          id: "page_stats_modal_traffic_extraction",
          ref_key: "modal_traffic_extraction",
          ref: modal_traffic_extraction
        }, null, 512 /* NEED_PATCH */),
        createVNode(script$2f, {
          ref_key: "modal_download_file",
          ref: modal_download_file,
          title: _i18n('page_stats.title_modal_download_file'),
          ext: "png",
          onDownload: download_chart_png
        }, null, 8 /* PROPS */, ["title"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    var css_248z$p = "\n.inline[data-v-937058e4] {\n    display: inline-block;\n}\n.select2-size[data-v-937058e4] {\n    min-width: 18rem;\n}\n.list-move[data-v-937058e4],\n\n.list-enter-active[data-v-937058e4],\n.list-leave-active[data-v-937058e4] {\n    transition: all 0.35s ease;\n}\n.list-enter-from[data-v-937058e4] {\n    opacity: 0;\n    transform: translateX(-60px);\n    /* entra da sinistra */\n}\n.list-leave-to[data-v-937058e4] {\n    opacity: 0;\n    transform: translateX(0);\n    /* esce verso destra */\n}\n\n/* ensure leaving items are taken out of layout flow so that moving\n   animations can be calculated correctly. */\n.list-leave-active[data-v-937058e4] {\n    position: absolute;\n}\n";
    styleInject(css_248z$p);

    script$2c.__scopeId = "data-v-937058e4";
    script$2c.__file = "http_src/vue/page-stats.vue";

    var script$2b = defineComponent({
      components: {
      },
      props: {
        id: String,
        tab_list: Array,
      },
      emits: ["click_item"],
      /** This method is the first method of the component called, it's called before html template creation. */
      created() {
      },
      data() {
        return {
          old_tab: null
        };
      },
      /** This method is the first method called after html template creation. */
      mounted() {
        let tmp = null;
        this.$props.tab_list.forEach(function(tab) {
          if(tab.active) {
            tmp = tab;
          }
        });
        this.old_tab = tmp || this.$props.tab_list[0];

        ntopng_sync.ready(this.$props["id"]);
      },
      methods: {
        change_tab: function(tab) {
          if((tab.id != null && this.old_tab.id != tab.id) ||
             (tab.name != null && this.old_tab.name != tab.name)) {
            this.old_tab = tab;
            this.$emit('click_item', tab);
          }
        }
      },
    });

    const _hoisted_1$1_ = { class: "card-header mb-2" };
    const _hoisted_2$1N = {
      class: "nav nav-tabs card-header-tabs",
      role: "tablist"
    };
    const _hoisted_3$1C = ["onClick"];
    const _hoisted_4$1z = ["onClick"];

    function render$b(_ctx, _cache, $props, $setup, $data, $options) {
      return (openBlock(), createElementBlock("div", _hoisted_1$1_, [
        createBaseVNode("ul", _hoisted_2$1N, [
          (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.tab_list, (tab) => {
            return (openBlock(), createElementBlock(Fragment, null, [
              (tab.active)
                ? (openBlock(), createElementBlock("li", {
                    key: 0,
                    onClick: $event => (_ctx.change_tab(tab)),
                    class: normalizeClass([{ 'active': tab.active }, "nav-item nav-link"])
                  }, toDisplayString(tab.title), 11 /* TEXT, CLASS, PROPS */, _hoisted_3$1C))
                : (openBlock(), createElementBlock("a", {
                    key: 1,
                    onClick: $event => (_ctx.change_tab(tab)),
                    href: "#",
                    class: "nav-item nav-link"
                  }, toDisplayString(tab.title), 9 /* TEXT, PROPS */, _hoisted_4$1z))
            ], 64 /* STABLE_FRAGMENT */))
          }), 256 /* UNKEYED_FRAGMENT */))
        ])
      ]))
    }

    script$2b.render = render$b;
    script$2b.__file = "http_src/vue/tab-list.vue";

    var script$2a = {
      __name: 'modal-delete-confirm',
      props: {
        body: String,
        title: String,
    },
      emits: ['delete','delete_all'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;

    const showed = () => {};
    let body_delete = ref("");
    let title_delete = ref("");
    const props = __props;
    const show = (body, title) => {

      if (body != null && title != null) {

        body_delete.value = body;
        title_delete.value = title;
      } else {

        body_delete.value = props.body;
        title_delete.value = props.title;
      }
      modal_id.value.show();
    };

    const delete_ = () => {
        emit('delete');

        close();
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[0] || (_cache[0] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title_delete)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(unref(body_delete)), 1 /* TEXT */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: delete_,
            class: "btn btn-danger"
          }, toDisplayString(_i18n('delete')), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$2a.__file = "http_src/vue/modal-delete-confirm.vue";

    const change_asset_table_tab_event = "change_asset_table_tab_event";

    var script$29 = {
        components: {
            'datatable': script$2e,
            'modal-delete-confirm': script$2a,
            'tab-list': script$2b,
            NoteList: script$2u
        },
        props: {
            page_csrf: String,
            url_params: Object,
            view: String,
            table_filters: Array,
        },
        /**
         * First method called when the component is created.
         */
        created() {
            ntopng_url_manager$1.set_key_to_url('asset_family', this.$props.url_params.asset_family);
            this.asset_table_tab = ntopng_url_manager$1.get_url_entry('view') || 'centrality';
            ntopng_url_manager$1.set_key_to_url('view', this.service_table_tab);
            this.tab_list.forEach((i) => {
                if (this.asset_table_tab == i.id)
                    i.active = true;
            });
            start_datatable$5(this);
        },
        mounted() {
            ntopng_events_manager$1.on_custom_event("page_asset_table", ntopng_custom_events.DATATABLE_LOADED, () => {
                if (ntopng_url_manager$1.get_url_entry('host'))
                    this.hide_dropdowns();
            });
            ntopng_events_manager$1.on_custom_event("change_asset_table_tab", change_asset_table_tab_event, (tab) => {
                let table = this.get_active_table();
                ntopng_url_manager$1.set_key_to_url('view', tab);
                table.destroy_table();
                this.asset_table_tab = tab;
            });

            $("#btn-delete-all").click(() => this.show_delete_all_dialog());
        },
        data() {
            return {
                i18n: (t) => i18n(t),
                config_devices_standard: null,
                config_devices_centrality: null,
                title_delete: i18n('map_page.delete_assets'),
                body_delete: i18n('map_page.delete_assets_message'),
                title_download: i18n('map_page.download'),
                body_download: i18n('map_page.download_message'),
                get_url: null,
                asset_table_tab: null,
                notes: [i18n('map_page.table_note_asset_map')],
                tab_list: [
                    {
                        title: i18n('map_page.standard_view'),
                        active: (view == 'standard'),
                        id: 'standard'
                    },
                    {
                        title: i18n('map_page.centrality_view'),
                        active: (view == 'centrality'),
                        id: 'centrality'
                    },
                ]
            };
        },
        methods: {
            destroy: function () {
                let table = this.get_active_table();
                table.destroy_table();
            },
            /* Method used to switch active table tab */
            click_item: function (item) {
                this.tab_list.forEach((i) => i.active = false);
                item.active = true;
                ntopng_events_manager$1.emit_custom_event(change_asset_table_tab_event, item.id);
            },
            delete_all: async function () {
                let url = `${http_prefix}/lua/pro/enterprise/network_maps.lua`;
                let params = {
                    ifid: this.url_params.ifid,
                    action: 'reset',
                    page: this.url_params.page,
                    csrf: this.$props.page_csrf,
                    map: this.url_params.map
                };
                try {
                    let headers = {
                        'Content-Type': 'application/json'
                    };
                    await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
                    this.reload_table();
                } catch (err) {
                    this.reload_table();
                }
            },
            create_action_button_historical_flow_link: function (_, type, rowData) {
                let historical_flow_link = {
                    handlerId: "historical_flow_link",
                    onClick: () => {
                        historical_flow$2(rowData);
                    },
                };

                return DataTableUtils.createActionButtons([
                    { class: `pointer`, handler: historical_flow_link, icon: 'fas fa-stream', title: i18n('db_explorer.historical_data') },
                ]);

            },
            hide_dropdowns: function () {
                $(`#network_dropdown`).removeClass('d-inline');
                $(`#vlan_id_dropdown`).removeClass('d-inline');
                $(`#network_dropdown`).attr('hidden', 'hidden');
                $(`#vlan_id_dropdown`).attr('hidden', 'hidden');
            },
            reload_table: function () {
                let table = this.get_active_table();
                NtopUtils.showOverlays();
                table.reload();
                NtopUtils.hideOverlays();
            },
            get_active_table: function () {
                return this.$refs[`table_asset_${this.asset_table_tab}`];
            },
            show_delete_all_dialog: function () {
                this.$refs["modal_delete_all"].show();
            },
        },
    };

    function historical_flow$2(row) {
        const client_ip = row.client.split("host=")[1].split("&")[0];
        const server_ip = row.server.split("host=")[1].split("&")[0];
        const epoch_end = row.epoch_end;
        const epoch_begin = row.epoch_begin;

        const params = {
            epoch_begin: epoch_begin,
            epoch_end: epoch_end,
            srv_ip: `${server_ip};eq`,
            cli_ip: `${client_ip};eq`,
        };
        const url_params = ntopng_url_manager$1.obj_to_url_params(params);
        ntopng_url_manager$1.go_to_url(`${http_prefix}/lua/pro/db_search.lua?${url_params}`);

    }
    function start_datatable$5(DatatableVue) {
        const datatableButton = [];
        let columns = [];

        DatatableVue.get_url = NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, url_params);

        /* Manage the buttons close to the search box */
        datatableButton.push({
            text: '<i class="fas fa-sync"></i>',
            className: 'btn-link',
            action: function (e, dt, node, config) {
                DatatableVue.reload_table();
            }
        });

        let tmp_params = url_params;
        tmp_params['view'] = 'standard';

        let defaultDatatableConfig = {
            table_buttons: datatableButton,
            columns_config: [],
            data_url: NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, tmp_params),
            enable_search: true,
        };

        let table_filters = [];
        for (let filter of (DatatableVue.$props.table_filters || [])) {
            filter.callbackFunction = (table, value) => {
                tmp_params['view'] = DatatableVue.asset_table_tab;
                tmp_params[filter.filterMenuKey] = value.id;
                ntopng_url_manager$1.set_key_to_url(filter.filterMenuKey, value.id);
                table.ajax.url(NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, tmp_params));
                NtopUtils.showOverlays();
                table.ajax.reload();
                NtopUtils.hideOverlays();
            },
                table_filters.push(filter);
        }

        /* Standard table configuration */

        columns = [
            {
                columnName: i18n("actions"), name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, render: function (_, type, rowData) {
                    return DatatableVue.create_action_button_historical_flow_link(_, type, rowData);
                }
            }, { columnName: i18n("map_page.client"), name: 'client', data: 'client', className: 'text-nowrap', responsivePriority: 1 },
            { columnName: i18n("map_page.server"), name: 'server', data: 'server', className: 'text-nowrap', responsivePriority: 1 },
            { columnName: i18n("map_page.asset_family"), name: 'family', data: 'family', className: 'text-nowrap', responsivePriority: 2 },
            { columnName: i18n("map_page.last_seen"), name: 'last_seen', data: 'last_seen', className: 'text-center', responsivePriority: 2 },
        ];

        let configDevices = ntopng_utility.clone(defaultDatatableConfig);
        configDevices.table_config = { serverSide: false, order: [[4 /* Last Seen */, 'desc']] };
        configDevices.columns_config = columns;
        configDevices.table_filters = table_filters;
        DatatableVue.config_devices_standard = configDevices;


        /* Centrality table configuration */

        tmp_params['view'] = 'centrality';
        defaultDatatableConfig.data_url = NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, tmp_params);

        columns = [
            { columnName: i18n("map_page.host"), name: 'host', data: 'host', className: 'text-nowrap', render: (data, type) => { return data.label }, responsivePriority: 1 },
            { columnName: i18n("map_page.asset_total_edges"), name: 'total_edges', data: 'total_edges', className: 'text-nowrap', responsivePriority: 1 },
            { columnName: i18n("map_page.asset_in_edges"), name: 'in_edges', data: 'in_edges', className: 'text-nowrap', responsivePriority: 2 },
            { columnName: i18n("map_page.asset_out_edges"), name: 'out_edges', data: 'out_edges', className: 'text-center', responsivePriority: 2 },
        ];



        let centralityConfigDevices = ntopng_utility.clone(defaultDatatableConfig);
        centralityConfigDevices.table_config = { serverSide: false, order: [[1 /* Total Edges */, 'desc']] };
        centralityConfigDevices.columns_config = columns;
        centralityConfigDevices.table_filters = table_filters;

        console.log(configDevices);
        console.log(centralityConfigDevices);

        DatatableVue.config_devices_centrality = centralityConfigDevices;
    }

    const _hoisted_1$1Z = { class: "row" };
    const _hoisted_2$1M = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1B = { class: "card" };
    const _hoisted_4$1y = { class: "card-body" };
    const _hoisted_5$1j = { id: "table_asset" };
    const _hoisted_6$15 = { class: "card-footer" };
    const _hoisted_7$_ = {
      type: "button",
      id: "btn-delete-all",
      class: "btn btn-danger me-1"
    };
    const _hoisted_8$Q = ["href"];

    function render$a(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_modal_delete_confirm = resolveComponent("modal-delete-confirm");
      const _component_tab_list = resolveComponent("tab-list");
      const _component_datatable = resolveComponent("datatable");
      const _component_NoteList = resolveComponent("NoteList");

      return (openBlock(), createElementBlock("div", _hoisted_1$1Z, [
        createBaseVNode("div", _hoisted_2$1M, [
          _cache[5] || (_cache[5] = createBaseVNode("div", {
            class: "alert alert-danger d-none",
            id: "alert-row-buttons",
            role: "alert"
          }, null, -1 /* CACHED */)),
          createBaseVNode("div", _hoisted_3$1B, [
            _cache[4] || (_cache[4] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1y, [
              createBaseVNode("div", _hoisted_5$1j, [
                createVNode(_component_modal_delete_confirm, {
                  ref: "modal_delete_all",
                  title: $data.title_delete,
                  body: $data.body_delete,
                  onDelete: $options.delete_all
                }, null, 8 /* PROPS */, ["title", "body", "onDelete"]),
                createVNode(_component_tab_list, {
                  ref: "asset_tab_list",
                  id: "asset_tab_list",
                  tab_list: $data.tab_list,
                  onClick_item: $options.click_item
                }, null, 8 /* PROPS */, ["tab_list", "onClick_item"]),
                ($data.asset_table_tab == 'standard')
                  ? (openBlock(), createBlock(_component_datatable, {
                      key: 0,
                      ref: "table_asset_standard",
                      table_buttons: $data.config_devices_standard.table_buttons,
                      columns_config: $data.config_devices_standard.columns_config,
                      data_url: $data.config_devices_standard.data_url,
                      enable_search: $data.config_devices_standard.enable_search,
                      filter_buttons: $data.config_devices_standard.table_filters
                    }, {
                      default: withCtx(() => [...(_cache[0] || (_cache[0] = [
                        createTextVNode(" :table_config=\"config_devices_standard.table_config\"> ", -1 /* CACHED */)
                      ]))]),
                      _: 1 /* STABLE */
                    }, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "filter_buttons"]))
                  : createCommentVNode("v-if", true),
                ($data.asset_table_tab == 'centrality')
                  ? (openBlock(), createBlock(_component_datatable, {
                      key: 1,
                      ref: "table_asset_centrality",
                      table_buttons: $data.config_devices_centrality.table_buttons,
                      columns_config: $data.config_devices_centrality.columns_config,
                      data_url: $data.config_devices_centrality.data_url,
                      enable_search: $data.config_devices_centrality.enable_search,
                      filter_buttons: $data.config_devices_centrality.table_filters
                    }, {
                      default: withCtx(() => [...(_cache[1] || (_cache[1] = [
                        createTextVNode(" :table_config=\"config_devices_centrality.table_config\"> ", -1 /* CACHED */)
                      ]))]),
                      _: 1 /* STABLE */
                    }, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "filter_buttons"]))
                  : createCommentVNode("v-if", true)
              ])
            ]),
            createBaseVNode("div", _hoisted_6$15, [
              createBaseVNode("button", _hoisted_7$_, [
                _cache[2] || (_cache[2] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString($data.i18n("map_page.delete_assets")), 1 /* TEXT */)
              ]),
              createBaseVNode("a", {
                href: $data.get_url,
                class: "btn btn-primary",
                role: "button",
                "aria-disabled": "true",
                download: "asset_map.json",
                target: "_blank"
              }, [...(_cache[3] || (_cache[3] = [
                createBaseVNode("i", { class: "fas fa-download" }, null, -1 /* CACHED */)
              ]))], 8 /* PROPS */, _hoisted_8$Q)
            ])
          ]),
          createVNode(_component_NoteList, { note_list: $data.notes }, null, 8 /* PROPS */, ["note_list"])
        ])
      ]))
    }

    script$29.render = render$a;
    script$29.__file = "http_src/vue/page-asset-table.vue";

    var script$28 = {
      components: {	  
        'datatable': script$2e,
        'modal-delete-confirm': script$2a,
        NoteList: script$2u
      },
      props: {
        page_csrf: String,
        url_params: Object,
        view: String,
        table_filters: Array,
        is_admin: Boolean,
      },
      /**
       * First method called when the component is created.
       */
      created() {
        start_datatable$4(this);
      },
      mounted() {  
        $("#btn-delete-all").click(() => this.show_delete_all_dialog());
        
        ntopng_events_manager.on_custom_event("page_periodicity_table", ntopng_custom_events.DATATABLE_LOADED, () => {
          if(ntopng_url_manager$1.get_url_entry('host'))
            this.hide_dropdowns();
        });
      },    
      data() {
        return {
          i18n: (t) => i18n(t),
          base_url: `${http_prefix}/lua/pro/enterprise/get_map.lua`,
          config_devices_standard: null,
          config_devices_centrality: null,
          title_delete: i18n('map_page.delete_services'),
          body_delete: i18n('map_page.delete_services_message'),
          title_download: i18n('map_page.download'),
          body_download: i18n('map_page.download_message'),
          notes: [i18n('map_page.table_note_periodicity_map')],
          get_url: null,
        };
      },
      methods: { 
        hide_dropdowns: function() {
          $(`#network_dropdown`).attr('hidden', 'hidden');
          $(`#vlan_id_dropdown`).attr('hidden', 'hidden');
          $(`#network_dropdown`).removeClass('d-inline');
          $(`#vlan_id_dropdown`).removeClass('d-inline');
        }, 
        create_action_button_historical_flow_link: function(_, type, rowData) {
        let historical_flow_link = {
          handlerId: "historical_flow_link",
          onClick: () => {
            historical_flow$1(rowData);
          },
        };
        let live_flow_link = {
          handlerId: "live_flow_link",
          onClick: () => {
            live_flow(rowData);
          },
        };

        return DataTableUtils.createActionButtons([
          { class: `pointer`, handler: historical_flow_link, icon: 'fas fa-stream', title: i18n('db_explorer.historical_data') },
          { class: `pointer`, handler: live_flow_link, icon: 'fas fa-stream', title: i18n('live_flows') },

        ]);
        
        },
        delete_all: async function() {
          let url = `${http_prefix}/lua/pro/enterprise/network_maps.lua`;
          let params = {
            ifid: this.url_params.ifid,
            action: 'reset',
            page: this.url_params.page,
            csrf: this.$props.page_csrf,
            map: this.url_params.map
          };
          try {
            let headers = {
              'Content-Type': 'application/json'
            };
            await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
            this.reload_table();
          } catch(err) {
            this.reload_table();  
          }      
        },
        reload_table: function() {
          let table = this.get_active_table();
          NtopUtils.showOverlays();
          table.reload();
          NtopUtils.hideOverlays();
        },
        destroy: function() {
          let table = this.get_active_table();
          table.destroy_table();
        },
        get_active_table: function() {
          return this.$refs[`table_periodicity`];
        },
        show_delete_all_dialog: function() {
          this.$refs["modal_delete_all"].show();
        },  
      },
    };  

    function historical_flow$1(row) {
      const client_ip = row.client.split("host=")[1].split(">")[0];
      const client = client_ip.substring(0, client_ip.length - 1);
      const server_ip = row.server.split("host=")[1].split(">")[0];
      const server = server_ip.substring(0, server_ip.length - 1);
      const port = row.port;

      const epoch_begin = row.first_seen;
      const epoch_end = row.last_seen.epoch_end;

      const params = {
        epoch_begin: epoch_begin,
        epoch_end: epoch_end,
        srv_ip: `${server};eq`,
        cli_ip: `${client};eq`,
        srv_port: `${port};eq`,
      };
      const url_params = ntopng_url_manager$1.obj_to_url_params(params);
      const url = `${http_prefix}/lua/pro/db_search.lua?${url_params}`;
      ntopng_url_manager$1.go_to_url(url);

    }

    function live_flow(row) {
      const client_ip = row.client.split("host=")[1].split(">")[0];
      const client = client_ip.substring(0, client_ip.length - 1);
      const server_ip = row.server.split("host=")[1].split(">")[0];
      const server = server_ip.substring(0, server_ip.length - 1);
      const port = row.port;


      const params = {
        server: `${server}`,
        client: `${client}`,
        port: `${port}`,
      };
      const url_params = ntopng_url_manager$1.obj_to_url_params(params);
      const url = `${http_prefix}/lua/flows_stats.lua?${url_params}`;
      ntopng_url_manager$1.go_to_url(url);
    }

    function start_datatable$4(DatatableVue) {
      const datatableButton = [];
      let columns = [];
      let default_sorting_columns = 0;
      DatatableVue.get_url = NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, url_params);
      
      /* Manage the buttons close to the search box */
      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function (e, dt, node, config) {
          DatatableVue.reload_table();
        }
      });
      
      let tmp_params = ntopng_utility.clone(url_params);
      tmp_params['view'] = null;
      let defaultDatatableConfig = {
        table_buttons: datatableButton,
        columns_config: [],
        data_url: NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, tmp_params),
        enable_search: true,
      };

      let table_filters = [];
      for (let filter of (DatatableVue.$props.table_filters || [])) {
        filter.callbackFunction = (table, value) => {
          tmp_params[filter.filterMenuKey] = value.id;
          ntopng_url_manager$1.set_key_to_url(filter.filterMenuKey, value.id);
          table.ajax.url(NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, tmp_params));
          NtopUtils.showOverlays();
          table.ajax.reload();
          NtopUtils.hideOverlays();
        },
        table_filters.push(filter);
      }
      
      /* Standard table configuration */  

      columns = [
        { columnName: i18n("actions"), name: 'actions',  className: 'text-center', orderable: false, responsivePriority: 0, render: function (_, type, rowData) {
            return DatatableVue.create_action_button_historical_flow_link(_, type,rowData);
          },
        },
        { columnName: i18n('map_page.last_seen'), name: 'last_seen', data: 'last_seen', className: 'text-center text-nowrap', render: (data, type) => { return data.value }, responsivePriority: 2 },
        { columnName: i18n('map_page.client'), name: 'client', data: 'client', className: 'text-nowrap', responsivePriority: 2 },
        { columnName: i18n('map_page.server'), name: 'server', data: 'server', className: 'text-nowrap', responsivePriority: 2 },
        { columnName: i18n('map_page.port'), name: 'port', data: 'port',  className: 'text-center', responsivePriority: 4 },
        { columnName: i18n('map_page.protocol'), name: 'l7proto', data: 'protocol', className: 'text-nowrap', responsivePriority: 3 },
        { columnName: i18n('map_page.first_seen'), name: 'first_seen', data: 'first_seen', visible: false, responsivePriority: 3 },
        { columnName: i18n('duration'), name: 'duration', data: 'duration', className: 'text-center text-nowrap',  responsivePriority: 3, orderable: true,  },
        { columnName: i18n('map_page.observations'), name: 'observations', data: 'observations', className: 'text-center', responsivePriority: 4 },
        { columnName: i18n('map_page.frequency'), name: 'frequency', data: 'frequency', className: 'text-center', orderable: true, responsivePriority: 4, render: ( data, type, row ) => {
            return (type == "sort" || type == 'type') ? data : data + " sec"; 
          }
        },
      ];

      default_sorting_columns = 8; /* Observation column */

      /* Extra table configuration */
      let table_config = {
        serverSide: true,
        order: [[ default_sorting_columns, 'desc' ]]
      };
      
      let configDevices = ntopng_utility.clone(defaultDatatableConfig);
      configDevices.table_buttons = defaultDatatableConfig.table_buttons;
      configDevices.data_url = `${configDevices.data_url}`;
      configDevices.columns_config = columns;
      configDevices.table_filters = table_filters;
      configDevices.table_config = ntopng_utility.clone(table_config);
      DatatableVue.config_devices_standard = configDevices;
    }

    const _hoisted_1$1Y = { class: "row" };
    const _hoisted_2$1L = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1A = { class: "card" };
    const _hoisted_4$1x = { class: "card-body" };
    const _hoisted_5$1i = { id: "periodicity-table" };
    const _hoisted_6$14 = { class: "card-footer" };
    const _hoisted_7$Z = {
      key: 0,
      type: "button",
      id: "btn-delete-all",
      class: "btn btn-danger me-1"
    };
    const _hoisted_8$P = ["href"];

    function render$9(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_modal_delete_confirm = resolveComponent("modal-delete-confirm");
      const _component_datatable = resolveComponent("datatable");
      const _component_NoteList = resolveComponent("NoteList");

      return (openBlock(), createElementBlock("div", _hoisted_1$1Y, [
        createBaseVNode("div", _hoisted_2$1L, [
          _cache[3] || (_cache[3] = createBaseVNode("div", {
            class: "alert alert-danger d-none",
            id: "alert-row-buttons",
            role: "alert"
          }, null, -1 /* CACHED */)),
          createBaseVNode("div", _hoisted_3$1A, [
            _cache[2] || (_cache[2] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1x, [
              createBaseVNode("div", _hoisted_5$1i, [
                createVNode(_component_modal_delete_confirm, {
                  ref: "modal_delete_all",
                  title: $data.title_delete,
                  body: $data.body_delete,
                  onDelete: $options.delete_all
                }, null, 8 /* PROPS */, ["title", "body", "onDelete"]),
                createVNode(_component_datatable, {
                  ref: "table_periodicity",
                  table_buttons: $data.config_devices_standard.table_buttons,
                  columns_config: $data.config_devices_standard.columns_config,
                  data_url: $data.config_devices_standard.data_url,
                  enable_search: $data.config_devices_standard.enable_search,
                  filter_buttons: $data.config_devices_standard.table_filters,
                  table_config: $data.config_devices_standard.table_config,
                  base_url: $data.base_url,
                  base_params: $props.url_params
                }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "filter_buttons", "table_config", "base_url", "base_params"])
              ])
            ]),
            createBaseVNode("div", _hoisted_6$14, [
              ($props.is_admin)
                ? (openBlock(), createElementBlock("button", _hoisted_7$Z, [
                    _cache[0] || (_cache[0] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                    createTextVNode(" " + toDisplayString($data.i18n("map_page.delete_services")), 1 /* TEXT */)
                  ]))
                : createCommentVNode("v-if", true),
              createBaseVNode("a", {
                href: $data.get_url,
                class: "btn btn-primary",
                role: "button",
                "aria-disabled": "true",
                download: "periodicity_map.json",
                target: "_blank"
              }, [...(_cache[1] || (_cache[1] = [
                createBaseVNode("i", { class: "fas fa-download" }, null, -1 /* CACHED */)
              ]))], 8 /* PROPS */, _hoisted_8$P)
            ])
          ]),
          createVNode(_component_NoteList, { note_list: $data.notes }, null, 8 /* PROPS */, ["note_list"])
        ])
      ]))
    }

    script$28.render = render$9;
    script$28.__file = "http_src/vue/page-periodicity-table.vue";

    /**
        (C) 2022 - ntop.org    
    */


    const defaultOptions = { 
      autoResize: true, /* By default already true */
      nodes: { 
        shape: "dot", 
        scaling: {
          min: 10,
          max: 30,
          label: {
            enabled: true,
            min: 8,
            max: 30,
            drawThreshold: 12,
          },
        },
        font: {
          color: "#212529"
        },
        shadow: false,
      },
      edges: {
        width: 0.2,
        color: { inherit: "from" },
        smooth: {
            type: "continuous",
            roundness: 0
        },
        arrows: {
          to: {
            enabled: true,
            scaleFactor: 0.3,
          }
        },
        arrowStrikethrough: false,
        scaling: {
          min: 1,
          max: 5,
        }
      },
      layout: {
        improvedLayout: false,
      },
      interaction: {
        hideEdgesOnDrag: false,
        hideEdgesOnZoom: true,
        hover: false,
        tooltipDelay: 0,
      },
      physics: {
        enabled: true,
        solver: "forceAtlas2Based",
        stabilization: {
          iterations: 300,
          enabled: true // This is here just to see what's going on from the very beginning.
        },
        forceAtlas2Based: {
          centralGravity: 0.01,
          springLength: 100,
        }
      },
      groups: {
          unknown: {
              shape: "dot",
          },
          printer: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf02f',
              size: 50,
              weight: 700,
            },
          },

          video: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf03d',
              size: 50,
              weight: 700,
            },
          },

          workstation: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf109',
              size: 50,
              weight: 700,
            },
          },

          laptop: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf109',
              size: 50,
              weight: 700,
            },
          },

          tablet: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf10a',
              size: 50,
              weight: 700,
            },
          },

          phone: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf10b',
              size: 50,
              weight: 700,
            },
          },

          tv: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf26c',
              size: 50,
              weight: 700,
            },
          },

          networking: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf0b2',
              size: 50,
              weight: 700,
            },
          },

          wifi: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf1eb',
              size: 50,
              weight: 700,
            },
          },

          nas: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf1c0',
              size: 50,
              weight: 700,
            },
          },

          multimedia: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\uf001',
              size: 50,
              weight: 700,
            },
          },

          iot: {
            shape: "icon",
            icon: {
              face: 'FontAwesome',
              code: '\ue012',
              size: 50,
              weight: 700,
            },
          },

      },
    };

    const ntopng_map_manager = {
      /**
       * Change the status of a service.
       * @param {string} service_id id of the service.
       * @param {string} new_state id of the new status of the service.
       * @param {function} callback function to be called on success.
       */
      toggle_state: function(service_id, new_state, callback, csrf) {
        const request = $.post(`${http_prefix}/lua/pro/enterprise/switch_service_state.lua`, { 
          service_id: service_id, service_status: new_state, csrf: csrf
        });
        request.then((data) => {
          if(data.success && callback) 
            callback();
        });
      },
      get_default_options: function() {
        return defaultOptions;
      },
    };

    const change_service_table_tab_event = "change_service_table_tab_event";

    var script$27 = {
      components: {	  
        'datatable': script$2e,
        'modal-delete-confirm': script$2a,
        'tab-list': script$2b,
        NoteList: script$2u
      },
      props: {
        page_csrf: String,
        url_params: Object,
        view: String,
        table_filters: Array,
        is_admin: Boolean,
        service_acceptance: Array,
      },
      /**
       * First method called when the component is created.
       */
      created() {
        this.service_table_tab = ntopng_url_manager$1.get_url_entry('view') || 'standard';
        ntopng_url_manager$1.set_key_to_url('view', this.service_table_tab);
        this.tab_list.forEach((i) => {
          if(this.service_table_tab == i.id)
            i.active = true;
        });
        start_datatable$3(this);
      },
      mounted() {
        ntopng_events_manager$1.on_custom_event("page_service_table", ntopng_custom_events.DATATABLE_LOADED, () => {
          if(ntopng_url_manager$1.get_url_entry('host'))
            this.hide_dropdowns();
        });

        ntopng_events_manager$1.on_custom_event("change_service_table_tab", change_service_table_tab_event, (tab) => {
    	    let table = this.get_active_table();
          ntopng_url_manager$1.set_key_to_url('view', tab);
          table.delete_button_handlers(this.service_table_tab);
          table.destroy_table();
          this.service_table_tab = tab;
        });
        $("#btn-delete-all").click(() => this.show_delete_all_dialog());
      },    
      data() {
        return {
          i18n: (t) => i18n(t),
          config_devices_standard: null,
          config_devices_centrality: null,
          title_delete: i18n('map_page.delete_services'),
          body_delete: i18n('map_page.delete_services_message'),
          get_url: null,
          service_table_tab: null,
          notes: [i18n('map_page.table_note_service_map')],
          tab_list: [
            { 
              title: i18n('map_page.standard_view'),
              active: (view == 'standard'),
              id: 'standard'
            },
            { 
              title: i18n('map_page.centrality_view'),
              active: (view == 'centrality'),
              id: 'centrality'
            },
          ]
        };
      },
      methods: { 
        hide_dropdowns: function() {
          $(`#network_dropdown`).attr('hidden', 'hidden');
          $(`#vlan_id_dropdown`).attr('hidden', 'hidden');
          $(`#network_dropdown`).removeClass('d-inline');
          $(`#vlan_id_dropdown`).removeClass('d-inline');
        }, 
        destroy: function() {
          let table = this.get_active_table();
          table.delete_button_handlers(this.service_table_tab);
          table.destroy_table();
        },
        /* Method used to switch active table tab */
        click_item: function(item) {
          this.tab_list.forEach((i) => i.active = false);
          item.active = true;
          ntopng_events_manager$1.emit_custom_event(change_service_table_tab_event, item.id);
        },
        create_action_buttons: function(data, type, service) {
          const reload = this.reload_table;
          const csrf = this.$props.page_csrf;
          const toggle_allowed_state = {
            onClick: () => {
              ntopng_map_manager.toggle_state(service.hash_id, this.$props.service_acceptance[0].id, reload, csrf);
            }
          };
          const toggle_denied_state = {
            onClick: () => {
              ntopng_map_manager.toggle_state(service.hash_id, this.$props.service_acceptance[1].id, reload, csrf);
            }
          };
          const toggle_undecided_state = {
            onClick: () => {
              ntopng_map_manager.toggle_state(service.hash_id, this.$props.service_acceptance[2].id, reload, csrf);
            }
          };

          if (type !== "display") return data;
          const currentStatus = service.acceptance;
          const allowedButton = { class: 'dropdown-item', href: '#', title: this.$props.service_acceptance[0].label, handler: toggle_allowed_state };
          const deniedButton = { class: 'dropdown-item', href: '#', title: this.$props.service_acceptance[1].label, handler: toggle_denied_state };
          const undecidedButton = { class: 'dropdown-item disabled', href: '#', title: this.$props.service_acceptance[2].label, handler: toggle_undecided_state };
          
          switch (currentStatus) {
            case 0: /* Allowed */   { allowedButton.class = 'dropdown-item active'; break; }
            case 1: /* Denied */    { deniedButton.class = 'dropdown-item active'; break; }
            case 2: /* Undecided */ { undecidedButton.class = 'dropdown-item active disabled'; break; }
          }
          return DataTableUtils.createActionButtons([undecidedButton, allowedButton, deniedButton]);
        },

        create_action_button_historical_flow_link: function(_, type, rowData) {
        let historical_flow_link = {
          handlerId: "historical_flow_link",
          onClick: () => {
            historical_flow(rowData);
          },
        };

        return DataTableUtils.createActionButtons([
          { class: `pointer`, handler: historical_flow_link, icon: 'fas fa-stream', title: i18n('db_explorer.historical_data') },
        ]);
        
        },
        delete_all: async function() {
          let url = `${http_prefix}/lua/pro/enterprise/network_maps.lua`;
          let params = {
            ifid: this.url_params.ifid,
            action: 'reset',
            page: this.url_params.page,
            csrf: this.$props.page_csrf,
            map: this.url_params.map
          };
          try {
            let headers = {
              'Content-Type': 'application/json'
            };
            await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
            this.reload_table();
          } catch(err) {
            this.reload_table();  
          }      
        },
        reload_table: function() {
          let table = this.get_active_table();
          NtopUtils.showOverlays();
          table.reload();
          NtopUtils.hideOverlays();
        },
        get_active_table: function() {
          return this.$refs[`table_service_${this.service_table_tab}`];
        },
        switch_to_standard: function() {
          let new_url = this.url_params;
          new_url['view'] = 'standard';
          document.location.href = NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/network_maps.lua`, url_params);
        },
        switch_to_centrality: function() {
          let new_url = this.url_params;
          new_url['view'] = 'centrality';
          document.location.href = NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/network_maps.lua`, url_params);
        },
        show_delete_all_dialog: function() {
          this.$refs["modal_delete_all"].show();
        },  
      },
    };  

    function historical_flow(row) {
      const client_ip = row.client.split("host=")[1].split(">")[0];
      const client = client_ip.substring(0, client_ip.length - 1);
      const server_ip = row.server.split("host=")[1].split(">")[0];
      const server = server_ip.substring(0, server_ip.length - 1);
      const port = row.port;

      const epoch_begin = row.first_seen;
      const epoch_end = row.last_seen.epoch_end;

      const params = {
        epoch_begin: epoch_begin,
        epoch_end: epoch_end,
        srv_ip: `${server};eq`,
        cli_ip: `${client};eq`,
        srv_port: `${port};eq`,
      };
      const url_params = ntopng_url_manager$1.obj_to_url_params(params);
      const url = `${http_prefix}/lua/pro/db_search.lua?${url_params}`;
      ntopng_url_manager$1.go_to_url(url);

    }

    function start_datatable$3(DatatableVue) {
      const datatableButton = [];
      let columns = [];
      let default_sorting_columns = 0;
      DatatableVue.get_url = NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, url_params);
      
      /* Manage the buttons close to the search box */
      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function (e, dt, node, config) {
          DatatableVue.reload_table();
        }
      });
      
      let tmp_params = url_params;
      tmp_params['view'] = 'standard';
      
      let defaultDatatableConfig = {
        table_buttons: datatableButton,
        columns_config: [],
        data_url: NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, tmp_params),
        enable_search: true,
      };

      let table_filters = [];
      for (let filter of (DatatableVue.$props.table_filters || [])) {
        filter.callbackFunction = (table, value) => {
          tmp_params['view'] = DatatableVue.service_table_tab;
          tmp_params[filter.filterMenuKey] = value.id;
          ntopng_url_manager$1.set_key_to_url(filter.filterMenuKey, value.id);
          table.ajax.url(NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, tmp_params));
          NtopUtils.showOverlays();
          table.ajax.reload();
          NtopUtils.hideOverlays();
        },
        table_filters.push(filter);
      }
      
      /* Standard table configuration */  

      columns = [
        { columnName: i18n("actions"), name: 'actions', data: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, render: function (_, type, rowData) {
            return DatatableVue.create_action_button_historical_flow_link(_, type,rowData);
          }, createdCell: DataTableRenders.applyCellStyle
        }, { columnName: i18n("map_page.last_seen"), name: 'last_seen', data: 'last_seen', className: 'text-center text-nowrap', render: (data, type) => { return data.value }, responsivePriority: 2 },
        { columnName: i18n("map_page.client"), name: 'client', data: 'client', className: 'text-nowrap', responsivePriority: 2 },
        { columnName: i18n("map_page.server"), name: 'server', data: 'server', className: 'text-nowrap', responsivePriority: 2 },
        { columnName: i18n("map_page.port"), name: 'port', data: 'port',  className: 'text-center', responsivePriority: 4 },
        { columnName: i18n("map_page.protocol"), name: 'l7proto', data: 'protocol', className: 'text-nowrap', responsivePriority: 3 },
        { columnName: i18n("map_page.first_seen"), name: 'first_seen', data: 'first_seen', visible: false, responsivePriority: 3 },
        { columnName: i18n("map_page.num_uses"), name: 'num_uses', data: 'num_uses',  className: 'text-center text-nowrap', responsivePriority: 4 },
        { columnName: i18n("map_page.info"), name: 'info', data: 'info', responsivePriority: 5 },
      ];

      default_sorting_columns = 7; /* Num Uses */

      if(DatatableVue.is_admin) {
        columns.push({ columnName: i18n("map_page.status"), name: 'service_acceptance', data: 'service_acceptance', className: 'text-center', orderable: false, responsivePriority: 1, render: (data, type, service) => {
            return DatatableVue.create_action_buttons(data, type, service);
          }
        });


      }
      
      /* Extra table configuration */
      let table_config = {
        serverSide: true,
        order: [[ default_sorting_columns, 'desc' ]]
      };
      
      let configDevices = ntopng_utility.clone(defaultDatatableConfig);
      configDevices.table_buttons = defaultDatatableConfig.table_buttons;
      configDevices.data_url = `${configDevices.data_url}`;
      configDevices.columns_config = columns;
      configDevices.table_filters = table_filters;
      configDevices.table_config = ntopng_utility.clone(table_config);
      DatatableVue.config_devices_standard = configDevices;

      /* Centrality table configuration */

      tmp_params['view'] = 'centrality';  
      defaultDatatableConfig.data_url = NtopUtils.buildURL(`${http_prefix}/lua/pro/enterprise/get_map.lua`, tmp_params);

      columns = [
        { columnName: i18n("map_page.host"), name: 'host', data: 'host', className: 'text-nowrap', responsivePriority: 1 },
        { columnName: i18n("map_page.total_edges"), name: 'total_edges', data: 'total_edges', className: 'text-nowrap', responsivePriority: 1 },
        { columnName: i18n("map_page.rank"), name: 'rank', data: 'rank', className: 'text-center', responsivePriority: 2 },
        { columnName: i18n("map_page.in_edges"), name: 'in_edges', data: 'in_edges', className: 'text-nowrap', responsivePriority: 2 },
        { columnName: i18n("map_page.out_edges"), name: 'out_edges', data: 'out_edges',  className: 'text-center', responsivePriority: 2 },
      ];
      
      default_sorting_columns = 2; /* Rank */
      table_config.order = [[ default_sorting_columns, 'desc' ]];
      configDevices = ntopng_utility.clone(defaultDatatableConfig);
      configDevices.table_buttons = defaultDatatableConfig.table_buttons;
      configDevices.data_url = `${configDevices.data_url}`;
      configDevices.columns_config = columns;
      configDevices.table_filters = table_filters;
      configDevices.table_config = ntopng_utility.clone(table_config);
      DatatableVue.config_devices_centrality = configDevices;
    }

    const _hoisted_1$1X = { class: "row" };
    const _hoisted_2$1K = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1z = { class: "card" };
    const _hoisted_4$1w = { class: "card-body" };
    const _hoisted_5$1h = { id: "table_service" };
    const _hoisted_6$13 = { class: "card-footer" };
    const _hoisted_7$Y = {
      type: "button",
      id: "btn-delete-all",
      class: "btn btn-danger me-1"
    };
    const _hoisted_8$O = ["href"];

    function render$8(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_modal_delete_confirm = resolveComponent("modal-delete-confirm");
      const _component_tab_list = resolveComponent("tab-list");
      const _component_datatable = resolveComponent("datatable");
      const _component_NoteList = resolveComponent("NoteList");

      return (openBlock(), createElementBlock("div", _hoisted_1$1X, [
        createBaseVNode("div", _hoisted_2$1K, [
          _cache[3] || (_cache[3] = createBaseVNode("div", {
            class: "alert alert-danger d-none",
            id: "alert-row-buttons",
            role: "alert"
          }, null, -1 /* CACHED */)),
          createBaseVNode("div", _hoisted_3$1z, [
            _cache[2] || (_cache[2] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1w, [
              createBaseVNode("div", _hoisted_5$1h, [
                createVNode(_component_modal_delete_confirm, {
                  ref: "modal_delete_all",
                  title: $data.title_delete,
                  body: $data.body_delete,
                  onDelete: $options.delete_all
                }, null, 8 /* PROPS */, ["title", "body", "onDelete"]),
                createVNode(_component_tab_list, {
                  ref: "service_tab_list",
                  id: "service_tab_list",
                  tab_list: $data.tab_list,
                  onClick_item: $options.click_item
                }, null, 8 /* PROPS */, ["tab_list", "onClick_item"]),
                ($data.service_table_tab == 'standard')
                  ? (openBlock(), createBlock(_component_datatable, {
                      key: 0,
                      ref: "table_service_standard",
                      table_buttons: $data.config_devices_standard.table_buttons,
                      columns_config: $data.config_devices_standard.columns_config,
                      data_url: $data.config_devices_standard.data_url,
                      enable_search: $data.config_devices_standard.enable_search,
                      filter_buttons: $data.config_devices_standard.table_filters,
                      table_config: $data.config_devices_standard.table_config
                    }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "filter_buttons", "table_config"]))
                  : createCommentVNode("v-if", true),
                ($data.service_table_tab == 'centrality')
                  ? (openBlock(), createBlock(_component_datatable, {
                      key: 1,
                      ref: "table_service_centrality",
                      table_buttons: $data.config_devices_centrality.table_buttons,
                      columns_config: $data.config_devices_centrality.columns_config,
                      data_url: $data.config_devices_centrality.data_url,
                      enable_search: $data.config_devices_centrality.enable_search,
                      filter_buttons: $data.config_devices_centrality.table_filters,
                      table_config: $data.config_devices_centrality.table_config
                    }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "filter_buttons", "table_config"]))
                  : createCommentVNode("v-if", true)
              ])
            ]),
            createBaseVNode("div", _hoisted_6$13, [
              createCommentVNode("\n        {% if is_admin then %}\n          <form class=\"d-inline\" id='switch-state-form'>\n            <div class=\"form-group mb-3 d-inline\">\n              <label>{* i18n(\"map_page.set_state\", {label = \"<span class='count'></span>\"}) *}</label>\n              <select name=\"new_state\" class=\"form-select d-inline\" style=\"width: 16rem\" {{ ternary(map.services_num == 0, \"disabled='disabled'\", \"\") }}>\n              {% for _, status in pairsByField(map.filters.service_status_filters, label, asc_insensitive) do %}\n                <option value=\"{{ status.id }}\">{* status.label *}</option>\n              {% end %}\n              </select>\n              <button class=\"btn btn-secondary d-inline\" class=\"btn-switch-state\" {{ ternary(map.services_num == 0, \"disabled='disabled'\", \"\") }}>\n                <i class=\"fas fa-random\"></i> {{ i18n(\"set\") }}\n              </button>\n            </div>\n          </form>\n        {% end %}\n    "),
              createBaseVNode("button", _hoisted_7$Y, [
                _cache[0] || (_cache[0] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString($data.i18n("map_page.delete_services")), 1 /* TEXT */)
              ]),
              createBaseVNode("a", {
                href: $data.get_url,
                class: "btn btn-primary",
                role: "button",
                "aria-disabled": "true",
                download: "service_map.json",
                target: "_blank"
              }, [...(_cache[1] || (_cache[1] = [
                createBaseVNode("i", { class: "fas fa-download" }, null, -1 /* CACHED */)
              ]))], 8 /* PROPS */, _hoisted_8$O)
            ])
          ]),
          createVNode(_component_NoteList, { note_list: $data.notes }, null, 8 /* PROPS */, ["note_list"])
        ])
      ]))
    }

    script$27.render = render$8;
    script$27.__file = "http_src/vue/page-service-table.vue";

    const _hoisted_1$1W = {
      key: 0,
      class: "alert alert-info"
    };
    const _hoisted_2$1J = ["id"];


    var script$26 = {
      __name: 'network-map',
      props: {
      empty_message: String,
      event_listeners: Object,
      page_csrf: String,
      url: String,
      url_params: Object,
      map_id: String,
    },
      setup(__props, { expose: __expose }) {

    const props = __props;

    const dataRequest = {
      ifid: props.url_params.ifid,
      action: 'load_graph',
      map: props.url_params.map_id
    };

    let nodes_dataset = {};
    let edges_dataset = {};
    let highlightActive = false;
    let network = null;
    const max_entries_reached = ref(false);
    const empty_map = ref(true);
    const is_destroyed = ref(false);
    const url_params = ref({});
    const datasets = ref(null);
    const options = ref(null);
    const all_nodes = ref(null);

    onMounted(async () => {
      const container = document.getElementById(props.map_id);
      load_scale();
      await request_info();
      options.value = ntopng_map_manager.get_default_options();
      network = new vis.Network(container, datasets.value, options.value);
      set_event_listener();
    });

    onBeforeUnmount(() => {
      if (is_destroyed.value == true) {
        return;
      }
      destroy();
    });

    const generate_html_tooltip = (x) => {
      const container = document.createElement("div");
      /* Necessary, otherwise it will go in conflict with other css */
      //container.style.color = "#111111";
      container.innerHTML = `<b>${x.label}</b><br><br>${i18n('db_explorer.host_data')}: ${x.id}`;
      return container;
    };

    /* This function is used to perform the request */
    const request_info = async () => {
      /* if an host has been defined inside the URL query then add it to the request */
      url_params.value = props.url_params;
      const url = NtopUtils.buildURL(props.url, url_params.value);
      await $.get(url, dataRequest, async function (response) {
        const { nodes, edges, max_entry_reached } = response.rsp;
        max_entries_reached.value = max_entry_reached;
        /* Adding tooltip to each node */
        nodes_dataset = new vis.DataSet(nodes.map((x) => {
          x.title = generate_html_tooltip(x);
          return x;
        }));
        edges_dataset = new vis.DataSet(edges);
        datasets.value = {
          nodes: nodes_dataset,
          edges: edges_dataset
        };
        all_nodes.value = nodes_dataset.get({ returnType: "Object" });
        empty_network();
      });
    };

    /* Add the host to the url and jump to the host */
    const jump_to_host = async (params) => {
      const host_info = params.id.split('@');
      url_params.value['host'] = host_info[0]; /* Host IP */
      url_params.value['vlan_id'] = host_info[1]; /* VLAN ID */
      ntopng_url_manager$1.set_key_to_url('host', url_params.value['host']);
      ntopng_url_manager$1.set_key_to_url('vlan_id', url_params.value['vlan_id']);
      ntopng_events_manager$1.emit_custom_event(ntopng_custom_events.CHANGE_PAGE_TITLE, params);
      await reload();
    };

    /* In case of empty network enable the "Empty Network" message */
    const empty_network = () => {
      if (datasets.value?.nodes.length == 0
        && datasets.value?.edges.length == 0) {
        empty_map.value = true;
      } else {
        empty_map.value = false;
      }
    };

    /* Fix the resizable width/height of the container */
    const load_scale = () => {
      const oldScale = NtopUtils.loadElementScale($(`.resizable-y-container`));

      if (oldScale == null) {
        const scale = { width: $(`.resizable-y-container`).width(), height: $(`.resizable-y-container`).height() };
        NtopUtils.saveElementScale($(this), scale);
        return;
      }

      $(`.resizable-y-container`).width(oldScale.width);
      $(`.resizable-y-container`).height(oldScale.height);
      $(`.resizable-y-container`).on('mouseup', function () {
        const scale = { width: $(`.resizable-y-container`).width(), height: $(`.resizable-y-container`).height() };
        NtopUtils.saveElementScale($(this), scale);
      });
    };

    function neighbourhoodHighlight(params) {
      // if something is selected:
      if (params.nodes.length > 0) {
        highlightActive = true;
        var i, j;
        var selectedNode = params.nodes[0];
        var degrees = 2;

        // mark all nodes as hard to read.
        for (var nodeId in all_nodes.value) {
          if (!all_nodes.value[nodeId].old_color) {
            all_nodes.value[nodeId].old_color =
              all_nodes.value[nodeId].color;
          }
          if (!all_nodes.value[nodeId].old_icon_color) {
            all_nodes.value[nodeId].old_icon_color =
              all_nodes.value[nodeId].icon;
          }
          all_nodes.value[nodeId].color = "#c8c8c8";
          all_nodes.value[nodeId].icon = {
            color: "#c8c8c8"
          };
          if (all_nodes.value[nodeId].hiddenLabel === undefined) {
            all_nodes.value[nodeId].hiddenLabel = all_nodes.value[nodeId].label;
            all_nodes.value[nodeId].label = undefined;
          }
        }
        var connectedNodes = network.getConnectedNodes(selectedNode);
        var allConnectedNodes = [];

        // get the second degree nodes
        for (i = 1; i < degrees; i++) {
          for (j = 0; j < connectedNodes.length; j++) {
            allConnectedNodes = allConnectedNodes.concat(
              network.getConnectedNodes(connectedNodes[j])
            );
          }
        }

        // all first degree nodes get their own color and their label back
        for (i = 0; i < connectedNodes.length; i++) {
          all_nodes.value[connectedNodes[i]].color =
            all_nodes.value[connectedNodes[i]].old_color;
          all_nodes.value[connectedNodes[i]].icon =
            all_nodes.value[connectedNodes[i]].old_icon_color;
          if (all_nodes.value[connectedNodes[i]].hiddenLabel !== undefined) {
            all_nodes.value[connectedNodes[i]].label =
              all_nodes.value[connectedNodes[i]].hiddenLabel;
            all_nodes.value[connectedNodes[i]].hiddenLabel = undefined;
          }
        }

        // the main node gets its own color and its label back.
        all_nodes.value[selectedNode].color =
          all_nodes.value[selectedNode].old_color;
        all_nodes.value[selectedNode].icon =
          all_nodes.value[selectedNode].old_icon_color;
        if (all_nodes.value[selectedNode].hiddenLabel !== undefined) {
          all_nodes.value[selectedNode].label = all_nodes.value[selectedNode].hiddenLabel;
          all_nodes.value[selectedNode].hiddenLabel = undefined;
        }
      } else if (highlightActive === true) {
        // reset all nodes
        for (var nodeId in all_nodes.value) {
          all_nodes.value[nodeId].color =
            all_nodes.value[nodeId].old_color;
          all_nodes.value[nodeId].icon =
            all_nodes.value[nodeId].old_icon_color;
          if (all_nodes.value[nodeId].hiddenLabel !== undefined) {
            all_nodes.value[nodeId].label = all_nodes.value[nodeId].hiddenLabel;
            all_nodes.value[nodeId].hiddenLabel = undefined;
          }
        }
        highlightActive = false;
      }

      // transform the object into an array
      var updateArray = [];
      for (nodeId in all_nodes.value) {
        if (all_nodes.value.hasOwnProperty(nodeId)) {
          updateArray.push(all_nodes.value[nodeId]);
        }
      }
      nodes_dataset.update(updateArray);
    }


    /* Set the event lister used for callbacks */
    const set_event_listener = () => {

      if (!props.event_listeners || !props.event_listeners["stabilizationIterationsDone"]) {
        network.on("stabilizationIterationsDone", function () {
          network.setOptions({ physics: false });
        });
      }

      if (!props.event_listeners || !props.event_listeners["click"]) {
        network.on("click", function (node) {
          neighbourhoodHighlight(node);
        });
      }

      if (!props.event_listeners || !props.event_listeners["doubleClick"]) {
        network.on("doubleClick", function (params) {
          jump_to_host(nodes_dataset.get(params.nodes[0]));
        });
      }


      if (!props.event_listeners || !props.event_listeners["afterDrawing"]) {
        network.on("afterDrawing", function (e) {
          ntopng_events_manager$1.emit_custom_event(ntopng_custom_events.VIS_DATA_LOADED);
        });
      }

      /* Given event listeners */
      for (const item in (props.event_listeners || {})) {
        network.on(item, props.event_listeners[item]);
      }
    };

    /* Function used to autolayout/stabilize the network */
    const autolayout = () => {
      if (network == null) {
        console.error("The network is undefined!");
        return;
      }

      if (!(network instanceof vis.Network)) {
        console.error("Not a vis.Network instance!");
        return;
      }

      network.stabilize();
    };

    /* Destroy the network if it's not null */
    const destroy = () => {
      if (network != null)
        network.destroy(true);

      is_destroyed.value = true;
    };

    /* This return true if the maximum number of nodes/edges has been reached */
    const is_max_entry_reached = () => {
      return max_entries_reached.value;
    };

    /* Function used to update the params */
    const update_url_params = (new_url_params) => {
      url_params.value = new_url_params;
    };

    /* Function used to reload the map */
    const reload = async () => {
      await request_info();
      if (network != null) {
        /* Reload of the physics is done due to a possible bug,
         * with many nodes, the physics could stuck infinitely 
         */
        network.setOptions({ physics: options.value.physics });
        network.setData(datasets.value);
      }
    };


    __expose({ reload, destroy, is_max_entry_reached, autolayout, update_url_params });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        (empty_map.value)
          ? (openBlock(), createElementBlock("div", _hoisted_1$1W, toDisplayString(__props.empty_message), 1 /* TEXT */))
          : createCommentVNode("v-if", true),
        createBaseVNode("div", {
          class: "d-flex justify-content-center align-items-center resizable-y-container",
          style: {"width":"100%","height":"60vh"},
          id: __props.map_id
        }, null, 8 /* PROPS */, _hoisted_2$1J)
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$26.__file = "http_src/vue/network-map.vue";

    var script$25 = {
      __name: 'modal-autolayout-confirm',
      props: {
        body: String,
        title: String,
    },
      emits: ['autolayout'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;

    const showed = () => {};

    const show = () => {
        modal_id.value.show();
    };

    const autolayout_ = () => {
        emit('autolayout');
        close();
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[0] || (_cache[0] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(__props.body), 1 /* TEXT */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: autolayout_,
            class: "btn btn-primary"
          }, toDisplayString(_i18n('confirm')), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$25.__file = "http_src/vue/modal-autolayout-confirm.vue";

    const change_filter_event$2 = "change_filter_event";

    var script$24 = {
      components: {
        'network-map': script$26,
        'modal-delete-confirm': script$2a,
        'modal-autolayout-confirm': script$25,
        'select-search': script$2H,
        NoteList: script$2u
    },
      props: {
        page_csrf: String,
        ifid: Number,
        url_params: Object,
        map_id: String,
        is_admin: Boolean,
        all_filter_list: Object,
      },
      /**
       * First method called when the component is created.
       */
      created() {
        start_vis_network_map$2(this);
      },
      mounted() {
        const max_entries_reached = this.max_entry_reached;
        const reload_map = this.reload_map;
        if (this.$props.url_params.host && this.$props.url_params.host != '') {
          this.hide_dropdowns();
        }

        ntopng_events_manager$1.on_custom_event("page_service_map", ntopng_custom_events.CHANGE_PAGE_TITLE, (node) => {
          this.hide_dropdowns();
        });

        ntopng_events_manager$1.on_custom_event("change_filter_event", change_filter_event$2, (filter) => {
          this.active_filter_list.forEach((filter_el, i) => {
            if (filter_el.filter_name == filter.filter_name) {
              this.active_filter_list[i] = filter;
            }
          });
          ntopng_url_manager.set_key_to_url(filter.filter_name, filter.key);
          this.url_params[filter.filter_name] = filter.key;
          this.update_and_reload_map();
        });

        ntopng_events_manager$1.on_custom_event(this.get_map(), ntopng_custom_events.VIS_DATA_LOADED, (filter) => {
          if (max_entries_reached()) {
            $(`#max-entries-reached`).removeAttr('hidden');
          } else {
            $(`#max-entries-reached`).attr('hidden', 'hidden');
          }
          NtopUtils.hideOverlays();
        });

        /* Remove invalid filters */
        let entries = ntopng_url_manager.get_url_entries();
        for (const [key, value] of entries) {
          this.url_params[key] = value;
        }

        //this.update_and_reload_map()

        $(`#reload-graph`).click(function (e) {
          reload_map();
        });

        $("#btn-delete-all").click(() => this.show_delete_all_dialog());
        $("#autolayout").click(() => this.show_autolayout_dialog());
      },
      data() {

        return {
          i18n: (t) => i18n(t),
          container: null,
          update_view_state_id: null,
          get_url: null,
          download_url: null,
          filter_list: [],
          active_filter_list: [],
          event_listeners: {},
          title_delete: i18n('map_page.delete_services'),
          body_delete: i18n('map_page.delete_services_message'),
          title_autolayout: i18n('map_page.autolayout_services'),
          body_autolayout: i18n('map_page.autolayout_services_message'),
          no_services_message: i18n('map_page.no_services'),
          max_entry_title: i18n('max_entries_reached'),
          notes: [i18n('map_page.graph_note_service_map')]
        };
      },
      methods: {
        destroy: function () {
          let map = this.get_map();
          map.destroy();
        },
        /* Method used to switch active table tab */
        click_item: function (filter) {
          ntopng_events_manager$1.emit_custom_event(change_filter_event$2, filter);
        },
        get_map: function () {
          return this.$refs[`service_map`];
        },
        max_entry_reached: function () {
          let map = this.get_map();
          return map.is_max_entry_reached();
        },
        reload_map: async function () {
          NtopUtils.showOverlays();
          let map = this.get_map();
          await map.reload();
        },
        update_and_reload_map: async function () {
          let map = this.get_map();
          NtopUtils.showOverlays();
          map.update_url_params(this.url_params);
          await map.reload();
        },
        autolayout: function () {
          let map = this.get_map();
          map.autolayout();
        },
        show_delete_all_dialog: function () {
          this.$refs["modal_delete_all"].show();
        },
        show_autolayout_dialog: function () {
          this.$refs["modal_autolayout"].show();
        },
        hide_dropdowns: function () {
          $(`#network_dropdown`).attr('hidden', 'hidden');
          $(`#vlan_id_dropdown`).attr('hidden', 'hidden');
        },
        delete_all: async function () {
          let url = `${http_prefix}/lua/pro/enterprise/network_maps.lua`;
          let params = {
            ifid: this.url_params.ifid,
            action: 'reset',
            csrf: this.$props.page_csrf,
            map: this.url_params.map
          };
          try {
            let headers = {
              'Content-Type': 'application/json'
            };
            await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
          } finally {
            this.reload_map();
          }
        },
      },
    };

    function start_vis_network_map$2(NetworkMapVue) {
      /* Format the filter list, to add the dropdowns */
      for (const filter_name in NetworkMapVue.$props.all_filter_list) {
        NetworkMapVue.filter_list.push(NetworkMapVue.$props.all_filter_list[filter_name]);
        const active_filter = ntopng_url_manager.get_url_entry(filter_name);
        /* Put the filter name into the filters */
        for (let [_, value] of Object.entries(NetworkMapVue.$props.all_filter_list[filter_name])) {
          value['filter_name'] = filter_name;
          if (active_filter) {
            /* If there is a filter selected in the url push that as active */
            if (value.id == active_filter)
              NetworkMapVue.active_filter_list.push(value);
          } else {
            /* push the default filter as active */
            if (value.currently_active == true)
              NetworkMapVue.active_filter_list.push(value);
          }
        }
      }

      NetworkMapVue.get_url = `${http_prefix}/lua/pro/rest/v2/get/interface/map/data.lua`;
      NetworkMapVue.download_url = NtopUtils.buildURL(NetworkMapVue.get_url, NetworkMapVue.$props.url_params);
      NetworkMapVue.event_listeners = {};
    }

    const _hoisted_1$1V = { class: "row" };
    const _hoisted_2$1I = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1y = { class: "card card-shadow" };
    const _hoisted_4$1v = { class: "card-body" };
    const _hoisted_5$1g = { id: "table_service" };
    const _hoisted_6$12 = { class: "d-flex align-items-center justify-content-end mb-2" };
    const _hoisted_7$X = ["title"];
    const _hoisted_8$N = { class: "d-flex ms-auto" };
    const _hoisted_9$F = { class: "m-1" };
    const _hoisted_10$A = { class: "card-footer" };
    const _hoisted_11$x = {
      type: "button",
      id: "btn-delete-all",
      class: "btn btn-danger me-1"
    };
    const _hoisted_12$s = ["href"];

    function render$7(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_select_search = resolveComponent("select-search");
      const _component_modal_delete_confirm = resolveComponent("modal-delete-confirm");
      const _component_modal_autolayout_confirm = resolveComponent("modal-autolayout-confirm");
      const _component_network_map = resolveComponent("network-map");
      const _component_NoteList = resolveComponent("NoteList");

      return (openBlock(), createElementBlock("div", _hoisted_1$1V, [
        createBaseVNode("div", _hoisted_2$1I, [
          createBaseVNode("div", _hoisted_3$1y, [
            _cache[5] || (_cache[5] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1v, [
              createBaseVNode("div", _hoisted_5$1g, [
                createBaseVNode("div", _hoisted_6$12, [
                  createBaseVNode("button", {
                    id: "max-entries-reached",
                    type: "button",
                    class: "btn btn-link",
                    title: $data.max_entry_title,
                    disabled: "",
                    hidden: ""
                  }, [...(_cache[0] || (_cache[0] = [
                    createBaseVNode("i", { class: "text-danger fa-solid fa-triangle-exclamation" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_7$X),
                  createBaseVNode("div", _hoisted_8$N, [
                    (openBlock(true), createElementBlock(Fragment, null, renderList($data.filter_list, (_, index) => {
                      return (openBlock(), createElementBlock("div", _hoisted_9$F, [
                        createVNode(_component_select_search, {
                          selected_option: $data.active_filter_list[index],
                          "onUpdate:selected_option": $event => (($data.active_filter_list[index]) = $event),
                          options: $data.filter_list[index],
                          onSelect_option: $options.click_item
                        }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options", "onSelect_option"])
                      ]))
                    }), 256 /* UNKEYED_FRAGMENT */))
                  ]),
                  _cache[1] || (_cache[1] = createBaseVNode("button", {
                    type: "button",
                    id: "reload-graph",
                    class: "btn btn-link btn-reload-graph"
                  }, [
                    createBaseVNode("i", { class: "fas fa-sync" })
                  ], -1 /* CACHED */)),
                  _cache[2] || (_cache[2] = createBaseVNode("button", {
                    type: "button",
                    id: "autolayout",
                    class: "btn btn-link btn-stabilize"
                  }, [
                    createBaseVNode("i", { class: "fas fa-magic" })
                  ], -1 /* CACHED */))
                ]),
                createVNode(_component_modal_delete_confirm, {
                  ref: "modal_delete_all",
                  title: $data.title_delete,
                  body: $data.body_delete,
                  onDelete: $options.delete_all
                }, null, 8 /* PROPS */, ["title", "body", "onDelete"]),
                createVNode(_component_modal_autolayout_confirm, {
                  ref: "modal_autolayout",
                  title: $data.title_autolayout,
                  body: $data.body_autolayout,
                  onAutolayout: $options.reload_map
                }, null, 8 /* PROPS */, ["title", "body", "onAutolayout"]),
                createVNode(_component_network_map, {
                  ref: "service_map",
                  empty_message: $data.no_services_message,
                  event_listeners: $data.event_listeners,
                  page_csrf: $props.page_csrf,
                  url: $data.get_url,
                  url_params: $props.url_params,
                  map_id: $props.map_id
                }, null, 8 /* PROPS */, ["empty_message", "event_listeners", "page_csrf", "url", "url_params", "map_id"])
              ])
            ]),
            createBaseVNode("div", _hoisted_10$A, [
              createBaseVNode("button", _hoisted_11$x, [
                _cache[3] || (_cache[3] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString($data.i18n("map_page.delete_services")), 1 /* TEXT */)
              ]),
              createBaseVNode("a", {
                href: $data.download_url,
                class: "btn btn-primary",
                role: "button",
                "aria-disabled": "true",
                download: "service_map.json",
                target: "_blank"
              }, [...(_cache[4] || (_cache[4] = [
                createBaseVNode("i", { class: "fas fa-download" }, null, -1 /* CACHED */)
              ]))], 8 /* PROPS */, _hoisted_12$s)
            ])
          ]),
          createVNode(_component_NoteList, { note_list: $data.notes }, null, 8 /* PROPS */, ["note_list"])
        ])
      ]))
    }

    script$24.render = render$7;
    script$24.__file = "http_src/vue/page-service-map.vue";

    const change_filter_event$1 = "change_filter_event";

    var script$23 = {
      components: {
        'network-map': script$26,
        'modal-delete-confirm': script$2a,
        'modal-autolayout-confirm': script$25,
        'select-search': script$2H,
        NoteList: script$2u
      },
      props: {
        page_csrf: String,
        ifid: Number,
        url_params: Object,
        map_id: String,
        is_admin: Boolean,
        all_filter_list: Object,
      },
      /**
       * First method called when the component is created.
       */
      created() {
        start_vis_network_map$1(this);
      },
      mounted() {
        this.updated_url_params = this.$props.url_params;
        /* Remove invalid filters */
        let entries = ntopng_url_manager.get_url_entries();
        for (const [key, value] of entries) {
          this.updated_url_params[key] = value;
        }

        const max_entries_reached = this.max_entry_reached;
        const reload_map = this.reload_map;
        this.update_and_reload_map();
        if (this.$props.url_params.host && this.$props.url_params.host != '') {
          this.hide_dropdowns();
        }

        ntopng_events_manager$1.on_custom_event("page_periodicity_map", ntopng_custom_events.CHANGE_PAGE_TITLE, (node) => {
          this.hide_dropdowns();
        });

        ntopng_events_manager$1.on_custom_event("change_filter_event", change_filter_event$1, (filter) => {
          this.active_filter_list.forEach((filter_el, i) => {
            if (filter_el.filter_name == filter.filter_name) {
              this.active_filter_list[i] = filter;
            }
          });
          ntopng_url_manager.set_key_to_url(filter.filter_name, filter.key);
          this.url_params[filter.filter_name] = filter.key;
          this.update_and_reload_map();
        });

        ntopng_events_manager$1.on_custom_event(this.get_map(), ntopng_custom_events.VIS_DATA_LOADED, (filter) => {
          if (max_entries_reached()) {
            $(`#max-entries-reached`).removeAttr('hidden');
          } else {
            $(`#max-entries-reached`).attr('hidden', 'hidden');
          }
          NtopUtils.hideOverlays();
        });

        $(`#reload-graph`).click(function (e) {
          reload_map();
        });

        $("#btn-delete-all").click(() => this.show_delete_all_dialog());
        $("#autolayout").click(() => this.show_autolayout_dialog());
      },
      data() {
        return {
          i18n: (t) => i18n(t),
          container: null,
          update_view_state_id: null,
          get_url: null,
          download_url: null,
          updated_url_params: {},
          filter_list: [],
          active_filter_list: [],
          event_listeners: {},
          title_delete: i18n('map_page.delete_services'),
          body_delete: i18n('map_page.delete_services_message'),
          title_autolayout: i18n('map_page.autolayout_services'),
          body_autolayout: i18n('map_page.autolayout_services_message'),
          no_services_message: i18n('map_page.no_services'),
          max_entry_title: i18n('max_entries_reached'),      
          notes: [i18n('map_page.graph_note_periodicity_map')]

        };
      },
      methods: {
        destroy: function () {
          let map = this.get_map();
          map.destroy();
        },
        /* Method used to switch active table tab */
        click_item: function (filter) {
          ntopng_events_manager$1.emit_custom_event(change_filter_event$1, filter);
        },
        get_map: function () {
          return this.$refs[`periodicity_map`]
        },
        max_entry_reached: function () {
          let map = this.get_map();
          return map.is_max_entry_reached();
        },
        reload_map: async function () {
          NtopUtils.showOverlays();
          let map = this.get_map();
          await map.reload();
        },
        hide_dropdowns: function () {
          $(`#network_dropdown`).attr('hidden', 'hidden');
          $(`#vlan_id_dropdown`).attr('hidden', 'hidden');
        },
        update_and_reload_map: async function () {
          let map = this.get_map();
          NtopUtils.showOverlays();
          map.update_url_params(this.updated_url_params);
          await map.reload();
        },
        autolayout: function () {
          let map = this.get_map();
          map.autolayout();
        },
        show_delete_all_dialog: function () {
          this.$refs["modal_delete_all"].show();
        },
        show_autolayout_dialog: function () {
          this.$refs["modal_autolayout"].show();
        },
        delete_all: async function () {
          let url = `${http_prefix}/lua/pro/enterprise/network_maps.lua`;
          let params = {
            ifid: this.updated_url_params.ifid,
            action: 'reset',
            csrf: this.$props.page_csrf,
            map: this.updated_url_params.map
          };
          try {
            let headers = {
              'Content-Type': 'application/json'
            };
            await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
          } finally {
            this.reload_map();
          }
        },
      },
    };

    function start_vis_network_map$1(NetworkMapVue) {
      /* Format the filter list, to add the dropdowns */
      for (const filter_name in NetworkMapVue.$props.all_filter_list) {
        NetworkMapVue.filter_list.push(NetworkMapVue.$props.all_filter_list[filter_name]);
        const active_filter = ntopng_url_manager.get_url_entry(filter_name);
        /* Put the filter name into the filters */
        for (let [_, value] of Object.entries(NetworkMapVue.$props.all_filter_list[filter_name])) {
          value['filter_name'] = filter_name;
          if (active_filter) {
            /* If there is a filter selected in the url push that as active */
            if (value.id == active_filter)
              NetworkMapVue.active_filter_list.push(value);
          } else {
            /* push the default filter as active */
            if (value.currently_active == true)
              NetworkMapVue.active_filter_list.push(value);
          }
        }
      }

      NetworkMapVue.get_url = `${http_prefix}/lua/pro/rest/v2/get/interface/map/data.lua`;
      NetworkMapVue.download_url = NtopUtils.buildURL(NetworkMapVue.get_url, NetworkMapVue.updated_url_params);
      NetworkMapVue.event_listeners = {};
    }

    const _hoisted_1$1U = { class: "row" };
    const _hoisted_2$1H = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1x = { class: "card card-shadow" };
    const _hoisted_4$1u = { class: "card-body" };
    const _hoisted_5$1f = { id: "table_periodicity" };
    const _hoisted_6$11 = { class: "d-flex align-items-center justify-content-end mb-2" };
    const _hoisted_7$W = ["title"];
    const _hoisted_8$M = { class: "d-flex ms-auto" };
    const _hoisted_9$E = { class: "m-1" };
    const _hoisted_10$z = { class: "card-footer" };
    const _hoisted_11$w = {
      type: "button",
      id: "btn-delete-all",
      class: "btn btn-danger me-1"
    };
    const _hoisted_12$r = ["href"];

    function render$6(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_select_search = resolveComponent("select-search");
      const _component_modal_delete_confirm = resolveComponent("modal-delete-confirm");
      const _component_modal_autolayout_confirm = resolveComponent("modal-autolayout-confirm");
      const _component_network_map = resolveComponent("network-map");
      const _component_NoteList = resolveComponent("NoteList");

      return (openBlock(), createElementBlock("div", _hoisted_1$1U, [
        createBaseVNode("div", _hoisted_2$1H, [
          createBaseVNode("div", _hoisted_3$1x, [
            _cache[5] || (_cache[5] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1u, [
              createBaseVNode("div", _hoisted_5$1f, [
                createBaseVNode("div", _hoisted_6$11, [
                  createBaseVNode("button", {
                    id: "max-entries-reached",
                    type: "button",
                    class: "btn btn-link",
                    title: $data.max_entry_title,
                    disabled: "",
                    hidden: ""
                  }, [...(_cache[0] || (_cache[0] = [
                    createBaseVNode("i", { class: "text-danger fa-solid fa-triangle-exclamation" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_7$W),
                  createBaseVNode("div", _hoisted_8$M, [
                    (openBlock(true), createElementBlock(Fragment, null, renderList($data.filter_list, (_, index) => {
                      return (openBlock(), createElementBlock("div", _hoisted_9$E, [
                        createVNode(_component_select_search, {
                          selected_option: $data.active_filter_list[index],
                          "onUpdate:selected_option": $event => (($data.active_filter_list[index]) = $event),
                          options: $data.filter_list[index],
                          onSelect_option: $options.click_item
                        }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options", "onSelect_option"])
                      ]))
                    }), 256 /* UNKEYED_FRAGMENT */))
                  ]),
                  _cache[1] || (_cache[1] = createBaseVNode("button", {
                    type: "button",
                    id: "reload-graph",
                    class: "btn btn-link btn-reload-graph"
                  }, [
                    createBaseVNode("i", { class: "fas fa-sync" })
                  ], -1 /* CACHED */)),
                  _cache[2] || (_cache[2] = createBaseVNode("button", {
                    type: "button",
                    id: "autolayout",
                    class: "btn btn-link btn-stabilize"
                  }, [
                    createBaseVNode("i", { class: "fas fa-magic" })
                  ], -1 /* CACHED */))
                ]),
                createVNode(_component_modal_delete_confirm, {
                  ref: "modal_delete_all",
                  title: $data.title_delete,
                  body: $data.body_delete,
                  onDelete: $options.delete_all
                }, null, 8 /* PROPS */, ["title", "body", "onDelete"]),
                createVNode(_component_modal_autolayout_confirm, {
                  ref: "modal_autolayout",
                  title: $data.title_autolayout,
                  body: $data.body_autolayout,
                  onAutolayout: $options.reload_map
                }, null, 8 /* PROPS */, ["title", "body", "onAutolayout"]),
                createVNode(_component_network_map, {
                  ref: "periodicity_map",
                  empty_message: $data.no_services_message,
                  event_listeners: $data.event_listeners,
                  page_csrf: $props.page_csrf,
                  url: $data.get_url,
                  url_params: $props.url_params,
                  map_id: $props.map_id
                }, null, 8 /* PROPS */, ["empty_message", "event_listeners", "page_csrf", "url", "url_params", "map_id"])
              ])
            ]),
            createBaseVNode("div", _hoisted_10$z, [
              createBaseVNode("button", _hoisted_11$w, [
                _cache[3] || (_cache[3] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString($data.i18n("map_page.delete_services")), 1 /* TEXT */)
              ]),
              createBaseVNode("a", {
                href: $data.download_url,
                class: "btn btn-primary",
                role: "button",
                "aria-disabled": "true",
                download: "periodicity_map.json",
                target: "_blank"
              }, [...(_cache[4] || (_cache[4] = [
                createBaseVNode("i", { class: "fas fa-download" }, null, -1 /* CACHED */)
              ]))], 8 /* PROPS */, _hoisted_12$r)
            ])
          ]),
          createVNode(_component_NoteList, { note_list: $data.notes }, null, 8 /* PROPS */, ["note_list"])
        ])
      ]))
    }

    script$23.render = render$6;
    script$23.__file = "http_src/vue/page-periodicity-map.vue";

    const change_filter_event = "change_filter_event";

    var script$22 = {
      components: {
        'network-map': script$26,
        'modal-delete-confirm': script$2a,
        'modal-autolayout-confirm': script$25,
        'select-search': script$2H,
        NoteList: script$2u
      },
      props: {
        page_csrf: String,
        ifid: Number,
        url_params: Object,
        map_id: String,
        is_admin: Boolean,
        all_filter_list: Object,
      },
      /**
       * First method called when the component is created.
       */
      created() {
        start_vis_network_map(this);
      },
      mounted() {
        const max_entries_reached = this.max_entry_reached;
        const reload_map = this.reload_map;
        if (this.$props.url_params.host && this.$props.url_params.host != '') {
          this.hide_dropdowns();
        }

        ntopng_events_manager$1.on_custom_event("page_service_map", ntopng_custom_events.CHANGE_PAGE_TITLE, (node) => {
          this.hide_dropdowns();
        });

        ntopng_events_manager$1.on_custom_event("change_filter_event", change_filter_event, (filter) => {
          this.active_filter_list.forEach((filter_el, i) => {
            if (filter_el.filter_name == filter.filter_name) {
              this.active_filter_list[i] = filter;
            }
          });
          ntopng_url_manager$1.set_key_to_url(filter.filter_name, filter.key);
          this.url_params[filter.filter_name] = filter.key;
          this.update_and_reload_map();
        });

        ntopng_events_manager$1.on_custom_event(this.get_map(), ntopng_custom_events.VIS_DATA_LOADED, (filter) => {
          if (max_entries_reached()) {
            $(`#max-entries-reached`).removeAttr('hidden');
          } else {
            $(`#max-entries-reached`).attr('hidden', 'hidden');
          }

          NtopUtils.hideOverlays();
        });

        /* Remove invalid filters */
        let entries = ntopng_url_manager$1.get_url_entries();
        for (const [key, value] of entries) {
          this.url_params[key] = value;
        }

        $(`#reload-graph`).click(function (e) {
          reload_map();
        });

        $("#btn-delete-all").click(() => this.show_delete_all_dialog());
        $("#autolayout").click(() => this.show_autolayout_dialog());
      },
      data() {
        return {
          i18n: (t) => i18n(t),
          container: null,
          update_view_state_id: null,
          get_url: null,
          download_url: null,
          filter_list: [],
          active_filter_list: [],
          event_listeners: {},
          title_delete: i18n('map_page.delete_services'),
          body_delete: i18n('map_page.delete_services_message'),
          title_autolayout: i18n('map_page.autolayout_services'),
          body_autolayout: i18n('map_page.autolayout_services_message'),
          no_services_message: i18n('map_page.no_services'),
          max_entry_title: i18n('max_entries_reached'),
          notes: [i18n('map_page.graph_note_asset_map')],
        };
      },
      methods: {
        destroy: function () {
          let map = this.get_map();
          map.destroy();
        },
        /* Method used to switch active table tab */
        click_item: function (filter) {
          ntopng_events_manager$1.emit_custom_event(change_filter_event, filter);
        },
        get_map: function () {
          return this.$refs[`asset_map`];
        },
        hide_dropdowns: function () {
          $(`#network_dropdown`).attr('hidden', 'hidden');
          $(`#vlan_id_dropdown`).attr('hidden', 'hidden');
        },
        max_entry_reached: function () {
          let map = this.get_map();
          return map.is_max_entry_reached();
        },
        reload_map: async function () {
          NtopUtils.showOverlays();
          let map = this.get_map();
          await map.reload();
        },
        update_and_reload_map: async function () {
          let map = this.get_map();
          NtopUtils.showOverlays();
          map.update_url_params(this.url_params);
          await map.reload();
        },
        autolayout: function () {
          let map = this.get_map();
          map.autolayout();
        },
        show_delete_all_dialog: function () {
          this.$refs["modal_delete_all"].show();
        },
        show_autolayout_dialog: function () {
          this.$refs["modal_autolayout"].show();
        },
        delete_all: async function () {
          let url = `${http_prefix}/lua/pro/enterprise/network_maps.lua`;
          let params = {
            ifid: this.url_params.ifid,
            action: 'reset',
            csrf: this.$props.page_csrf,
            map: this.url_params.map
          };
          try {
            let headers = {
              'Content-Type': 'application/json'
            };
            await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
          } finally {
            NtopUtils.showOverlays();
            this.reload_map();
          }
        },
      },
    };

    function start_vis_network_map(NetworkMapVue) {
      /* Format the filter list, to add the dropdowns */
      for (const filter_name in NetworkMapVue.$props.all_filter_list) {
        NetworkMapVue.filter_list.push(NetworkMapVue.$props.all_filter_list[filter_name]);
        const active_filter = ntopng_url_manager$1.get_url_entry(filter_name);
        /* Put the filter name into the filters */
        for (let [_, value] of Object.entries(NetworkMapVue.$props.all_filter_list[filter_name])) {
          value['filter_name'] = filter_name;
          if (active_filter) {
            /* If there is a filter selected in the url push that as active */
            if (value.id == active_filter)
              NetworkMapVue.active_filter_list.push(value);
          } else {
            /* push the default filter as active */
            if (value.currently_active == true)
              NetworkMapVue.active_filter_list.push(value);
          }
        }
      }

      NetworkMapVue.get_url = `${http_prefix}/lua/pro/rest/v2/get/interface/map/data.lua`;
      NetworkMapVue.download_url = NtopUtils.buildURL(NetworkMapVue.get_url, NetworkMapVue.$props.url_params);
      NetworkMapVue.event_listeners = {};
    }

    const _hoisted_1$1T = { class: "row" };
    const _hoisted_2$1G = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1w = { class: "card card-shadow" };
    const _hoisted_4$1t = { class: "card-body" };
    const _hoisted_5$1e = { id: "table_asset" };
    const _hoisted_6$10 = { class: "d-flex align-items-center justify-content-end mb-2" };
    const _hoisted_7$V = ["title"];
    const _hoisted_8$L = { class: "d-flex ms-auto" };
    const _hoisted_9$D = { class: "m-1" };
    const _hoisted_10$y = { class: "card-footer" };
    const _hoisted_11$v = {
      type: "button",
      id: "btn-delete-all",
      class: "btn btn-danger me-1"
    };
    const _hoisted_12$q = ["href"];

    function render$5(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_select_search = resolveComponent("select-search");
      const _component_modal_delete_confirm = resolveComponent("modal-delete-confirm");
      const _component_modal_autolayout_confirm = resolveComponent("modal-autolayout-confirm");
      const _component_network_map = resolveComponent("network-map");
      const _component_NoteList = resolveComponent("NoteList");

      return (openBlock(), createElementBlock("div", _hoisted_1$1T, [
        createBaseVNode("div", _hoisted_2$1G, [
          createBaseVNode("div", _hoisted_3$1w, [
            _cache[5] || (_cache[5] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1t, [
              createBaseVNode("div", _hoisted_5$1e, [
                createBaseVNode("div", _hoisted_6$10, [
                  createBaseVNode("button", {
                    id: "max-entries-reached",
                    type: "button",
                    class: "btn btn-link",
                    title: $data.max_entry_title,
                    disabled: "",
                    hidden: ""
                  }, [...(_cache[0] || (_cache[0] = [
                    createBaseVNode("i", { class: "text-danger fa-solid fa-triangle-exclamation" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_7$V),
                  createBaseVNode("div", _hoisted_8$L, [
                    (openBlock(true), createElementBlock(Fragment, null, renderList($data.filter_list, (_, index) => {
                      return (openBlock(), createElementBlock("div", _hoisted_9$D, [
                        createVNode(_component_select_search, {
                          selected_option: $data.active_filter_list[index],
                          "onUpdate:selected_option": $event => (($data.active_filter_list[index]) = $event),
                          options: $data.filter_list[index],
                          onSelect_option: $options.click_item
                        }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options", "onSelect_option"])
                      ]))
                    }), 256 /* UNKEYED_FRAGMENT */))
                  ]),
                  _cache[1] || (_cache[1] = createBaseVNode("button", {
                    type: "button",
                    id: "reload-graph",
                    class: "btn btn-link btn-reload-graph"
                  }, [
                    createBaseVNode("i", { class: "fas fa-sync" })
                  ], -1 /* CACHED */)),
                  _cache[2] || (_cache[2] = createBaseVNode("button", {
                    type: "button",
                    id: "autolayout",
                    class: "btn btn-link btn-stabilize"
                  }, [
                    createBaseVNode("i", { class: "fas fa-magic" })
                  ], -1 /* CACHED */))
                ]),
                createVNode(_component_modal_delete_confirm, {
                  ref: "modal_delete_all",
                  title: $data.title_delete,
                  body: $data.body_delete,
                  onDelete: $options.delete_all
                }, null, 8 /* PROPS */, ["title", "body", "onDelete"]),
                createVNode(_component_modal_autolayout_confirm, {
                  ref: "modal_autolayout",
                  title: $data.title_autolayout,
                  body: $data.body_autolayout,
                  onAutolayout: $options.reload_map
                }, null, 8 /* PROPS */, ["title", "body", "onAutolayout"]),
                createVNode(_component_network_map, {
                  ref: "asset_map",
                  empty_message: $data.no_services_message,
                  event_listeners: $data.event_listeners,
                  page_csrf: $props.page_csrf,
                  url: $data.get_url,
                  url_params: $props.url_params,
                  map_id: $props.map_id
                }, null, 8 /* PROPS */, ["empty_message", "event_listeners", "page_csrf", "url", "url_params", "map_id"])
              ])
            ]),
            createBaseVNode("div", _hoisted_10$y, [
              createBaseVNode("button", _hoisted_11$v, [
                _cache[3] || (_cache[3] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString($data.i18n("map_page.delete_services")), 1 /* TEXT */)
              ]),
              createBaseVNode("a", {
                href: $data.download_url,
                class: "btn btn-primary",
                role: "button",
                "aria-disabled": "true",
                download: "asset_map.json",
                target: "_blank"
              }, [...(_cache[4] || (_cache[4] = [
                createBaseVNode("i", { class: "fas fa-download" }, null, -1 /* CACHED */)
              ]))], 8 /* PROPS */, _hoisted_12$q)
            ])
          ]),
          createVNode(_component_NoteList, { note_list: $data.notes }, null, 8 /* PROPS */, ["note_list"])
        ])
      ]))
    }

    script$22.render = render$5;
    script$22.__file = "http_src/vue/page-asset-map.vue";

    const _hoisted_1$1S = { class: "form-group mb-3 row" };
    const _hoisted_2$1F = { class: "col-form-label col-sm-4" };
    const _hoisted_3$1v = { class: "col-sm-7" };
    const _hoisted_4$1s = { class: "form-group mb-3 row" };
    const _hoisted_5$1d = { class: "col-form-label col-sm-4 pt-2" };
    const _hoisted_6$$ = { class: "form-switch col-sm-7 pt-2 ps-3" };
    const _hoisted_7$U = { class: "col-form-label" };


    var script$21 = {
      __name: 'modal-add-device-exclusion',
      props: {
        body: String,
        title: String,
        footer: String,
        list_notes: String,
    },
      emits: ['add'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const input_mac_list = ref("");
    const input_trigger_alerts = ref("");

    const modal_id = ref(null);
    const emit = __emit;

    const showed = () => {};

    const show = () => {
        input_mac_list.value = "";
        modal_id.value.show();
    };

    const add_ = () => {
        emit('add', { mac_list: input_mac_list.value, trigger_alerts: input_trigger_alerts.value });
        close();
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[2] || (_cache[2] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$1S, [
            createBaseVNode("label", _hoisted_2$1F, toDisplayString(__props.body), 1 /* TEXT */),
            createBaseVNode("div", _hoisted_3$1v, [
              withDirectives(createBaseVNode("textarea", {
                name: "items-list",
                id: "itemslist-textarea",
                class: "w-100 form-control",
                rows: "6",
                "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((input_mac_list).value = $event))
              }, null, 512 /* NEED_PATCH */), [
                [vModelText, input_mac_list.value]
              ]),
              createBaseVNode("small", null, toDisplayString(__props.list_notes), 1 /* TEXT */),
              _cache[3] || (_cache[3] = createBaseVNode("div", { class: "invalid-feedback" }, null, -1 /* CACHED */))
            ])
          ]),
          createBaseVNode("div", _hoisted_4$1s, [
            createBaseVNode("label", _hoisted_5$1d, toDisplayString(_i18n('edit_check.trigger_device_disconnected_alert')), 1 /* TEXT */),
            createBaseVNode("div", _hoisted_6$$, [
              withDirectives(createBaseVNode("input", {
                type: "checkbox",
                class: "form-check-input ms-0",
                "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((input_trigger_alerts).value = $event))
              }, null, 512 /* NEED_PATCH */), [
                [vModelCheckbox, input_trigger_alerts.value]
              ])
            ]),
            createBaseVNode("small", _hoisted_7$U, toDisplayString(_i18n('edit_check.trigger_device_disconnected_alert_descr')), 1 /* TEXT */)
          ])
        ]),
        footer: withCtx(() => [
          createTextVNode(toDisplayString(_i18n('host_details.notes')) + ": " + toDisplayString(__props.footer) + " ", 1 /* TEXT */),
          createBaseVNode("button", {
            type: "button",
            onClick: add_,
            class: "btn btn-primary"
          }, toDisplayString(_i18n('add')), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$21.__file = "http_src/vue/modal-add-device-exclusion.vue";

    const _hoisted_1$1R = {
      key: 0,
      class: "form-group mb-3 row"
    };
    const _hoisted_2$1E = { class: "col-form-label col-sm-4" };
    const _hoisted_3$1u = { class: "col-sm-7" };
    const _hoisted_4$1r = ["placeholder"];
    const _hoisted_5$1c = { class: "form-group mb-3 row" };
    const _hoisted_6$_ = { class: "col-form-label col-sm-4" };
    const _hoisted_7$T = { class: "col-sm-7" };
    const _hoisted_8$K = { class: "form-group mb-3 row" };
    const _hoisted_9$C = { class: "col-form-label col-sm-4 pt-2" };
    const _hoisted_10$x = { class: "form-switch col-sm-7 pt-2 ps-3" };
    const _hoisted_11$u = { class: "col-form-label" };



    var script$20 = {
      __name: 'modal-edit-device-exclusion',
      props: {
      title: String,
      title_edit_all: String,
    },
      emits: ['edit'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const input_mac_address_name = ref("");
    const input_device_status = ref("");
    const input_trigger_alerts = ref("");

    const custom_name_placeholder = ref(i18n('if_stats_config.custom_name'));
    const modal_id = ref(null);
    const emit = __emit;

    const showed = () => { };


    const _i18n = (t) => i18n(t);
    const device_status_list = ref([
      { id: "allowed", value: "allowed", label: _i18n('edit_check.authorized') },
      { id: "denied", value: "denied", label: _i18n('edit_check.unauthorized') },
    ]);

    const edit_all = ref(false);

    const show = (row) => {
      if (row != null) {
        let tmp_device_status = null;
        device_status_list.value.forEach((item) => {
          if (item.id == row.status) {
            tmp_device_status = item;
          }
        });
        input_device_status.value = tmp_device_status;
        input_mac_address_name.value = row.mac_address.mac;
        input_trigger_alerts.value = row.trigger_alert || false;
        edit_all.value = false;
      } else {
        input_device_status.value = device_status_list.value[0];
        edit_all.value = true;
      }

      modal_id.value.show();
    };

    const edit_ = () => {
      if (edit_all.value == false)
        emit('edit', { mac_alias: input_mac_address_name.value, mac_status: input_device_status.value.value, trigger_alerts: input_trigger_alerts.value });
      else
        emit('edit', { mac_status: input_device_status.value.value, trigger_alerts: input_trigger_alerts.value, mac_alias: 'all', });

      close();
    };

    const close = () => {
      modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[3] || (_cache[3] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          (edit_all.value == false)
            ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
              ], 64 /* STABLE_FRAGMENT */))
            : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                createTextVNode(toDisplayString(__props.title_edit_all), 1 /* TEXT */)
              ], 64 /* STABLE_FRAGMENT */))
        ]),
        body: withCtx(() => [
          (edit_all.value == false)
            ? (openBlock(), createElementBlock("div", _hoisted_1$1R, [
                createBaseVNode("label", _hoisted_2$1E, toDisplayString(_i18n('edit_check.device_alias')), 1 /* TEXT */),
                createBaseVNode("div", _hoisted_3$1u, [
                  withDirectives(createBaseVNode("input", {
                    type: "text",
                    name: "custom_name",
                    class: "form-control",
                    placeholder: custom_name_placeholder.value,
                    "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((input_mac_address_name).value = $event))
                  }, null, 8 /* PROPS */, _hoisted_4$1r), [
                    [vModelText, input_mac_address_name.value]
                  ])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", _hoisted_5$1c, [
            createBaseVNode("label", _hoisted_6$_, toDisplayString(_i18n('edit_check.device_status')), 1 /* TEXT */),
            createBaseVNode("div", _hoisted_7$T, [
              createVNode(script$2H, {
                selected_option: input_device_status.value,
                "onUpdate:selected_option": _cache[1] || (_cache[1] = $event => ((input_device_status).value = $event)),
                options: device_status_list.value
              }, null, 8 /* PROPS */, ["selected_option", "options"])
            ])
          ]),
          createBaseVNode("div", _hoisted_8$K, [
            createBaseVNode("label", _hoisted_9$C, toDisplayString(_i18n('edit_check.trigger_device_disconnected_alert')), 1 /* TEXT */),
            createBaseVNode("div", _hoisted_10$x, [
              withDirectives(createBaseVNode("input", {
                type: "checkbox",
                class: "form-check-input ms-0",
                "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((input_trigger_alerts).value = $event))
              }, null, 512 /* NEED_PATCH */), [
                [vModelCheckbox, input_trigger_alerts.value]
              ])
            ]),
            createBaseVNode("small", _hoisted_11$u, toDisplayString(_i18n('edit_check.trigger_device_disconnected_alert_descr')), 1 /* TEXT */)
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: edit_,
            class: "btn btn-primary"
          }, toDisplayString(_i18n('edit')), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$20.__file = "http_src/vue/modal-edit-device-exclusion.vue";

    /*
      (C) 2013-23 - ntop.org
     */

    /* ******************************************************************** */

    function format_num_for_sort(num) {
        if (typeof num === "number") {
            /* Check if it's a number */
            return num;
        } else if (typeof num === "string") {
            if (num == "") {
                /* Safety check */
                return 0;
            }

            /* If it's a string convert it into a number */
            num = num.split(',').join("");
            num = parseInt(num);
        } else {
            /* In case both failed, convert num to 0 */
            num = 0;
        }

        return num;
    }

    // max number value for sort number with normalize option 
    // for cases: last scan and last duration column to handle empty values
    const MAX_NUMBER_VALUE = 99999999999;

    const normalize_number_value = function (lower_value, val, sort) {
        if (val == lower_value) {
            if (sort == 1) {
                val = MAX_NUMBER_VALUE;
            }
        }
        return val;
    };
    /* ******************************************************************** */

    const sortByName = function (val_1, val_2, sort) {
        // Convert the values into number, to see if they are numbers or not
        const num1 = Number(val_1);
        const num2 = Number(val_2);

        const isNumeric1 = !isNaN(num1) && val_1 !== null && val_1 !== '';
        const isNumeric2 = !isNaN(num2) && val_2 !== null && val_2 !== '';

        /* Both are numbers */
        if (isNumeric1 && isNumeric2) {
            return sortByNumber(num1, num2, sort);
        }

        /* At least a string */
        const str1 = (val_1 ?? '').toString();
        const str2 = (val_2 ?? '').toString();

        if (sort === 1) {
            if (!str1) return -1;
            if (!str2) return 1;
            return str1.localeCompare(str2, undefined, { numeric: true, sensitivity: 'base' });
        }

        if (!str1) return 1;
        if (!str2) return -1;
        return str2.localeCompare(str1, undefined, { numeric: true, sensitivity: 'base' });
    };

    /* ******************************************************************** */

    /* Sort by IP Addresses */
    const sortByIP = function (val_1, val_2, sort) {
        val_1 = String(NtopUtils.convertIPAddress(val_1));
        val_2 = String(NtopUtils.convertIPAddress(val_2));
        if (sort == 1) {
            return val_1.localeCompare(val_2);
        }
        return val_2.localeCompare(val_1);
    };

    /* ******************************************************************** */

    /* Sort by MAC Addresses */
    const sortByMacAddress = function (val_1, val_2, sort) {
        val_1 = NtopUtils.convertMACAddress(val_1);
        val_2 = NtopUtils.convertMACAddress(val_2);
        if (sort == 1) {
            return val_1.localeCompare(val_2);
        }
        return val_2.localeCompare(val_1);
    };

    /* ******************************************************************** */

    /* Sort by Number */
    const sortByNumber = function (val_1, val_2, sort) {
        /* It's an array */
        val_1 = format_num_for_sort(val_1);
        val_2 = format_num_for_sort(val_2);

        if (sort == 1) {
            return val_1 - val_2;
        }
        return val_2 - val_1;
    };

    /* ******************************************************************** */

    /* Sort by Number after values normalization */
    const sortByNumberWithNormalizationValue = function (val_1, val_2, sort, lower_value) {
        val_1 = normalize_number_value(lower_value, val_1, sort);
        val_2 = normalize_number_value(lower_value, val_2, sort);

        return sortByNumber(val_1, val_2, sort);
    };

    /* ******************************************************************** */

    const sortingFunctions$1 = function () {
        return {
            sortByIP,
            sortByName,
            sortByNumber,
            sortByMacAddress,
            sortByNumberWithNormalizationValue,
        };
    }();

    const _hoisted_1$1Q = { class: "row" };
    const _hoisted_2$1D = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1t = { class: "card" };
    const _hoisted_4$1q = { class: "card-body" };
    const _hoisted_5$1b = {
      key: 0,
      class: "alert alert-info"
    };
    const _hoisted_6$Z = { id: "table_devices_vue" };
    const _hoisted_7$S = { class: "card-footer mt-3" };


    var script$1$ = {
      __name: 'page-device-exclusions',
      props: {
        context: Object
    },
      setup(__props) {

    const table_device_exclusions = ref();
    const modal_delete_confirm = ref();
    const modal_delete_all = ref();
    const modal_add_device = ref();
    const modal_edit_device = ref();

    const table_id = ref('device_exclusions');

    const add_url = `${http_prefix}/lua/pro/rest/v2/add/device/exclusion.lua`;
    const delete_url = `${http_prefix}/lua/pro/rest/v2/delete/device/exclusion.lua`;
    const edit_url = `${http_prefix}/lua/pro/rest/v2/edit/device/exclusion.lua`;
    const learning_status_url = `${http_prefix}/lua/pro/rest/v2/get/device/learning_status.lua`;
    const is_learning_status = ref(false);
    const _i18n = (t) => i18n(t);

    let title_delete = '';
    let body_delete = '';
    let title_delete_all = _i18n('edit_check.delete_all_device_exclusions');
    let body_delete_all = _i18n('edit_check.delete_all_device_exclusions_message');
    let title_add = _i18n('edit_check.add_device_exclusion');
    let body_add = _i18n('edit_check.add_device_exclusion_message');
    let footer_add = _i18n('edit_check.add_device_exclusion_notes');
    let list_notes_add = _i18n('edit_check.add_device_exclusion_list_notes');
    let title_edit = _i18n('edit_check.edit_device_exclusion');
    let title_edit_all = _i18n('edit_check.edit_all_devices_status');
    let learning_message = _i18n('edit_check.learning');
    let row_to_delete = ref(null);
    let row_to_edit = ref(null);

    const props = __props;


    const rest_params = {
        csrf: props.context.csrf,
        ifid: props.context.ifid
    };

    const notes_list = [
        _i18n("edit_check.device_exclusion_page_notes.note_1")
    ];

    const sub_notes_list = [
        _i18n("edit_check.device_exclusion_page_notes.sub_note_1"),
        _i18n("edit_check.device_exclusion_page_notes.sub_note_2")
    ];

    /* ******************************************************************** */

    /* Function to handle all buttons */
    function on_table_custom_event(event) {

        let events_managed = {
            "click_button_edit_device": click_button_edit_device,
            "click_button_historical_flows": click_button_historical_flows,
            "click_button_delete": click_button_delete,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    async function click_button_delete(event) {
        let body = `${i18n('edit_check.delete_device_exclusion')} ${event.row.mac_address.mac}`;
        row_to_delete.value = event.row;

        body_delete = body;

        title_delete = i18n('edit_check.device_exclusion');
        modal_delete_confirm.value.show(body_delete, title_delete);

    }

    async function click_button_edit_device(event) {
        row_to_edit.value = event.row;
        modal_edit_device.value.show(row_to_edit.value);
    }

    function click_button_historical_flows(event) {
        const rowData = event.row;
        const url = `${http_prefix}/lua/pro/db_search.lua?epoch_begin=${rowData.first_seen.timestamp}&epoch_end=${rowData.last_seen.timestamp}&mac=${rowData.mac_address.mac};eq&aggregated=false`;
        window.open(url, '_blank');
    }

    onMounted(async () => {

        await learning_status();

    });

    const csrf = props.crsf;

    /* Function to delete device */
    const delete_row = async function () {
        const row = row_to_delete.value;
        const delete_params = {
            csrf: props.context.csrf,
            ifid: props.context.ifid,
            device: row.mac_address.mac
        };
        await ntopng_utility.http_post_request(delete_url, delete_params);
        refresh();
    };

    const delete_all_confirm = async function () {
        modal_delete_all.value.show();
    };

    const edit_all_devices_confirm = async function () {
        modal_edit_device.value.show();
    };

    /* Function to delete all devices */
    const delete_all = async function () {
        const url = NtopUtils.buildURL(delete_url, {
            device: 'all',
        });

        await ntopng_utility.http_post_request(url, rest_params);
        refresh();

    };

    const learning_status = async function () {

        const rsp = await ntopng_utility.http_request(learning_status_url);
        if (rsp.learning_done) {
            is_learning_status.value = false;
        } else {
            is_learning_status.value = true;
        }
    };

    const refresh = async function () {
        await learning_status();
        table_device_exclusions.value.refresh_table();
    };

    function add_device() {
        modal_add_device.value.show();
    }

    const add_device_rest = async function (set_params_in_url) {
        let params = set_params_in_url;
        params.mac_list = params.mac_list.replace(/(?:\t| )/g, '');
        params.mac_list = params.mac_list.replace(/(?:\r\n|\r|\n)/g, ',');

        const url = NtopUtils.buildURL(add_url, {
            ...params
        });

        await ntopng_utility.http_post_request(url, rest_params);
        refresh();

    };

    const edit_row = async function (params) {
        let edit_params = {
            csrf: props.context.csrf,
            ifid: props.context.ifid,
            mac_status: params.mac_status,
            trigger_alerts: params.trigger_alerts ? "1" : "0"
        };
        let row = row_to_edit.value;
        if (row != null)
            edit_params.mac_alias = params.mac_alias.replace(/(?:\t| )/g, '');
        if (row != null)
            edit_params.mac = row.mac_address.mac;
        await ntopng_utility.http_post_request(edit_url, edit_params);

        refresh();
    };


    function columns_sorting(col, r0, r1) {
        if (col != null) {
            let r0_col = r0[col.data.data_field];
            let r1_col = r1[col.data.data_field];
            if (col.id == "last_ip") {
                if (r0_col != '') {
                    r0_col = take_ip(r0_col);
                    r0_col = NtopUtils.convertIPAddress(r0_col);
                }
                if (r1_col != '') {
                    r1_col = take_ip(r1_col);
                    r1_col = NtopUtils.convertIPAddress(r1_col);
                }
                if (col.sort == 1) {
                    return r0_col.localeCompare(r1_col);
                }
                return r1_col.localeCompare(r0_col);
            } else if (col.id == "manufacturer") {
                if (r0_col === undefined) r0_col = '';
                if (r1_col === undefined) r1_col = '';
                if (col.sort == 1) {
                    return r0_col.localeCompare(r1_col);
                }
                return r1_col.localeCompare(r0_col);
            } else if (col.id == "mac_address") {
                r0_col = r0_col.mac;
                r1_col = r1_col.mac;
                if (col.sort == 1) {
                    return r0_col.localeCompare(r1_col);
                }
                return r1_col.localeCompare(r0_col);
            } else if (col.id == "first_seen") {
                r0_col = r0["first_seen"]["timestamp"] == 0 ? 0 : r0["first_seen"]["timestamp"];
                r1_col = r1["first_seen"]["timestamp"] == 0 ? 0 : r1["first_seen"]["timestamp"];
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort)
            } else if (col.id == "last_seen") {
                r0_col = r0["last_seen"]["timestamp"] == 0 ? 0 : r0["last_seen"]["timestamp"];
                r1_col = r1["last_seen"]["timestamp"] == 0 ? 0 : r1["last_seen"]["timestamp"];
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort)
            } else if (col.id == "status") {
                if (col.sort == 1) {
                    return r0_col.localeCompare(r1_col);
                }
                return r1_col.localeCompare(r0_col);
            } else if (col.id == "trigger_alert") {
                r0_col = format_bool(r0_col);
                r1_col = format_bool(r1_col);

                if (col.sort == 1) {
                    return r0_col.localeCompare(r1_col);
                }
                return r1_col.localeCompare(r0_col);
            }
        }

    }

    function take_ip(r_col) {
        const ip = r_col.split('host=')[1].split("\'")[0];
        return ip;
    }

    function format_bool(r_col) {
        if (r_col) {
            return 'true';
        }

        if (!r_col) {
            return 'false';
        }

        if (r_col == 'true') {
            return r_col;
        }

        if (r_col == 'false') {
            return r_col;
        }
    }

    const map_table_def_columns = async (columns) => {

        let map_columns = {
            "mac_address": (data, row) => {
                let label = data.mac;
                let alias = data.alias;

                if ((data.symbolic_mac) && (data.symbolic_mac != label))
                    label = data.symbolic_mac;

                if ((alias != null) && (alias != label))
                    label = `${label} (${alias})`;

                if (data.url != null)
                    label = `<a href='${data.url}' title='${data.mac}'>${label}</a>`;

                let badge = `<span class="badge bg-secondary">${i18n('asset_details.offline')}</span>`;

                if (row.last_seen.is_online) {
                    badge = `<span class="badge bg-success">${i18n('asset_details.online')}</span>`;
                }

                return `${label} ${badge}`;
            },
            "first_seen": (first_seen, row) => {
                if (first_seen.timestamp == 0) {
                    return '';
                } else {
                    return formatterUtils.formatDateTime(first_seen.timestamp)
                }
            },
            "last_seen": (last_seen, row) => {
                if (last_seen.timestamp == 0) {
                    return '';
                } else {
                    return formatterUtils.formatDateTime(last_seen.timestamp)
                }
            },
            "status": (status, row) => {
                //<span class="badge bg-success" title="${label}">${label}</span>
                //<span class="badge bg-danger" title="${label}">${label}</span>
                //const label = _i18n(status);
                let label = "";
                if (status == "allowed") {
                    label = _i18n("edit_check.authorized");
                    return `<span class="badge bg-success" title="${label}">${label}</span>`
                } else {
                    label = _i18n("edit_check.unauthorized");
                    return `<span class="badge bg-danger" title="${label}">${label}</span>`
                }

            },
            "trigger_alert": (trigger_alert, row) => {
                let is_enabled = false;
                if (trigger_alert == "false")
                    is_enabled = false;
                else
                    is_enabled = trigger_alert;
                return is_enabled ? `<i class="fas fa-check text-success"></i>` : `<i class="fas fa-times text-danger"></i>`;
            }
        };
        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];

            /*if (c.id == "actions") {
                    
              c.button_def_array.forEach((b) => {
                  
                b.f_map_class = (current_class, row) => { 
                  current_class = current_class.filter((class_item) => class_item != "disabled");
                  if((row.is_ok_last_scan == 4 || row.is_ok_last_scan == null || row.num_open_ports < 1) && visible_dict[b.id]) {
                    current_class.push("disabled"); 
                  }
                  return current_class;
                }
              });
            }*/
        });
        // console.log(columns);
        return columns;
    };

    const get_extra_params_obj = () => {
        /*let params = get_url_params(active_page, per_page, columns_wrap, map_search, first_get_rows);
        set_params_in_url(params);*/
        let params = get_url_params();
        return params;
    };

    function get_url_params() {
        let actual_params = {
            ifid: ntopng_url_manager.get_url_entry("ifid") || props.context.ifid,
        };

        return actual_params;
    }

    const map_config = (config) => {
        return config;
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1Q, [
        createBaseVNode("div", _hoisted_2$1D, [
          _cache[3] || (_cache[3] = createBaseVNode("div", {
            class: "alert alert-danger d-none",
            id: "alert-row-buttons",
            role: "alert"
          }, null, -1 /* CACHED */)),
          createBaseVNode("div", _hoisted_3$1t, [
            createBaseVNode("div", _hoisted_4$1q, [
              (is_learning_status.value)
                ? (openBlock(), createElementBlock("div", _hoisted_5$1b, toDisplayString(unref(learning_message)), 1 /* TEXT */))
                : createCommentVNode("v-if", true),
              createBaseVNode("div", _hoisted_6$Z, [
                createVNode(script$2a, {
                  ref_key: "modal_delete_confirm",
                  ref: modal_delete_confirm,
                  title: unref(title_delete),
                  body: unref(body_delete),
                  onDelete: delete_row
                }, null, 8 /* PROPS */, ["title", "body"]),
                createVNode(script$2a, {
                  ref_key: "modal_delete_all",
                  ref: modal_delete_all,
                  title: unref(title_delete_all),
                  body: unref(body_delete_all),
                  onDelete: delete_all
                }, null, 8 /* PROPS */, ["title", "body"]),
                createVNode(script$21, {
                  ref_key: "modal_add_device",
                  ref: modal_add_device,
                  title: unref(title_add),
                  body: unref(body_add),
                  footer: unref(footer_add),
                  list_notes: unref(list_notes_add),
                  onAdd: add_device_rest
                }, null, 8 /* PROPS */, ["title", "body", "footer", "list_notes"]),
                createVNode(script$20, {
                  ref_key: "modal_edit_device",
                  ref: modal_edit_device,
                  title: unref(title_edit),
                  title_edit_all: unref(title_edit_all),
                  onEdit: edit_row
                }, null, 8 /* PROPS */, ["title", "title_edit_all"]),
                createVNode(script$2w, {
                  ref_key: "table_device_exclusions",
                  ref: table_device_exclusions,
                  csrf: unref(csrf),
                  table_id: table_id.value,
                  f_map_columns: map_table_def_columns,
                  get_extra_params_obj: get_extra_params_obj,
                  f_map_config: map_config,
                  f_sort_rows: columns_sorting,
                  onCustom_event: on_table_custom_event
                }, {
                  custom_header: withCtx(() => [
                    createBaseVNode("button", {
                      class: "btn btn-link",
                      type: "button",
                      ref: "add_device",
                      onClick: add_device
                    }, [...(_cache[0] || (_cache[0] = [
                      createBaseVNode("i", { class: "fas fa-plus" }, null, -1 /* CACHED */)
                    ]))], 512 /* NEED_PATCH */)
                  ]),
                  _: 1 /* STABLE */
                }, 8 /* PROPS */, ["csrf", "table_id"])
              ])
            ]),
            createBaseVNode("div", _hoisted_7$S, [
              createBaseVNode("button", {
                type: "button",
                onClick: delete_all_confirm,
                class: "btn btn-danger me-1"
              }, [
                _cache[1] || (_cache[1] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString(_i18n("edit_check.delete_all_device_exclusions")), 1 /* TEXT */)
              ]),
              createBaseVNode("button", {
                type: "button",
                onClick: edit_all_devices_confirm,
                class: "btn btn-secondary"
              }, [
                _cache[2] || (_cache[2] = createBaseVNode("i", { class: "fas fa-edit" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString(_i18n("edit_check.edit_all_devices_status")), 1 /* TEXT */)
              ])
            ])
          ]),
          createVNode(script$2u, {
            note_list: notes_list,
            add_sub_notes: true,
            sub_note_list: sub_notes_list
          })
        ])
      ]))
    }
    }

    };

    script$1$.__file = "http_src/vue/page-device-exclusions.vue";

    var script$1_ = {
      components: {	  
        'datatable': script$2e,
      },
      props: {
        page_csrf: String,
        url_params: Object,
      },
      /**
       * First method called when the component is created.
       */
      created() {
        start_datatable$2(this);
      },
      mounted() {},    
      data() {
        return {
          i18n: (t) => i18n(t),
          config_devices_standard: null,
          config_devices_centrality: null,
        };
      },
      methods: { 
        /* Method used to switch active table tab */
        reload_table: function() {
          let table = this.get_active_table();
          table.reload();
        },
        get_active_table: function() {
          return this.$refs[`table_fingerprint`];
        },
      },
    };  

    function start_datatable$2(DatatableVue) {
      const datatableButton = [];
      let columns = [];
      let default_sorting_columns = 3 /* Contacts column */;
      
      /* Manage the buttons close to the search box */
      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function (e, dt, node, config) {
          DatatableVue.reload_table();
        }
      });
      
      let defaultDatatableConfig = {
        table_buttons: datatableButton,
        columns_config: [],
        data_url: NtopUtils.buildURL(`${http_prefix}/lua/rest/v2/get/host/fingerprint/data.lua`, url_params),
        enable_search: true,
      };

      /* Standard table configuration */  

      columns = [
        {
          columnName: i18n("client_fingerprint_type"), name: 'type', data: 'type', className: 'text-nowrap', render: (data, type) => {
            if (data == 'JA3')
              return `<a class="ntopng-external-link" href="https://github.com/salesforce/ja3">${data} <i class="fas fa-external-link-alt"></i></a>`;
            else
              return `<a class="ntopng-external-link" href="https://github.com/FoxIO-LLC/ja4">${data} <i class="fas fa-external-link-alt"></i></a>`;

          }, responsivePriority: 0, createdCell: DataTableRenders.applyCellStyle },
        { columnName: i18n("client_fingerprint"), name: 'hash', data: 'hash', className: 'text-nowrap', render: (data, type, rowData) => {
          if (rowData.type == 'JA3')
            return `<a class="ntopng-external-link" style="max-width:315px" href="https://sslbl.abuse.ch/ja3-fingerprints/${data}">${data} <i class="fas fa-external-link-alt"></i></a>`;
          else
            return data;
          }, responsivePriority: 0, createdCell: DataTableRenders.applyCellStyle },
        { columnName: i18n("status"), name: 'is_malicious', data: 'is_malicious', className: 'text-nowrap text-center', responsivePriority: 0, render: (data, type) => {
            return (data ? `<i class="fa-solid fa-face-frown text-danger" title="${i18n('malicious')}"></i>` : `<i class="fa-solid fa-face-smile text-success" title="${i18n('ok')}"></i>`);
          }
        },
        { columnName: i18n("app_name"), name: 'app_name', data: 'app_name', className: 'text-nowrap text-right', responsivePriority: 1 },
        { columnName: i18n("num_uses"), name: 'num_uses', data: 'num_uses', className: 'text-nowrap text-right', responsivePriority: 1, render: (data) => { return NtopUtils.formatValue(data); } },
      ];

      /* Extra table configuration */
      let table_config = {
        serverSide: false,
        order: [[ default_sorting_columns, 'desc' ]]
      };
      
      let configDevices = ntopng_utility.clone(defaultDatatableConfig);
      configDevices.table_buttons = defaultDatatableConfig.table_buttons;
      configDevices.data_url = `${configDevices.data_url}`;
      configDevices.columns_config = columns;
      configDevices.table_config = ntopng_utility.clone(table_config);
      DatatableVue.config_devices_standard = configDevices;
    }

    const _hoisted_1$1P = { class: "row" };
    const _hoisted_2$1C = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1s = { class: "card" };
    const _hoisted_4$1p = { class: "card-body" };
    const _hoisted_5$1a = { id: "table_host_tls" };
    const _hoisted_6$Y = { class: "card-footer" };

    function render$4(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_datatable = resolveComponent("datatable");

      return (openBlock(), createElementBlock("div", _hoisted_1$1P, [
        createBaseVNode("div", _hoisted_2$1C, [
          _cache[0] || (_cache[0] = createBaseVNode("div", {
            class: "alert alert-danger d-none",
            id: "alert-row-buttons",
            role: "alert"
          }, null, -1 /* CACHED */)),
          createBaseVNode("div", _hoisted_3$1s, [
            createBaseVNode("div", _hoisted_4$1p, [
              createBaseVNode("div", _hoisted_5$1a, [
                createVNode(_component_datatable, {
                  ref: "table_fingerprint",
                  table_buttons: $data.config_devices_standard.table_buttons,
                  columns_config: $data.config_devices_standard.columns_config,
                  data_url: $data.config_devices_standard.data_url,
                  enable_search: $data.config_devices_standard.enable_search,
                  table_config: $data.config_devices_standard.table_config
                }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "table_config"])
              ])
            ]),
            createBaseVNode("div", _hoisted_6$Y, [
              createBaseVNode("b", null, toDisplayString($data.i18n('fingerprint_note')), 1 /* TEXT */)
            ])
          ])
        ])
      ]))
    }

    script$1_.render = render$4;
    script$1_.__file = "http_src/vue/page-host-tls.vue";

    var script$1Z = {
      components: {	  
        'datatable': script$2e,
      },
      props: {
        page_csrf: String,
        url_params: Object,
      },
      /**
       * First method called when the component is created.
       */
      created() {
        start_datatable$1(this);
      },
      mounted() {},    
      data() {
        return {
          i18n: (t) => i18n(t),
          config_devices_standard: null,
          config_devices_centrality: null,
        };
      },
      methods: { 
        /* Method used to switch active table tab */
        reload_table: function() {
          let table = this.get_active_table();
          table.reload();
        },
        get_active_table: function() {
          return this.$refs[`table_hassh`];
        },
      },
    };  

    function start_datatable$1(DatatableVue) {
      const datatableButton = [];
      let columns = [];
      let default_sorting_columns = 3 /* Contacts column */;
      
      /* Manage the buttons close to the search box */
      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function (e, dt, node, config) {
          DatatableVue.reload_table();
        }
      });
      
      let defaultDatatableConfig = {
        table_buttons: datatableButton,
        columns_config: [],
        data_url: NtopUtils.buildURL(`${http_prefix}/lua/rest/v2/get/host/fingerprint/data.lua`, url_params),
        enable_search: true,
      };

      /* Standard table configuration */  

      columns = [
        { columnName: i18n("hassh_fingerprint"), name: 'ja3', data: 'ja3', className: 'text-nowrap', render: (data, type) => {
            return `<a class="ntopng-external-link" href="https://sslbl.abuse.ch/ja3-fingerprints/${data}">${data} <i class="fas fa-external-link-alt"></i></a>`;
          }, responsivePriority: 0, createdCell: DataTableRenders.applyCellStyle },
        { columnName: i18n("status"), name: 'is_malicious', data: 'is_malicious', className: 'text-nowrap text-center', responsivePriority: 0, render: (data, type) => {
            return (data ? `<i class="fa-solid fa-face-frown text-danger" title="${i18n('malicious')}"></i>` : `<i class="fa-solid fa-face-smile text-success" title="${i18n('ok')}"></i>`);
          }
        },
        { columnName: i18n("app_name"), name: 'app_name', data: 'app_name', className: 'text-nowrap text-right', responsivePriority: 1 },
        { columnName: i18n("num_uses"), name: 'num_uses', data: 'num_uses', className: 'text-nowrap text-right', responsivePriority: 1, render: (data) => { return NtopUtils.formatValue(data); } },
      ];

      /* Extra table configuration */
      let table_config = {
        serverSide: false,
        order: [[ default_sorting_columns, 'desc' ]]
      };
      
      let configDevices = ntopng_utility.clone(defaultDatatableConfig);
      configDevices.table_buttons = defaultDatatableConfig.table_buttons;
      configDevices.data_url = `${configDevices.data_url}`;
      configDevices.columns_config = columns;
      configDevices.table_config = ntopng_utility.clone(table_config);
      DatatableVue.config_devices_standard = configDevices;
    }

    const _hoisted_1$1O = { class: "row" };
    const _hoisted_2$1B = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1r = { class: "card" };
    const _hoisted_4$1o = { class: "card-body" };
    const _hoisted_5$19 = { id: "table_host_ssh" };

    function render$3(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_datatable = resolveComponent("datatable");

      return (openBlock(), createElementBlock("div", _hoisted_1$1O, [
        createBaseVNode("div", _hoisted_2$1B, [
          _cache[0] || (_cache[0] = createBaseVNode("div", {
            class: "alert alert-danger d-none",
            id: "alert-row-buttons",
            role: "alert"
          }, null, -1 /* CACHED */)),
          createBaseVNode("div", _hoisted_3$1r, [
            createBaseVNode("div", _hoisted_4$1o, [
              createBaseVNode("div", _hoisted_5$19, [
                createVNode(_component_datatable, {
                  ref: "table_hassh",
                  table_buttons: $data.config_devices_standard.table_buttons,
                  columns_config: $data.config_devices_standard.columns_config,
                  data_url: $data.config_devices_standard.data_url,
                  enable_search: $data.config_devices_standard.enable_search,
                  table_config: $data.config_devices_standard.table_config
                }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "table_config"])
              ])
            ])
          ])
        ])
      ]))
    }

    script$1Z.render = render$3;
    script$1Z.__file = "http_src/vue/page-host-ssh.vue";

    const change_map_event = "change_map_event";

      var script$1Y = {
        components: {	  
          'page-periodicity-map': script$23,
          'page-periodicity-table': script$28,
          'page-asset-map': script$22,
          'page-asset-table': script$29,
          'page-service-map': script$24,
          'page-service-table': script$27,
          'page-navbar': script$2K,
        },
        props: {
          page_csrf: String,
          base_url_params: Object,
          ifid: Number,
          is_admin: Boolean,
          map_id: String,
          view: String,
          navbar_info: Object,
          service_acceptance: Array,
          service_map_filter_list: Object,
          service_table_filter_list: Array,
          periodicity_map_filter_list: Object,
          periodicity_table_filter_list: Array,
          asset_map_filter_list: Object,
          asset_table_filter_list: Array,
        },
        /**
         * First method called when the component is created.
         */
        created() {
          this.url_params = this.$props.base_url_params;
          this.active_tab = this.$props.map_id;
          this.page = this.url_params.page;
          this.updated_view = this.$props.view;

          if(asset_map_filter_list && asset_table_filter_list) {
            this.navbar_context.items_table.push({ active: false, class: "disabled ps-0 pe-0", label: '-' });
            this.navbar_context.items_table.push({ active: false, icon: "fas fa-lg fa-compass", class: "disabled pe-0", title: i18n('asset_map'), label: i18n('asset') + ": " });
            this.navbar_context.items_table.push({ active: false, label: i18n('map_format'), id: "asset_map", page: "graph" });
            this.navbar_context.items_table.push({ active: false, class: "disabled ps-0 pe-0", label: '/' });
            this.navbar_context.items_table.push({ active: false, label: i18n('table_format'), id: "asset_map", page: "table" });
          }

          this.navbar_context.items_table.forEach((i) => {
            (i.id == this.active_tab && i.page == this.page) ? i.active = true : i.active = false;
          });
        },
        mounted() {
          
          const format_navbar = this.format_navbar_title;
          format_navbar(this.$props.navbar_info);

          ntopng_events_manager.on_custom_event("page_navbar", ntopng_custom_events.CHANGE_PAGE_TITLE, (node) => {
            format_navbar({ selected_iface: this.$props.navbar_info.selected_iface, selected_host: node });
          });

          ntopng_events_manager.on_custom_event("change_service_table_tab", change_map_event, (tab) => {
            ntopng_url_manager$1.set_key_to_url('map', tab.id);
            ntopng_url_manager$1.set_key_to_url('page', tab.page);
            if(tab.page == 'table')
              this.destroy();
            
            this.active_tab = tab.id;
            this.page = tab.page;
            this.url_params.map = tab.id;
            this.url_params.page = tab.page;
            this.updated_view = ntopng_url_manager$1.get_url_entry('view');
            format_navbar();
         });
        },    
        data() {
          return {
            i18n: (t) => i18n(t),
            active_tab: null,
            page: null,
            url_params: {},
            updated_view: null,
            navbar_node: {},
            navbar_context: {
              main_title: {
                label: ' ' + i18n("maps"),
                icon: "fas fa-map",
              },
              secondary_title_list: [],
              items_table: [
                { active: false, icon: "fas fa-lg fa-concierge-bell", class: "disabled pe-0", title: i18n('service_map'), label: i18n('service') + ": " },
                { active: true, label: i18n('map_format'), id: "service_map", page: "graph" },
                { active: false, class: "disabled ps-0 pe-0", label: '/' },
                { active: false, label: i18n('table_format'), id: "service_map", page: "table" },
                { active: false, class: "disabled ps-0 pe-0", label: '-' },
                { active: false, icon: "fas fa-lg fa-clock", class: "disabled pe-0", title: i18n('periodicity_map'), label: i18n('periodicity') + ": " },
                { active: false, label: i18n('map_format'), id: "periodicity_map", page: "graph" },
                { active: false, class: "disabled ps-0 pe-0", label: '/' },
                { active: false, label: i18n('table_format'), id: "periodicity_map", page: "table" },
              ],
            },
          };
        },
        methods: { 
          destroy: function() {
            let current_tab = this.get_active_tab();
            current_tab.destroy();
          },
          format_navbar_title: function(data) {
            if(data) {
              this.navbar_node = data; /* Update navbar node */
            } else {
              data = this.navbar_node; /* Use the old navbar node */
            } 

            this.navbar_context.secondary_title_list = [
              { label: data.selected_iface.label, title: NtopUtils.shortenLabel(`${data.selected_iface.label}`, 16) }
            ];

            if(data.selected_host && data.selected_host.id != '') {
              const host_name = data.selected_host.label;
              const ip = NtopUtils.removeVlan(data.selected_host.id);
              this.navbar_context.secondary_title_list[0]['href'] = `${http_prefix}/lua/pro/enterprise/network_maps.lua?map=${this.active_tab}&page=${this.page}&ifid=${this.$props.ifid}`;
              this.navbar_context.secondary_title_list.push({
                label: `${NtopUtils.formatNameValue(host_name, ip)}`, // Adding the laptop icon
                title: `${data.selected_host.label}`,
                href: data.selected_host.is_active ? `${http_prefix}/lua/host_details.lua?host=${ip}` : null,
                icon: "fa-solid fa-laptop",
                target_blank: "true",
              });
            }  
          },
          get_active_tab: function() {
            return this.$refs[this.active_tab + "_" + this.page];
          },
          /* Method used to switch active table tab */
          click_item: function(item) {
            if(item.id != this.active_tab || item.page != this.page) {
              this.navbar_context.items_table.forEach((i) => i.active = false);
              item.active = true;
              ntopng_events_manager.emit_custom_event(change_map_event, item);
            }
          },
        },
      };

    function render$2(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_page_navbar = resolveComponent("page-navbar");
      const _component_page_service_map = resolveComponent("page-service-map");
      const _component_page_service_table = resolveComponent("page-service-table");
      const _component_page_periodicity_map = resolveComponent("page-periodicity-map");
      const _component_page_periodicity_table = resolveComponent("page-periodicity-table");
      const _component_page_asset_map = resolveComponent("page-asset-map");
      const _component_page_asset_table = resolveComponent("page-asset-table");

      return (openBlock(), createElementBlock(Fragment, null, [
        createVNode(_component_page_navbar, {
          id: "page_navbar",
          main_title: $data.navbar_context.main_title,
          secondary_title_list: $data.navbar_context.secondary_title_list,
          help_link: $data.navbar_context.help_link,
          items_table: $data.navbar_context.items_table,
          onClick_item: $options.click_item
        }, null, 8 /* PROPS */, ["main_title", "secondary_title_list", "help_link", "items_table", "onClick_item"]),
        ($data.active_tab == 'service_map' && $data.page == 'graph')
          ? (openBlock(), createBlock(_component_page_service_map, {
              key: 0,
              ref: "service_map_graph",
              page_csrf: $props.page_csrf,
              url_params: $data.url_params,
              ifid: $props.ifid,
              is_admin: $props.is_admin,
              map_id: $props.map_id,
              all_filter_list: $props.service_map_filter_list
            }, null, 8 /* PROPS */, ["page_csrf", "url_params", "ifid", "is_admin", "map_id", "all_filter_list"]))
          : createCommentVNode("v-if", true),
        ($data.active_tab == 'service_map' && $data.page == 'table')
          ? (openBlock(), createBlock(_component_page_service_table, {
              key: 1,
              ref: "service_map_table",
              page_csrf: $props.page_csrf,
              url_params: $data.url_params,
              view: $data.updated_view,
              table_filters: $props.service_table_filter_list,
              is_admin: $props.is_admin,
              service_acceptance: $props.service_acceptance
            }, null, 8 /* PROPS */, ["page_csrf", "url_params", "view", "table_filters", "is_admin", "service_acceptance"]))
          : createCommentVNode("v-if", true),
        ($data.active_tab == 'periodicity_map' && $data.page == 'graph')
          ? (openBlock(), createBlock(_component_page_periodicity_map, {
              key: 2,
              ref: "periodicity_map_graph",
              page_csrf: $props.page_csrf,
              url_params: $data.url_params,
              ifid: $props.ifid,
              is_admin: $props.is_admin,
              map_id: $props.map_id,
              all_filter_list: $props.periodicity_map_filter_list
            }, null, 8 /* PROPS */, ["page_csrf", "url_params", "ifid", "is_admin", "map_id", "all_filter_list"]))
          : createCommentVNode("v-if", true),
        ($data.active_tab == 'periodicity_map' && $data.page == 'table')
          ? (openBlock(), createBlock(_component_page_periodicity_table, {
              key: 3,
              ref: "periodicity_map_table",
              page_csrf: $props.page_csrf,
              url_params: $data.url_params,
              view: $data.updated_view,
              table_filters: $props.periodicity_table_filter_list,
              is_admin: $props.is_admin
            }, null, 8 /* PROPS */, ["page_csrf", "url_params", "view", "table_filters", "is_admin"]))
          : createCommentVNode("v-if", true),
        ($props.asset_map_filter_list && $props.asset_table_filter_list)
          ? (openBlock(), createElementBlock(Fragment, { key: 4 }, [
              ($data.active_tab == 'asset_map' && $data.page == 'graph')
                ? (openBlock(), createBlock(_component_page_asset_map, {
                    key: 0,
                    ref: "asset_map_graph",
                    page_csrf: $props.page_csrf,
                    url_params: $data.url_params,
                    ifid: $props.ifid,
                    is_admin: $props.is_admin,
                    map_id: $props.map_id,
                    all_filter_list: $props.asset_map_filter_list
                  }, null, 8 /* PROPS */, ["page_csrf", "url_params", "ifid", "is_admin", "map_id", "all_filter_list"]))
                : createCommentVNode("v-if", true),
              ($data.active_tab == 'asset_map' && $data.page == 'table')
                ? (openBlock(), createBlock(_component_page_asset_table, {
                    key: 1,
                    ref: "asset_map_table",
                    page_csrf: $props.page_csrf,
                    url_params: $data.url_params,
                    view: $data.updated_view,
                    table_filters: $props.asset_table_filter_list
                  }, null, 8 /* PROPS */, ["page_csrf", "url_params", "view", "table_filters"]))
                : createCommentVNode("v-if", true)
            ], 64 /* STABLE_FRAGMENT */))
          : createCommentVNode("v-if", true)
      ], 64 /* STABLE_FRAGMENT */))
    }

    script$1Y.render = render$2;
    script$1Y.__file = "http_src/vue/page-home-map.vue";

    const _hoisted_1$1N = ["width", "height"];


    var script$1X = {
      __name: 'page-sankey',
      props: {
        width: Number,
        height: Number,
    },
      setup(__props) {

    const d3 = d3v7;

    const props = __props;

    const sankey_chart_ref = ref(null);
    const sankey_size = ref({});

    onBeforeMount(async() => {});

    onMounted(async () => {    
        draw_sankey();
    });

    async function draw_sankey() {
        const colors = d3.scaleOrdinal(d3.schemeCategory10);
        let data = await get_sankey_data();
        const size = get_size();
        sankey_size.value = size;
        const { links, nodes } = calculate_sankey(data, size.width - 10, size.height - 5);
        
        d3.select(sankey_chart_ref.value)
    	.select("g.nodes")
    	.selectAll("rect")
    	.data(nodes)
    	.join(
                (enter) => {
    		const e = enter.append("rect");
    		
    		e.attr("x", (d) => d.x0).attr("y", (d) => d.y0);
    		
    		e.transition(d3.easeLinear)
    		    .delay(1000)
    		    .duration(500)
    		    .attr("height", (d) => d.y1 - d.y0)
    		    .attr("width", (d) => d.x1 - d.x0)
    		    .attr("dataIndex", (d) => d.index)
    		    .attr("fill", (d) => colors(d.index / nodes.length));
    		e.append("title").text((d) => `${d.name}\n${d.hours}`);
                },
                (update) =>
    		update
    		.transition(d3.easeLinear)
    		.delay(500)
    		.duration(500)
    		.attr("x", (d) => d.x0)
    		.attr("y", (d) => d.y0)
    		.attr("height", (d) => d.y1 - d.y0)
    		.attr("width", (d) => d.x1 - d.x0)
    		.attr("dataIndex", (d) => d.index)
    		.attr("fill", (d) => colors(d.index / nodes.length))
    		.select("title")
    		.text((d) => `${d.name}\n${d.hours}`),
                (exit) =>
    		exit.transition(d3.easeLinear).duration(500).style("opacity", 0).remove()
    	).on("dblclick", function(data) {
    	    //todo portare fuori
    	    // data = data.currentTarget.__data__
    	    // const sourceLink = data.sourceLinks;
    	    // const targetLink = data.targetLinks;
    	    // const link = (sourceLink && sourceLink[0]) ? sourceLink[0] : targetLink[0];
    	    
    	    // if(link) {
    	    //   /* Get the node link from the rest */
    	    //   if(link.source.id === data.id) {
    	    //     if(link.source_link && link.source_link !== '')
    	    //       window.open(link.source_link, '_blank');
    	    //   } else if(link.target.id === data.id) {
    	    //     if(link.target_link && link.target_link !== '')
    	    //       window.open(link.target_link, '_blank');
    	    //   } 
    	    // } 
    	});
        
        d3.select(sankey_chart_ref.value)
    	.select("g.texts")
    	.selectAll("text")
    	.data(nodes)
    	.join(
                (enter) => {
    		const e = enter.append("text");
    		
    		e.transition(d3.easeLinear)
    		    .delay(1000)
    		    .duration(500)
    		    .attr("x", (d) => (d.x0 < size.width / 2 ? d.x1 + 6 : d.x0 - 6))
    		    .attr("y", (d) => (d.y1 + d.y0) / 2)
    		    .attr("fill", (d) => d3.rgb(colors(d.index / nodes.length)).darker())
    		    .attr("alignment-baseline", "middle")
    		    .attr("text-anchor", (d) =>
    			  d.x0 < size.width / 2 ? "start" : "end"
    			 )
    		    .attr("font-size", 9)
    		    .text((d) => d.name);
                },
                (update) =>
    		update
    		.transition(d3.easeLinear)
    		.delay(500)
    		.duration(500)
    		.attr("x", (d) => (d.x0 < size.width / 2 ? d.x1 + 6 : d.x0 - 6))
    		.attr("y", (d) => (d.y1 + d.y0) / 2)
    		.attr("fill", (d) => d3.rgb(colors(d.index / nodes.length)).darker())
    		.attr("text-anchor", (d) =>
    		      d.x0 < size.width / 2 ? "start" : "end"
    		     )
    		.attr("font-size", 9)
    		.text((d) => d.name),
                (exit) =>
    		exit
    		.transition(d3.easeLinear)
                /* .delay(500) */
    		.duration(500)
    		.style("opacity", 0)
    		.remove()
    	);
        
        d3.select(sankey_chart_ref.value)
    	.select("defs")
    	.selectAll("linearGradient")
    	.data(links)
    	.join(
                (enter) => {
    		const lg = enter.append("linearGradient");
    		
    		lg.attr("id", (d) => `gradient-${d.index}`)
    		    .attr("gradientUnits", "userSpaceOnUse")
    		    .attr("x1", (d) => d.source.x1)
    		    .attr("x2", (d) => d.target.x0);
    		
    		lg.append("stop")
    		    .attr("offset", "0")
    		    .attr("stop-color", (d) => colors(d.source.index / nodes.length));
    		
    		lg.append("stop")
    		    .attr("offset", "100%")
    		    .attr("stop-color", (d) => colors(d.target.index / nodes.length));
                },
                (update) => {
    		update
    		    .attr("id", (d) => `gradient-${d.index}`)
    		    .attr("gradientUnits", "userSpaceOnUse")
    		    .attr("x1", (d) => d.source.x1)
    		    .attr("x2", (d) => d.target.x0);
    		update.selectAll("stop").remove();
    		update
    		    .append("stop")
    		    .attr("offset", "0")
    		    .attr("stop-color", (d) => colors(d.source.index / nodes.length));
    		
    		update
    		    .append("stop")
    		    .attr("offset", "100%")
    		    .attr("stop-color", (d) => colors(d.target.index / nodes.length));
                },
                (exit) => exit.remove()
    	);
        
        d3.select(sankey_chart_ref.value)
    	.select("g.links")
    	.selectAll("path")
    	.data(links)
    	.join(
                (enter) => {
    		const e = enter.append("path");
    		e.transition(d3.easeLinear)
    		    .delay(1000)
    		    .duration(500)
    		    .attr("d", d3.sankeyLinkHorizontal())
    		    .attr("stroke", (d) => `url(#gradient-${d.index}`)
    		    .attr("stroke-width", (d) => d.width);
    		e.append("title").text((d) => `${d.hours}`);
                },
                (update) =>
    		update
    		.transition(d3.easeLinear)
    		.delay(500)
    		.duration(500)
    		.attr("d", d3.sankeyLinkHorizontal())
    		.attr("stroke", (d) => `url(#gradient-${d.index}`)
    		.attr("stroke-width", (d) => d.width)
    		.select("title")
    		.text((d) => `${d.hours}`),
                (exit) =>
    		exit
    		.transition(d3.easeLinear)
                /* .delay(1000) */
    		.duration(500)
    		.style("opacity", 0)
    		.remove()
    	);
    }

    async function get_sankey_data() {
        const rsp = [
    	{
    	    "link_color": "#e377c2",
    	    "source_color": "#e377c2",
    	    "source_link": "/lua/host_details.lua?page=flows&host=192.168.1.7&vlan=0&application=IGMP",
    	    "target": "224.0.0.251",
    	    "source": "IGMP",
    	    "link": "IGMP",
    	    "target_link": "/lua/host_details.lua?host=224.0.0.251&vlan=0",
    	    "target_node": "224.0.0.251",
    	    "source_node": "192.168.1.7",
    	    "value": 60
    	},
    	{
    	    "link_color": "#e377c2",
    	    "source_link": "/lua/host_details.lua?host=192.168.1.7&vlan=0",
    	    "target": "IGMP",
    	    "source": "192.168.1.7",
    	    "link": "IGMP",
    	    "target_link": "/lua/host_details.lua?page=flows&host=192.168.1.7&vlan=0&application=IGMP",
    	    "target_node": "224.0.0.2",
    	    "target_color": "#e377c2",
    	    "source_node": "192.168.1.7",
    	    "value": 120
    	},
    	{
    	    "link_color": "#e377c2",
    	    "source_color": "#e377c2",
    	    "source_link": "/lua/host_details.lua?page=flows&host=192.168.1.7&vlan=0&application=IGMP",
    	    "target": "224.0.0.2",
    	    "source": "IGMP",
    	    "link": "IGMP",
    	    "target_link": "/lua/host_details.lua?host=224.0.0.2&vlan=0",
    	    "target_node": "224.0.0.2",
    	    "source_node": "192.168.1.7",
    	    "value": 60
    	},
    	{
    	    "link_color": "#bcbd22",
    	    "source_link": "/lua/host_details.lua?host=192.168.1.7&vlan=0",
    	    "target": "MDNS",
    	    "source": "192.168.1.7",
    	    "link": "MDNS",
    	    "target_link": "/lua/host_details.lua?page=flows&host=192.168.1.7&vlan=0&application=MDNS",
    	    "target_node": "224.0.0.251",
    	    "target_color": "#bcbd22",
    	    "source_node": "192.168.1.7",
    	    "value": 396
    	},
    	{
    	    "link_color": "#bcbd22",
    	    "source_color": "#bcbd22",
    	    "source_link": "/lua/host_details.lua?page=flows&host=192.168.1.7&vlan=0&application=MDNS",
    	    "target": "224.0.0.251",
    	    "source": "MDNS",
    	    "link": "MDNS",
    	    "target_link": "/lua/host_details.lua?host=224.0.0.251&vlan=0",
    	    "target_node": "224.0.0.251",
    	    "source_node": "192.168.1.7",
    	    "value": 396
    	}
        ];
        
    /*
        let data = {
    	// nodes: [
    	//     { index: 0, name: "Liikevaihto", value: 100, hours: "100%" },
    	//     { index: 1, name: "Kiinteät kulut", value: 75, hours: "85%" },
    	//     { index: 2, name: "Muuttuvat kulut", value: 10, hours: "3:00" },
    	//     { index: 3, name: "Palkkakulut", value: 69, hours: "1:20" },
    	//     { index: 4, name: "Muut kiinte", value: 6, hours: "1:40" },
    	//     { index: 5, name: "Kate", value: 15, hours: "1:40" }
    	// ],
    	nodes: [
    	    { index: 0, name: "Liikevaihto", hours: "100%" },
    	    { index: 1, name: "Kiinteät kulut", hours: "85%" },
    	    { index: 2, name: "Muuttuvat kulut", hours: "3:00" },
    	    { index: 3, name: "Palkkakulut", hours: "1:20" },
    	    { index: 4, name: "Muut kiinte", hours: "1:40" },
    	    { index: 5, name: "Kate", hours: "1:40" }
    	],
    	links: [
    	    { source: 0, target: 1, value: 75, hours: "+1:00" },
    	    { source: 0, target: 2, value: 10, hours: "+2:00" },
    	    { source: 1, target: 3, value: 69, hours: "+1:20" },
    	    { source: 1, target: 4, value: 6, hours: "+1:40" },
    	    { source: 0, target: 5, value: 15, hours: "+1:40" }
    	]
        };
    */
        data = wrap_graph_rsp(rsp);

        //debugger;
        return data;
    }

    function wrap_graph_rsp(rsp) {
        let nodes = [];
        let links = [];

        let nodes_added_dict = {};
        const f_add_node = (node_id, href, color) => {
    	if (nodes_added_dict[node_id] != null) { return; }
    	let index = nodes.length;
    	nodes_added_dict[node_id] = index;
    	let new_node = { index, name: node_id, href, color };
    	nodes.push(new_node);
        };
        const f_add_link = (source, target, value, label) => {
    	const source_index = nodes_added_dict[source];
    	const target_index = nodes_added_dict[target];
    	let new_link = { source: source_index, target: target_index, value, label };
    	links.push(new_link);
        };
        rsp.forEach((el) => {
    	f_add_node(el.source, el.source_link, el.source_color);
    	f_add_node(el.target, el.target_link, el.target_color);
    	f_add_link(el.source, el.target, el.value, el.link);
        });
        return { nodes, links };
    }

    function get_size() {
        let width = props.width;
        if (width == null) { width = window.innerWidth - 200; }
        let height = props.height;
        if (height == null) { height = window.innerHeight - 50; }

        return { width, height };
    }

    function calculate_sankey(data, width, height) {
        const sankeyimpl = d3.sankey()
    	  .nodeAlign(d3.sankeyCenter)
    	  .nodeWidth(10)
    	  .nodePadding(10)
    	  .extent([
    	      [0, 5],
    	      [width, height]
    	  ]);
        
        return sankeyimpl(data);
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", null, [
        (openBlock(), createElementBlock("svg", {
          ref_key: "sankey_chart_ref",
          ref: sankey_chart_ref,
          width: sankey_size.value.width,
          height: sankey_size.value.height,
          style: {"margin":"10px"}
        }, [...(_cache[0] || (_cache[0] = [
          createBaseVNode("defs", null, null, -1 /* CACHED */),
          createBaseVNode("g", {
            class: "nodes",
            style: {"stroke":"#000","strokeOpacity":"0.5"}
          }, null, -1 /* CACHED */),
          createBaseVNode("g", {
            class: "links",
            style: {"stroke":"#000","strokeOpacity":"0.3","fill":"none"}
          }, null, -1 /* CACHED */),
          createBaseVNode("g", { class: "texts" }, null, -1 /* CACHED */)
        ]))], 8 /* PROPS */, _hoisted_1$1N))
      ]))
    }
    }

    };

    var css_248z$o = "\n.node rect {\n  fill-opacity: 0.9;\n  shape-rendering: crispEdges;\n}\n.node text {\n  pointer-events: none;\n  text-shadow: 0 1px 0 #fff;\n}\n.link {\n  fill: none;\n  stroke: #000;\n  stroke-opacity: 0.2;\n}\n.link:hover {\n  stroke-opacity: 0.5;\n}\n";
    styleInject(css_248z$o);

    script$1X.__file = "http_src/vue/page-sankey.vue";

    const change_applications_tab_event = "change_applications_tab_event";

    var script$1W = {
      components: {	  
        'chart': script$2I,
        'datatable': script$2e,
        'modal-delete-confirm': script$2a,
        'tab-list': script$2b,
      },
      props: {
        page_csrf: String,
        url_params: Object,
        view: String,
        is_ch_enabled: Boolean,
        is_locale: String,
        ts_l7_enabled: Boolean,
        ts_cat_enabled: Boolean
      },
      /**
       * First method called when the component is created.
       */
      created() {
        this.applications_tab = ntopng_url_manager$1.get_url_entry("view") || this.$props.view;
        this.tab_list.forEach((i) => {
          this.applications_tab == i.id ? i.active = true : i.active = false;
        });
        start_datatable(this);
      },
      mounted() {
        ntopng_events_manager$1.on_custom_event("change_applications_tab_event", change_applications_tab_event, (tab) => {
    	    let table = this.get_active_table();
          ntopng_url_manager$1.set_key_to_url('view', tab.id);
          table.destroy_table();
          this.applications_tab = tab.id;
        });
      },    
      data() {
        return {
          i18n: (t) => i18n(t),
          applications_tab: null,
          config_devices_applications: null,
          config_devices_categories: null,
          chart_options: [
            {
              title: i18n('graphs.top_10_ndpi_protocols'),
              type: ntopChartApex.typeChart.DONUT,
              url: `${http_prefix}/lua/rest/v2/get/host/l7/proto_data.lua`,
              tab: `applications`,
              id: `top_applications`,
            },
            {
              title: i18n('graphs.top_breed'),
              type: ntopChartApex.typeChart.DONUT,
              url: `${http_prefix}/lua/rest/v2/get/host/l7/breed_data.lua`,
              tab: `applications`,
              id: `top_breed`,
            },
            {
              title: i18n('graphs.top_10_ndpi_categories'),
              type: ntopChartApex.typeChart.DONUT,
              url: `${http_prefix}/lua/rest/v2/get/host/l7/cat_data.lua`,
              tab: `categories`,
              id: `top_categories`,
            },
            {
              title: i18n('graphs.top_breed'),
              type: ntopChartApex.typeChart.DONUT,
              url: `${http_prefix}/lua/rest/v2/get/host/l7/breed_data.lua`,
              tab: `categories`,
              id: `top_breed`,
            },
          ],
          tab_list: [
            { 
              title: i18n('host_details.applications_tab'),
              active: (this.$props.view == 'applications'),
              id: 'applications'
            },
            { 
              title: i18n('host_details.categories_tab'),
              active: (this.$props.view == 'categories'),
              id: 'categories'
            },
          ]
        };
      },
      methods: {
        add_action_column: function(columns, name, value) {
          const host = `${this.$props.url_params.host}`;
          const vlan = `${this.$props.url_params.vlan}`;
          const ifid = `${this.$props.url_params.ifid}`;
          let handlerId = "page-stats-action-jump-historical";
          columns.push({ columnName: i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, handlerId, render: (data, type, service) => {
            const jump_to_historical = {
              handlerId,
              onClick: () => {
                let url = `${http_prefix}/lua/pro/db_search.lua?ifid=${ifid}&${name}=${service[value].id};eq&ip=${host};eq`;
                if(vlan != 0)
                  url = `${url}&vlan_id=${vlan};eq`;
                window.open(url);
              }
            };
            return DataTableUtils.createActionButtons([{ class: 'dropdown-item', href: '#', title: i18n('db_explorer.historical_data'), handler: jump_to_historical }])
          }});
        },
        destroy: function() {
          let table = this.get_active_table();
          table.destroy_table();
        },
        /* Method used to switch active table tab */
        click_item: function(item) {
          this.tab_list.forEach((i) => i.active = false);
          item.active = true;
          ntopng_events_manager$1.emit_custom_event(change_applications_tab_event, item);
        }, 
        reload_table: function() {
          let table = this.get_active_table();
          NtopUtils.showOverlays();
          table.reload();
          NtopUtils.hideOverlays();
        },
        get_active_table: function() {
          return this.$refs[`table_host_${this.applications_tab}`];
        },
        get_f_get_custom_chart_options() {
          console.log("get_f_");
          return async (url) => {
            return charts_options_items.value[chart_index].chart_options;
          }
        }
      },
    };  

    function start_datatable(PageVue) {
      const datatableButton = [];

      /* Manage the buttons close to the search box */
      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function (e, dt, node, config) {
          PageVue.reload_table();
        }
      });
      
      let tmp_params = url_params;
      tmp_params['view'] = 'applications';
      
      let defaultDatatableConfig = {
        table_buttons: datatableButton,
        data_url: NtopUtils.buildURL(`${http_prefix}/lua/rest/v2/get/host/l7/data.lua`, tmp_params),
        enable_search: true,
        table_config: { 
          serverSide: false, 
          order: [[ 6 /* percentage column */, 'desc' ]],
          columnDefs: [
            { type: "time-uni", targets: 1 },
            { type: "file-size", targets: 2 },
            { type: "file-size", targets: 3 },
            { type: "file-size", targets: 5 },
          ]
        }
      };
      
      /* Applications table configuration */  

      let columns = [
        { columnName: i18n("host_details.application"), targets: 0, width: '20', name: 'application', data: 'application', className: 'text-nowrap', responsivePriority: 1, render: (data) => {
          if (PageVue.$props.is_locale == "1" && PageVue.$props.ts_l7_enabled === true) {
            return `<a href="${http_prefix}/lua/host_details.lua?host=${PageVue.$props.url_params.host}@${PageVue.$props.url_params.vlan}&page=historical&ifid=${PageVue.$props.url_params.ifid}&protocol=${data.label}&ts_schema=host:ndpi" target="_blank">${data.label}</a>`
          } else
            return `${data.label}`;
          } 
        },
        { columnName: i18n("host_details.duration"), targets: 1, width: '10', name: 'duration', data: 'duration', className: 'text-nowrap', responsivePriority: 1, render: (data) => {
            return NtopUtils.secondsToTime(data);
          }  
        },
        { columnName: i18n("host_details.sent"), targets: 2, width: '10', name: 'sent', data: 'bytes_sent', className: 'text-nowrap', responsivePriority: 2, render: (data) => {
            return NtopUtils.bytesToSize(data);
          }  
        },
        { columnName: i18n("host_details.rcvd"), targets: 3, width: '10', name: 'rcvd', data: 'bytes_rcvd',  className: 'text-center text-nowrap', responsivePriority: 2, render: (data) => {
            return NtopUtils.bytesToSize(data);
          }  
        },
        { columnName: i18n("host_details.breakdown"), targets: 4, width: '10', name: 'breakdown', data: 'breakdown', orderable: false, className: 'text-center text-nowrap', responsivePriority: 2, render: (data, type, row) => {
            const percentage_sent = (row.bytes_sent * 100) / row.tot_bytes;
            const percentage_rcvd = (row.bytes_rcvd * 100) / row.tot_bytes;
            return NtopUtils.createBreakdown(percentage_sent, percentage_rcvd, i18n('host_details.sent'), i18n('host_details.rcvd'));
          }  
        },
        { columnName: i18n("host_details.tot_bytes"), targets: 5, width: '20', name: 'tot_bytes', data: 'tot_bytes', className: 'text-center text-nowrap', responsivePriority: 2, render: (data) => {
            return NtopUtils.bytesToSize(data);
          }   
        },
        { columnName: i18n("host_details.tot_percentage"), targets: 6, width: '20', name: 'percentage', data: 'percentage',  className: 'text-center text-nowrap', responsivePriority: 2, render: (data) => {
            const percentage = data.toFixed(1);
            return NtopUtils.createProgressBar(percentage);
          }  
        },
      ];

      if(is_ch_enabled)
        PageVue.add_action_column(columns, 'l7proto', 'application');
      
      let applicationsConfig = ntopng_utility.clone(defaultDatatableConfig);
      applicationsConfig.columns_config = columns;
      PageVue.config_devices_applications = applicationsConfig;


      /* Categories table configuration */

      tmp_params['view'] = 'categories';  
      defaultDatatableConfig.data_url = NtopUtils.buildURL(`${http_prefix}/lua/rest/v2/get/host/l7/data.lua`, tmp_params);


      columns = [
        { columnName: i18n("host_details.category"), targets: 0, name: 'category', data: 'category', className: 'text-nowrap', responsivePriority: 1, render: (data) => {
          if (PageVue.$props.is_locale == "1" && PageVue.$props.ts_cat_enabled === true)
            return `<a href="${http_prefix}/lua/host_details.lua?host=${PageVue.$props.url_params.host}@${PageVue.$props.url_params.vlan}&ts_schema=host:ndpi_categories&page=historical&category=${data.label}" target="_blank">${data.label}</a>`
          else
            return `${data.label}`;
          } 
        },
        { columnName: i18n("host_details.applications"), targets: 0, name: 'applications', data: 'applications', orderable: false, className: 'text-nowrap', responsivePriority: 1, render: (data) => {
            if(PageVue.$props.is_locale == "1")
              return `${data.label || ''} <a href="${http_prefix}/${data.href}${data.category_id}">${data.more_protos || ''}</a>`
            else
              return `${data.label || ''}`;
          } 
        },
        { columnName: i18n("host_details.duration"), targets: 0, name: 'duration', data: 'duration', className: 'text-nowrap', responsivePriority: 1, render: (data) => {
            return NtopUtils.secondsToTime(data);
          }  
        },
        { columnName: i18n("host_details.tot_bytes"), targets: 0, name: 'tot_bytes', data: 'tot_bytes', className: 'text-center text-nowrap', responsivePriority: 2, render: (data) => {
            return NtopUtils.bytesToSize(data);
          }  
        },
        { columnName: i18n("host_details.tot_percentage"), targets: 0, name: 'percentage', data: 'percentage', className: 'text-center text-nowrap', responsivePriority: 2, render: (data) => {
            const percentage = data.toFixed(1);
            return NtopUtils.createProgressBar(percentage);
          }  
        },
      ];

      if(is_ch_enabled)
        PageVue.add_action_column(columns, 'l7cat', 'category');
      
      let categoriesConfig = ntopng_utility.clone(defaultDatatableConfig);
      categoriesConfig.columns_config = columns;
      categoriesConfig.table_config.order = [[ 4 /* percentage column */, 'desc' ]];
      categoriesConfig.table_config.columnDefs = [
        { type: "time-uni", targets: 2 },
        { type: "file-size", targets: 3 },
      ];
      
      PageVue.config_devices_categories = categoriesConfig;
    }

    const _hoisted_1$1M = { class: "row" };
    const _hoisted_2$1A = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1q = { class: "card" };
    const _hoisted_4$1n = { class: "card-body" };
    const _hoisted_5$18 = { id: "host_details_applications" };
    const _hoisted_6$X = {
      class: "row mb-4 mt-4",
      id: "host_details_applications"
    };
    const _hoisted_7$R = { class: "col-6" };
    const _hoisted_8$J = {
      key: 0,
      class: "widget-name"
    };

    function render$1(_ctx, _cache, $props, $setup, $data, $options) {
      const _component_tab_list = resolveComponent("tab-list");
      const _component_chart = resolveComponent("chart");
      const _component_datatable = resolveComponent("datatable");

      return (openBlock(), createElementBlock("div", _hoisted_1$1M, [
        createBaseVNode("div", _hoisted_2$1A, [
          createBaseVNode("div", _hoisted_3$1q, [
            _cache[0] || (_cache[0] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1n, [
              createBaseVNode("div", _hoisted_5$18, [
                createVNode(_component_tab_list, {
                  ref: "host_details_applications_tab_list",
                  id: "host_details_applications_tab_list",
                  tab_list: $data.tab_list,
                  onClick_item: $options.click_item
                }, null, 8 /* PROPS */, ["tab_list", "onClick_item"]),
                createBaseVNode("div", _hoisted_6$X, [
                  (openBlock(true), createElementBlock(Fragment, null, renderList($data.chart_options, (chart_option) => {
                    return (openBlock(), createElementBlock("div", _hoisted_7$R, [
                      (chart_option.tab == $data.applications_tab)
                        ? (openBlock(), createElementBlock("h3", _hoisted_8$J, toDisplayString(chart_option.title), 1 /* TEXT */))
                        : createCommentVNode("v-if", true),
                      (chart_option.tab == $data.applications_tab)
                        ? (openBlock(), createBlock(_component_chart, {
                            key: 1,
                            id: chart_option.id,
                            chart_type: chart_option.type,
                            base_url_request: chart_option.url,
                            register_on_status_change: false
                          }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"]))
                        : createCommentVNode("v-if", true)
                    ]))
                  }), 256 /* UNKEYED_FRAGMENT */))
                ]),
                ($data.applications_tab == 'applications')
                  ? (openBlock(), createBlock(_component_datatable, {
                      key: 0,
                      ref: "table_host_applications",
                      table_buttons: $data.config_devices_applications.table_buttons,
                      columns_config: $data.config_devices_applications.columns_config,
                      data_url: $data.config_devices_applications.data_url,
                      enable_search: $data.config_devices_applications.enable_search,
                      table_config: $data.config_devices_applications.table_config
                    }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "table_config"]))
                  : createCommentVNode("v-if", true),
                ($data.applications_tab == 'categories')
                  ? (openBlock(), createBlock(_component_datatable, {
                      key: 1,
                      ref: "table_host_categories",
                      table_buttons: $data.config_devices_categories.table_buttons,
                      columns_config: $data.config_devices_categories.columns_config,
                      data_url: $data.config_devices_categories.data_url,
                      enable_search: $data.config_devices_categories.enable_search,
                      table_config: $data.config_devices_categories.table_config
                    }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "table_config"]))
                  : createCommentVNode("v-if", true)
              ])
            ])
          ])
        ])
      ]))
    }

    script$1W.render = render$1;
    script$1W.__file = "http_src/vue/page-host-details-applications.vue";

    const _hoisted_1$1L = { class: "row" };
    const _hoisted_2$1z = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1p = { class: "card" };
    const _hoisted_4$1m = { class: "card-body" };
    const _hoisted_5$17 = { id: "host_details_traffic" };
    const _hoisted_6$W = {
      class: "row mb-4 mt-4",
      id: "host_details_traffic"
    };
    const _hoisted_7$Q = { class: "col-4" };
    const _hoisted_8$I = { class: "widget-name" };


    var script$1V = {
      __name: 'page-host-details-traffic',
      props: {
      page_csrf: String,
    },
      setup(__props) {

    const traffic_table = ref(null);
    ref([]);
    const config_traffic_table = ref({});

    const destroy = () => {
      traffic_table.value.destroy_table();
    };

    const reload_table = () => {
      traffic_table.value.reload();
    };
        
    onBeforeMount(async () => {
      start_datatable();
    });

    onUnmounted(async () => {
      destroy();
    });

    const chart_options = [
      {
        title: i18n('graphs.l4_proto'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/l4/proto_data.lua`,
        id: `traffic_protos`,
      },
      {
        title: i18n('graphs.contacted_hosts'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/l4/contacted_hosts_data.lua`,
        id: `contacted_hosts`,
      },
      {
        title: i18n('graphs.traffic'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/l4/traffic_data.lua`,
        id: `traffic`,
      },
    ];

    function start_datatable(PageVue) {
      const datatableButton = [];
      let url_params = {};
      
      url_params["host"] = ntopng_url_manager$1.get_url_entry("host");
      url_params["vlan"] = ntopng_url_manager$1.get_url_entry("vlan");
      url_params["ifid"] = ntopng_url_manager$1.get_url_entry("ifid");

      /* Manage the buttons close to the search box */
      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function (e, dt, node, config) {
          reload_table();
        }
      });
        
      let defaultDatatableConfig = {
        table_buttons: datatableButton,
        data_url: NtopUtils.buildURL(`${http_prefix}/lua/rest/v2/get/host/l4/data.lua`, url_params),
        enable_search: true,
        table_config: { 
          serverSide: false, 
          order: [[ 6 /* percentage column */, 'desc' ]],
          columnDefs: [
            { type: "time-uni", targets: 1 },
            { type: "file-size", targets: 2 },
            { type: "file-size", targets: 3 },
            { type: "file-size", targets: 5 },
          ]
        }
      };
      
      /* Applications table configuration */  

      let columns = [
        { columnName: i18n("protocol"), targets: 0, name: 'protocol', data: 'protocol', className: 'text-nowrap', responsivePriority: 1 },
        { columnName: i18n("chart"), targets: 1, name: 'historical', data: 'historical', className: 'text-nowrap', responsivePriority: 1 },
        { columnName: i18n("traffic_labels.bytes_sent"), targets: 2, name: 'sent', data: 'bytes_sent', className: 'text-nowrap', responsivePriority: 2, render: (data) => {
            return NtopUtils.bytesToSize(data);
          }  
        },
        { columnName: i18n("traffic_labels.bytes_rcvd"), targets: 3, name: 'rcvd', data: 'bytes_rcvd',  className: 'text-center text-nowrap', responsivePriority: 2, render: (data) => {
            return NtopUtils.bytesToSize(data);
          }  
        },
        { columnName: i18n("traffic_labels.breakdown"), targets: 4, name: 'breakdown', data: 'breakdown', orderable: false, className: 'text-center text-nowrap', responsivePriority: 2, render: (data, type, row) => {
            const percentage_sent = (row.bytes_sent * 100) / row.total_bytes;
            const percentage_rcvd = (row.bytes_rcvd * 100) / row.total_bytes;
            return NtopUtils.createBreakdown(percentage_sent, percentage_rcvd, i18n('host_details.sent'), i18n('host_details.rcvd'));
          }  
        },
        { columnName: i18n("traffic_labels.total_bytes"), targets: 5, name: 'tot_bytes', data: 'total_bytes', className: 'text-center text-nowrap', responsivePriority: 2, render: (data) => {
            return NtopUtils.bytesToSize(data);
          }   
        },
        { columnName: i18n("traffic_labels.total_percentage"), targets: 6, width: '20%', name: 'percentage', data: 'total_percentage',  className: 'text-center text-nowrap', responsivePriority: 2, render: (data) => {
            const percentage = data.toFixed(1);
            return NtopUtils.createProgressBar(percentage);
          }  
        },
      ];

      let trafficConfig = ntopng_utility.clone(defaultDatatableConfig);
      trafficConfig.columns_config = columns;
      config_traffic_table.value = trafficConfig;
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1L, [
        createBaseVNode("div", _hoisted_2$1z, [
          createBaseVNode("div", _hoisted_3$1p, [
            _cache[0] || (_cache[0] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1m, [
              createBaseVNode("div", _hoisted_5$17, [
                createBaseVNode("div", _hoisted_6$W, [
                  (openBlock(), createElementBlock(Fragment, null, renderList(chart_options, (chart_option) => {
                    return createBaseVNode("div", _hoisted_7$Q, [
                      createBaseVNode("h3", _hoisted_8$I, toDisplayString(chart_option.title), 1 /* TEXT */),
                      createVNode(script$2I, {
                        id: chart_option.id,
                        chart_type: chart_option.type,
                        base_url_request: chart_option.url,
                        register_on_status_change: false
                      }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"])
                    ])
                  }), 64 /* STABLE_FRAGMENT */))
                ]),
                createVNode(script$2e, {
                  ref_key: "traffic_table",
                  ref: traffic_table,
                  table_buttons: config_traffic_table.value.table_buttons,
                  columns_config: config_traffic_table.value.columns_config,
                  data_url: config_traffic_table.value.data_url,
                  enable_search: config_traffic_table.value.enable_search,
                  table_config: config_traffic_table.value.table_config
                }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "table_config"])
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1V.__file = "http_src/vue/page-host-details-traffic.vue";

    const _hoisted_1$1K = { class: "row" };
    const _hoisted_2$1y = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1o = { class: "card" };
    const _hoisted_4$1l = { class: "card-body" };
    const _hoisted_5$16 = { class: "row" };
    const _hoisted_6$V = { class: "col-6 mb-4 mt-4" };
    const _hoisted_7$P = { class: "widget-name" };


    var script$1U = {
      __name: 'page-host-details-packets',
      props: {
      page_csrf: String,
      url_params: Object,
    },
      setup(__props) {
    const chart_options = [
      {
        title: i18n('graphs.packets_sent'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/packets/sent_data.lua`,
        id: `packets_sent`,
      },
      {
        title: i18n('graphs.packets_rcvd'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/packets/rcvd_data.lua`,
        id: `packets_rcvd`,
      },
      {
        title: i18n('graphs.tcp_flags'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/packets/tcp_flags_data.lua`,
        id: `tcp_flags`,
      },
      {
        title: i18n('graphs.arp_distribution'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/packets/arp_data.lua`,
        id: `arp_requests`,
      },
    ];

    function chart_done(data, tmp, tmp2) {
      NtopUtils$1.hideOverlays();
    }

    onMounted(() => {});


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1K, [
        createBaseVNode("div", _hoisted_2$1y, [
          createBaseVNode("div", _hoisted_3$1o, [
            _cache[0] || (_cache[0] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1l, [
              createBaseVNode("div", _hoisted_5$16, [
                (openBlock(), createElementBlock(Fragment, null, renderList(chart_options, (chart_option) => {
                  return createBaseVNode("div", _hoisted_6$V, [
                    createBaseVNode("h3", _hoisted_7$P, toDisplayString(chart_option.title), 1 /* TEXT */),
                    createVNode(script$2I, {
                      id: chart_option.id,
                      chart_type: chart_option.type,
                      base_url_request: chart_option.url,
                      register_on_status_change: false,
                      onChart_reloaded: chart_done
                    }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"])
                  ])
                }), 64 /* STABLE_FRAGMENT */))
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1U.__file = "http_src/vue/page-host-details-packets.vue";

    const _hoisted_1$1J = {
      key: 0,
      class: "mb-2"
    };
    const _hoisted_2$1x = {
      class: "btn-group btn-ontop",
      role: "group"
    };
    const _hoisted_3$1n = ["title"];
    const _hoisted_4$1k = ["title"];
    const _hoisted_5$15 = ["title"];
    const _hoisted_6$U = {
      key: 1,
      class: "alert alert-info",
      id: "empty-message"
    };


    var script$1T = {
      __name: 'sankey',
      props: {
        no_data_message: String,
        width: Number,
        height: Number,
        sankey_data: Object,
        autorefresh: { type: Boolean, default: null }
    },
      emits: ['node_click', 'update_width', 'update_height', 'autorefresh_toggle'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const d3 = d3v7;
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    let eventsAttached = false;
    let resizeTimeout = null;

    function setNoDataFlag(set_no_data) {
        no_data.value = set_no_data;
    }

    const props = __props;

    const autoRefreshEnabled = ref(props.autorefresh ?? true);
    const showAutoRefresh = computed$1(() => props.autorefresh !== null);

    const sankey_size = ref({});
    const no_data = ref(false);
    const sankey_wrapper = ref(null);
    const sankey_div = ref(null);

    let svg = null;
    let zoomGroup = null;
    let sankey = null;
    let sankeyData = null;
    let isZoomed = ref(false);
    const currentScale = ref(1); // keep track of zzom

    let zoom = null;

    function toggleAutoRefresh() {
        autoRefreshEnabled.value = !autoRefreshEnabled.value;
        emit('autorefresh_toggle', autoRefreshEnabled.value);
    }

    /* ******************************************** */

    onBeforeMount(async () => { });

    /* ******************************************** */

    onMounted(async () => {
        await set_sankey_data();
    });

    /* ******************************************** */

    onBeforeUnmount(() => {
        if (svg) {
            // clean up all D3 elements
            svg.selectAll('*').remove();
            svg.on('dblclick', null);
            svg.on('mousemove', null);
            svg.on('mouseleave', null);
        }
        
        eventsAttached = false;
    });

    /* ******************************************** */

    watch(() => props.sankey_data, (cur_value, old_value) => {
        set_sankey_data(true);
    });

    /* ******************************************** */

    /* ZOOM FUNCTIONS */

    // zoomValue must be float, 1.0 is 100% zoom, 1.5 is 150% and so on
    function zoomChart(zoomValue) {
        if (!zoom) return;
        if (!svg) return; /* Check if the svg is defined */
        let newZoomValue = Math.max(currentScale.value + zoomValue, 1); // minimum 1x zoom
        currentScale.value = Math.min(newZoomValue, 3); // maximum 3x zoom

        // get svg center
        const width = sankey_size.value.width;
        const height = sankey_size.value.height;
        const centerX = width / 2;
        const centerY = height / 2;

        // compute transformation
        const transform = d3.zoomIdentity
            .translate(centerX, centerY)
            .scale(currentScale.value)
            .translate(-centerX, -centerY);

        // apply transformation to D3 zoom behavior and zoom group
        svg.transition()
            .duration(300)
            .call(zoom.transform, transform);

        zoomGroup.transition()
            .duration(300)
            .attr("transform", `translate(${transform.x}, ${transform.y}) scale(${transform.k})`);

        isZoomed.value = currentScale.value > 1;
    }

    /* ******************************************** */
    function initializeZoom() {
        if (!svg) return;
        zoomGroup = svg.select('.zoom-group');
        zoom = d3.zoom()
            .scaleExtent([1, 5]) // Max 500% zoom
            .on("zoom", (event) => {
                currentScale.value = event.transform.k;
                isZoomed.value = currentScale.value > 1;
                zoomGroup.attr("transform", event.transform);
            })
            .filter(event => {
                if (event.type === 'dblclick') {
                    event.preventDefault();
                    return false;
                }
                if (event.type === 'mousedown' && event.button !== 0) {
                    return false;
                }

                // don't pan if clicking on a draggable node
                if (event.type === 'mousedown' && event.target.closest('.sankey-node, .label')) {
                    return false;
                }

                // disable panning if not zoomed
                if (event.type === 'mousedown' && currentScale.value <= 1) {
                    return false;
                }

                return !event.ctrlKey && event.type !== 'dblclick';
            });

        // update cursor style based on zoom
        svg.on('mousedown', (event) => {

            if (event.button === 0 && currentScale.value > 1) { // left click and zoomed
                svg.style("cursor", `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' width='18px' height='18px'%3E%3Cpath d='M278.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-64 64c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l32 0 0 96-96 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6l0-32 96 0 0 96-32 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l64 64c12.5 12.5 32.8 12.5 45.3 0l64-64c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8l-32 0 0-96 96 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 32-96 0 0-96 32 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-64-64z'/%3E%3C/svg%3E"), move`);
            } else {
                svg.style("cursor", "default");
            }
        });

        // reset cursor on mouse up
        svg.on('mouseup', () => {
            if (currentScale.value > 1) {
                svg.style("cursor", "grab");
            } else {
                svg.style("cursor", "default");
            }
        });

        // add double click handler to reset zoom
        svg.on('dblclick', (event) => {
            event.preventDefault();
            resetZoom();
        });

        // init zoom
        svg.call(zoom)
            .call(zoom.transform, d3.zoomIdentity);
    }

    /* ******************************************** */

    function resetZoom() {
        if (!zoom) return;
        if (!svg) return; /* Check if the svg is created or not */
        svg.transition()
            .duration(750)
            .call(zoom.transform, d3.zoomIdentity);

        currentScale.value = 1;
        isZoomed.value = false;

        zoomGroup.attr("transform", "translate(0,0) scale(1)");
    }

    /* ******************************************** */

    async function set_sankey_data(reset) {
        if (reset && svg) {
            sankey_wrapper.value.replaceChildren();
        }
        if (props.sankey_data.nodes == null || props.sankey_data.links == null
            || props.sankey_data.length == 0 || props.sankey_data.links.length == 0) {
            setNoDataFlag(true); /* No data */
            return;
        }

        setNoDataFlag(false); /* There is some data */
        await draw_sankey();
        attach_events();
        initializeZoom();
    }

    /* ******************************************** */
    // attach resize event listener
    function attach_events() {
        if (eventsAttached) return;
        
        const handleResize = () => {
            clearTimeout(resizeTimeout);
            resizeTimeout = setTimeout(async () => {
                await set_sankey_data(true);
                initializeZoom();
            }, 150);
        };
        
        window.addEventListener('resize', handleResize);
        eventsAttached = true;
    }

    /* ******************************************** */

    function get_size() {
        emit('update_width');
        let width = props.width;
        if (width == undefined) { width = $(sankey_div.value).width(); }
        
        emit('update_height');
        let height = props.height;
        if (height == undefined) { height = $(sankey_div.value).height(); }

        return { width, height };
    }


    /* ******************************************** */
    function createNodeDrag(links, svg) {
        return d3.drag()
            .on("start", function(event, d) {
                d3.select(this).select("rect").style("fill-opacity", 1.0);

                // Store initial relative positions ONLY ONCE (on first drag)
                // These offsets should remain constant based on the original layout
                links.forEach(link => {
                    if (link.source === d && link.sourceY0Offset === undefined) {
                        // Store the relative offset from the top of the source node
                        link.sourceY0Offset = link.y0 - d.y0;
                    }
                    if (link.target === d && link.targetY1Offset === undefined) {
                        // Store the relative offset from the top of the target node
                        link.targetY1Offset = link.y1 - d.y0;
                    }
                });
            })
            .on("drag", function(event, d) {
                const nodeWidth = d.x1 - d.x0;
                const nodeHeight = d.y1 - d.y0;

                // get current zoom
                const transform = d3.zoomTransform(svg.node());
                const scale = transform.k;

                // get svg dimensions
                const svgRect = svg.node().getBoundingClientRect();
                const margin = { top: 8, right: 8, bottom: 8, left: 8 };

                // calculate visible bounds in the sankey coordinate space
                // The sankey extent is [0, 0] to [width - margins, height - margins]
                const sankeyWidth = sankey_size.value.width - margin.left - margin.right;
                const sankeyHeight = sankey_size.value.height - margin.top - margin.bottom;

                const visibleLeft = Math.max(0, -transform.x / scale);
                const visibleTop = Math.max(0, -transform.y / scale);
                const visibleRight = Math.min(sankeyWidth, visibleLeft + svgRect.width / scale);
                const visibleBottom = Math.min(sankeyHeight, visibleTop + svgRect.height / scale);

                // do not let the node be dragged out of view
                let constrainedX = Math.max(visibleLeft, Math.min(visibleRight - nodeWidth, event.x));
                let constrainedY = Math.max(visibleTop, Math.min(visibleBottom - nodeHeight, event.y));
                
                // update node bounds with constrained positions
                d.x0 = constrainedX;
                d.x1 = constrainedX + nodeWidth;
                d.y0 = constrainedY;
                d.y1 = constrainedY + nodeHeight;
                
                // Move the node group
                d3.select(this).attr("transform", `translate(${d.x0}, ${d.y0})`);

                // update links connected to dragged node, preserving relative positions
                links.forEach(link => {
                    if (link.source === d) {
                        // Update source link position preserving relative offset
                        link.y0 = d.y0 + (link.sourceY0Offset !== undefined ? link.sourceY0Offset : 0);
                    }
                    if (link.target === d) {
                        // Update target link position preserving relative offset
                        link.y1 = d.y0 + (link.targetY1Offset !== undefined ? link.targetY1Offset : 0);
                    }
                });
                
                // redraw all links
                svg.select("g.links").selectAll("path.sankey-link")
                    .attr("d", d3.sankeyLinkHorizontal());
                
                // update gradients for affected links only
                svg.select("g.links").selectAll("linearGradient")
                    .attr("x1", link => link.source.x1)
                    .attr("y1", link => link.y0)
                    .attr("x2", link => link.target.x0)
                    .attr("y2", link => link.y1);
            })
            .on("end", function(event, d) {
                d3.select(this).select("rect").style("fill-opacity", 0.9);
            });
    }
    /* ******************************************** */
    async function draw_sankey() {
        const colors = d3.scaleOrdinal(d3.schemeCategory10);
        let data = props.sankey_data;
        const size = get_size();
        /* Add a margin of 8 px (1 rem) on every side */
        const margin = { top: 8, right: 8, bottom: 8, left: 8 };
        sankey_size.value = size;
        //console.log(`New width: ${sankey_size.value.width} New height: ${sankey_size.value.height}`)

        svg = d3.select(sankey_wrapper.value)
            .append("svg")
            .attr("height", sankey_size.value.height)
            .attr("width", sankey_size.value.width)
            .append("g")
            .attr("transform", `translate(${margin.left},${margin.top})`);

        sankey = d3.sankey()
            .nodeWidth(15)
            .nodePadding(10)
            .extent([[0, 0], [sankey_size.value.width - margin.right - margin.left, sankey_size.value.height - margin.top - margin.bottom]]);

        // Sort nodes descending by value property
        sankey.nodeSort((a, b) => d3.descending(a.value, b.value));

        let links, nodes;
        if (Object.keys(data).length !== 0) {
            sankeyData = sankey(data);
            ({ links, nodes } = sankeyData);
        }

        if (!links || !nodes) return;

        svg.style("cursor", "default");

        const zoomGroup = svg.append("g")
            .attr("class", "zoom-group");

        zoomGroup.append("g")
            .attr("class", "links")
            .style("stroke", "#000")
            .style("stroke-opacity", 0.3)
            .style("fill", "none");

        zoomGroup.append("g")
            .attr("class", "nodes");

        // Helper function to find all nodes in the backward path (left side)
        function findBackwardPath(targetNode, links) {
            const pathNodes = new Set();
            const queue = [targetNode];
            const visited = new Set();

            while (queue.length > 0) {
                const current = queue.shift();
                if (visited.has(current)) continue;
                visited.add(current);
                pathNodes.add(current);

                // Find all links where current node is the target (incoming links)
                const incomingLinks = links.filter(link => link.target === current);

                incomingLinks.forEach(link => {
                    const source = link.source;
                    // Only nodes to the left
                    if (!visited.has(source) && source.x0 < current.x0) {
                        queue.push(source);
                    }
                });
            }

            return pathNodes;
        }

        // Helper function to find all nodes in the forward path (right side)
        function findForwardPath(sourceNode, links) {
            const pathNodes = new Set();
            const queue = [sourceNode];
            const visited = new Set();

            while (queue.length > 0) {
                const current = queue.shift();
                if (visited.has(current)) continue;
                visited.add(current);
                pathNodes.add(current);

                // Find all links where current node is the source (outgoing links)
                const outgoingLinks = links.filter(link => link.source === current);

                outgoingLinks.forEach(link => {
                    const target = link.target;
                    if (!visited.has(target) && target.x0 > current.x0) { // Only nodes to the right
                        queue.push(target);
                    }
                });
            }

            return pathNodes;
        }

        // Helper function to find all links in the full path
        function findFullPathLinks(pathNodes, links) {
            const pathLinks = new Set();

            links.forEach(link => {
                if (pathNodes.has(link.source) && pathNodes.has(link.target)) {
                    pathLinks.add(link);
                }
            });

            return pathLinks;
        }

        // Reset function to restore original styling
        function resetHighlight() {
            svg.selectAll(".sankey-link")
                .style("stroke-opacity", 0.4)
                .style("stroke", (d) => `url(#gradient-${d.index})`);

            svg.selectAll(".sankey-node")
                .style("fill-opacity", 0.9)
                .style("fill", (d) => colors(d.index % 10)); // Fixed color calculation

            svg.selectAll(".label")
                .style("fill-opacity", 0.85);
        }

        // Highlight function for full path
        function highlightFullPath(hoveredLink) {
            const sourceNode = hoveredLink.source;
            const targetNode = hoveredLink.target;

            // Find all nodes in the backward path (from source to leftmost nodes)
            const backwardNodes = findBackwardPath(sourceNode, links);

            // Find all nodes in the forward path (from target to rightmost nodes)
            const forwardNodes = findForwardPath(targetNode, links);

            // Combine all path nodes
            const allPathNodes = new Set([...backwardNodes, ...forwardNodes]);

            // Find all links in the full path
            const pathLinks = findFullPathLinks(allPathNodes, links);

            // Dim all links first
            svg.selectAll(".sankey-link")
                .style("stroke-opacity", 0.15);

            // Highlight path links with moderate opacity
            svg.selectAll(".sankey-link")
                .filter(d => pathLinks.has(d))
                .style("stroke-opacity", 0.7);

            // Dim all nodes first
            svg.selectAll(".sankey-node")
                .style("fill-opacity", 0.2);

            // Highlight path nodes with high opacity
            svg.selectAll(".sankey-node")
                .filter(d => allPathNodes.has(d))
                .style("fill-opacity", 1.0);

            // Dim all labels first
            svg.selectAll(".label")
                .style("fill-opacity", 0.25);

            // Highlight labels for path nodes
            svg.selectAll(".label")
                .filter(d => allPathNodes.has(d))
                .style("fill-opacity", 1.0);
        }


        const d3_nodes = svg.select("g.nodes")
            .selectAll("g")
            .data(nodes)
            .join((enter) => enter.append("g"))
            .attr("transform", (d) => `translate(${d.x0}, ${d.y0})`)
            .call(createNodeDrag(links, svg));

        d3_nodes.append("rect")
            .attr("height", (d) => Math.max(3, d.y1 - d.y0)) // Ensure minimum height of 3px
            .attr("width", (d) => Math.max(15, d.x1 - d.x0)) // Ensure minimum width of 15px
            .attr("dataIndex", (d) => d.index)
            .attr("fill", (d) => colors(d.index % 10)) // Fixed: use modulo to ensure proper color cycling
            .attr("fill-opacity", 0.9)
            .attr("class", "sankey-node")
            .attr("style", "cursor:move;")
            .style("stroke", "none");


        d3.selectAll("rect").append("title").text((d) => `${d?.label}`);

        d3_nodes.append("text")
            .attr('class', 'label')
            .style('pointer-events', 'auto')
            .attr("style", "cursor:pointer;")
            .style('fill-opacity', 0.85)
            .attr("fill", "#000")
            .attr("x", (d) => (d.x0 < sankey_size.value.width / 2 ? 6 + Math.max(15, d.x1 - d.x0) : -6)) // Adjusted for minimum width
            .attr("y", (d) => Math.max(3, d.y1 - d.y0) / 2) // Adjusted for minimum height
            .attr("alignment-baseline", "middle")
            .attr("text-anchor", (d) => d.x0 < sankey_size.value.width / 2 ? "start" : "end")
            .attr("font-size", 12)
            .text((d) => d.label)
            .on("click", function (event, data_obj) {
                event.stopPropagation();
                // emit node click event to parent component
                emit('node_click', data_obj.data, data_obj);
            });

        // Draw links
        const links_d3 = svg.select("g.links")
            .selectAll("g")
            .data(links)
            .join((enter) => enter.append("g"));

        const lg_d3 = links_d3.append("linearGradient");
        lg_d3.attr("id", (d) => `gradient-${d.index}`)
            .attr("gradientUnits", "userSpaceOnUse")
            .attr("x1", (d) => d.source.x1)
            .attr("x2", (d) => d.target.x0);

        lg_d3.append("stop")
            .attr("offset", "0")
            .attr("stop-color", (d) => colors(d.source.index % 10)); // Fixed color calculation

        lg_d3.append("stop")
            .attr("offset", "100%")
            .attr("stop-color", (d) => colors(d.target.index % 10)); // Fixed color calculation

        links_d3
            .append("path")
            .attr("class", "sankey-link")
            .attr("d", d3.sankeyLinkHorizontal())
            .attr("stroke-width", (d) => Math.max(1, d.width))
            .attr("stroke", (d) => `url(#gradient-${d.index})`)
            .attr("stroke-opacity", 0.4)
            .attr("data-bs-toggle", "tooltip")
            .attr("data-bs-placement", "top")
            .attr("title", (d) => `${d.label}`)
            .text((d) => `${d.label}`)
            .on("mouseover", function (event, d) {
                highlightFullPath(d);
            })
            .on("mouseout", function (event, d) {
                resetHighlight();
            });


        svg.on("mouseleave", function () {
            resetHighlight();
        });
    }

    __expose({ draw_sankey, setNoDataFlag });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", {
        ref_key: "sankey_div",
        ref: sankey_div,
        class: "d-flex align-items-center justify-content-center flex-column flex-grow-1 position-relative"
      }, [
        createCommentVNode(" Zoom button group "),
        (!no_data.value)
          ? (openBlock(), createElementBlock("div", _hoisted_1$1J, [
              createBaseVNode("div", _hoisted_2$1x, [
                createBaseVNode("button", {
                  type: "button",
                  class: "btn zoom-btn",
                  onClick: _cache[0] || (_cache[0] = $event => (zoomChart(0.5)))
                }, [
                  createBaseVNode("i", {
                    class: "fa-solid fa-magnifying-glass-plus",
                    "data-bs-toggle": "tooltip",
                    "data-bs-placement": "top",
                    title: _i18n('date_time_range_picker.btn_zoom_in')
                  }, null, 8 /* PROPS */, _hoisted_3$1n)
                ]),
                createBaseVNode("button", {
                  type: "button",
                  class: "btn zoom-btn",
                  onClick: _cache[1] || (_cache[1] = $event => (zoomChart(-0.5)))
                }, [
                  createBaseVNode("i", {
                    class: "fa-solid fa-magnifying-glass-minus",
                    "data-bs-toggle": "tooltip",
                    "data-bs-placement": "top",
                    title: _i18n('date_time_range_picker.btn_zoom_out')
                  }, null, 8 /* PROPS */, _hoisted_4$1k)
                ]),
                (showAutoRefresh.value)
                  ? (openBlock(), createElementBlock("button", {
                      key: 0,
                      button: "",
                      type: "button",
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      class: normalizeClass(["btn refresh-btn", { 'active': autoRefreshEnabled.value }]),
                      onClick: toggleAutoRefresh,
                      title: autoRefreshEnabled.value ? 'Auto-refresh enabled' : 'Auto-refresh disabled'
                    }, [
                      createBaseVNode("i", {
                        class: normalizeClass(["fa-solid fa-arrows-rotate", { 'fa-spin': autoRefreshEnabled.value }])
                      }, null, 2 /* CLASS */)
                    ], 10 /* CLASS, PROPS */, _hoisted_5$15))
                  : createCommentVNode("v-if", true)
              ])
            ]))
          : createCommentVNode("v-if", true),
        createCommentVNode(" no data "),
        (no_data.value)
          ? (openBlock(), createElementBlock("div", _hoisted_6$U, toDisplayString(__props.no_data_message || _i18n('flows_page.no_data')), 1 /* TEXT */))
          : createCommentVNode("v-if", true),
        createBaseVNode("div", {
          ref_key: "sankey_wrapper",
          ref: sankey_wrapper
        }, null, 512 /* NEED_PATCH */)
      ], 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$n = "\n.btn-ontop[data-v-5e5b2ca8] {\n    position: absolute;\n    right: 0;\n    top: -0.7rem;\n    z-index: 10;\n}\n.alert[data-v-5e5b2ca8] {\n    margin: 20px;\n    padding: 15px;\n    border-radius: 4px;\n}\n.alert-info[data-v-5e5b2ca8] {\n    background-color: #f8f9fa;\n    border: 1px solid #dee2e6;\n    color: #0c5460;\n}\n.zoom-btn[data-v-5e5b2ca8] {\n    background-color: #fd7e14 !important;\n    color: white !important;\n    border: none !important;\n    height: 24px;\n    display: flex;\n    align-items: center;\n    padding: 0 8px;\n}\n.zoom-btn[data-v-5e5b2ca8]:hover {\n    background-color: #e76b06 !important;\n}\n.sankey-wrapper-container[data-v-5e5b2ca8] {\n    display: flex;\n    align-items: flex-start;\n    gap: 1rem;\n}\n.btn-group-container[data-v-5e5b2ca8] {\n    display: flex;\n    justify-content: flex-end;\n    margin-bottom: 1rem;\n}\n.zoom-controls[data-v-5e5b2ca8] {\n    position: absolute;\n    top: 10px;\n    right: 10px;\n    z-index: 10;\n}\n.refresh-btn[data-v-5e5b2ca8] {\n    background-color: #6c757d !important;\n    color: white !important;\n    border: none !important;\n    height: 24px;\n    display: flex;\n    align-items: center;\n    padding: 0 8px;\n}\n.refresh-btn[data-v-5e5b2ca8]:hover {\n    background-color: #5a6268 !important;\n}\n.refresh-btn.active[data-v-5e5b2ca8] {\n    background-color: #007bff !important;\n}\n.refresh-btn.active[data-v-5e5b2ca8]:hover {\n    background-color: #0056b3 !important;\n}\n.sankey-container svg[data-v-5e5b2ca8] {\n    overflow: visible;\n}\n.sankey-container[data-v-5e5b2ca8] {\n  overflow: hidden;\n}\n";
    styleInject(css_248z$n);

    script$1T.__scopeId = "data-v-5e5b2ca8";
    script$1T.__file = "http_src/vue/sankey.vue";

    const _hoisted_1$1I = { class: "row" };
    const _hoisted_2$1w = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1m = { class: "card card-shadow" };
    const _hoisted_4$1j = { class: "form-group d-flex align-items-end" };
    const _hoisted_5$14 = { class: "dropdown mt-2 ms-3 d-inline-block" };
    const _hoisted_6$T = {
      class: "card-body",
      style: {"height":"70vh"}
    };
    const _hoisted_7$O = {
      class: "d-flex",
      style: {"height":"100%"}
    };


    var script$1S = {
      __name: 'page-host-details-flow-sankey',
      props: {
        is_local: Boolean
    },
      setup(__props) {

    const props = __props;

    const _i18n = (t) => i18n(t);
    const url = `${http_prefix}/lua/pro/rest/v2/get/host/flows/data.lua`;
    const sankey_chart = ref(null);
    const loading = ref(true);

    const sankey_format_list = [
        { filter_name: 'hosts_type', key: 3, id: 'local_origin_remote_target', title: _i18n('flows_page.local_cli_remote_srv'), label: _i18n('flows_page.local_cli_remote_srv'), filter_icon: false, countable: false },
        { filter_name: 'hosts_type', key: 4, id: 'remote_origin_local_target', title: _i18n('flows_page.local_srv_remote_cli'), label: _i18n('flows_page.local_srv_remote_cli'), filter_icon: false, countable: false },
        { filter_name: 'hosts_type', key: 5, id: 'all_hosts', title: _i18n('flows_page.all_flows'), label: _i18n('flows_page.all_flows'), filter_icon: false, countable: false },
    ];

    if (props.is_local) {
        sankey_format_list.unshift({ filter_name: 'hosts_type', key: 1, id: 'local_only', title: _i18n('flows_page.local_only'), label: _i18n('flows_page.local_only'), filter_icon: false, countable: false });
    } else {
        sankey_format_list.unshift({ filter_name: 'hosts_type', key: 2, id: 'remote_only', title: _i18n('flows_page.remote_only'), label: _i18n('flows_page.remote_only'), filter_icon: false, countable: false });
    }

    const active_hosts_type = ref(sankey_format_list[3]);

    const sankey_data = ref({});

    onBeforeMount(() => { });

    onMounted(() => {
        update_sankey();
    });

    function on_node_click(node) {
        if (node.is_link_node == true) { return; }
        let url_obj = {
            host: node.info.ip,
            vlan: node.info.vlan,
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(url_obj);
        const host_url = `${http_prefix}/lua/host_details.lua?${url_params}`;
        ntopng_url_manager$1.go_to_url(host_url);
        //host=' .. flow["cli.ip"] .. '&vlan=' .. flow["vlan"]
    }

    const update_sankey = function () {
        let entry = active_hosts_type.value;
        ntopng_url_manager$1.set_key_to_url(entry.filter_name, entry.id);
        set_sankey_data();
    };

    async function set_sankey_data() {
        loading.value = true;
        let data = await get_sankey_data();
        sankey_data.value = data;
        loading.value = false;
    }

    /*
    function add_fake_circular_link(graph) {
        const node_id_0 = "node_id_0", node_id_1 = "node_id_1";
        graph.nodes.push({ node_id: node_id_0, label: node_id_0 });
        graph.nodes.push({ node_id: node_id_1, label: node_id_1 });
        graph.links.push({ source_node_id: node_id_0, target_node_id: node_id_1, value: 10, label: `${node_id_0}_${node_id_1}` });
        graph.links.push({ source_node_id: node_id_1, target_node_id: node_id_0, value: 10, label: `${node_id_1}_${node_id_0}` });
    }
    */

    async function get_sankey_data() {
        const url_request = get_sankey_url();
        // let graph = await sankeyUtils.get_data();
        let graph = await ntopng_utility$1.http_request(url_request);
        // add_fake_circular_link(graph);
        graph = make_complete_graph(graph);
        let main_node_id = get_main_node_id();
        let sankey_data = get_sankey_data_from_rest_data(graph, main_node_id);
        // sankey_data = make_dag_graph(sankey_data);
        return sankey_data;
    }

    function get_sankey_url() {
        let params = {
            host: ntopng_url_manager$1.get_url_entry("host"),
            vlan: ntopng_url_manager$1.get_url_entry("vlan"),
            ifid: ntopng_url_manager$1.get_url_entry("ifid"),
            hosts_type: ntopng_url_manager$1.get_url_entry("hosts_type"),
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        let url_request = `${url}?${url_params}`;
        return url_request;
    }

    function get_main_node_id() {
        return ntopng_url_manager$1.get_url_entry("host");
    }

    function get_sankey_data_from_rest_data(graph, main_node_id) {
        if (graph.nodes.length == 0 && graph.links.length == 0) { return graph; }
        let node_dict = {};
        // create a node dict
        graph.nodes.forEach((node) => node_dict[node.node_id] = node);

        //get link direction 
        const f_get_link_direction = (link) => {
            if (link.source_node_id == main_node_id) {
                return -1;
            } else if (link.target_node_id == main_node_id) {
                return 1;
            }
            throw `Wrong direction link ${link.source_node_id} -> ${link.target_node_id}`;
        };

        // get node id with direction
        const f_get_node_direction_id = (node_id, direction) => {
            if (node_id == main_node_id) {
                return node_id;
            }
            return `${direction}_${node_id}`;
        };

        // create a new graph duplicating all nodes with different direction
        let graph2_node_dict = {};
        let graph2 = { nodes: [], links: [] };
        graph.links.forEach((link) => {
            let direction = f_get_link_direction(link);
            let new_link = {
                source_node_id: f_get_node_direction_id(link.source_node_id, direction),
                target_node_id: f_get_node_direction_id(link.target_node_id, direction),
                label: link.label,
                value: link.value,
                data: link,
            };
            let new_node;
            if (direction == -1) {
                let n = node_dict[link.target_node_id];
                new_node = { node_id: new_link.target_node_id, label: n.label, data: n };
            } else {
                let n = node_dict[link.source_node_id];
                new_node = { node_id: new_link.source_node_id, label: n.label, data: n };
            }
            graph2.links.push(new_link);
            if (graph2_node_dict[new_node.node_id] == null) {
                graph2_node_dict[new_node.node_id] = true;
                graph2.nodes.push(new_node);
            }
        });
        let main_node = node_dict[main_node_id];
        graph2.nodes.push({ node_id: main_node.node_id, label: main_node.label, data: main_node });

        // update node dict
        graph2.nodes.forEach((node) => node_dict[node.node_id] = node);

        // return the link node_id 
        const f_get_link_node_id = (link) => {
            let direction = f_get_link_direction(link);
            return `${direction}_${link.label}`;
            // return `${link.source_node_id}_${link.label}`; 
        };

        let link_to_nodes_dict = {}; // key: link node id, value: links
        // merge all links by link node_id
        graph2.links.forEach((link) => {
            let link_node_id = f_get_link_node_id(link);
            let link_to_nodes = link_to_nodes_dict[link_node_id];
            if (link_to_nodes == null) {
                link_to_nodes = {
                    id: link_node_id,
                    label: link.label,
                    data: { ...link, is_link_node: true },
                    node_links: [],
                };
                link_to_nodes_dict[link_node_id] = link_to_nodes;
            }
            link_to_nodes.node_links.push({
                source: node_dict[link.source_node_id],
                target: node_dict[link.target_node_id],
                value: get_link_value(link),
            });
        });

        // create nodes and links graph, creating a new node for each link
        let nodes = graph2.nodes.map((n) => n), links = [];
        for (let link_node_id in link_to_nodes_dict) {
            let link_to_nodes = link_to_nodes_dict[link_node_id];
            let link_node = {
                node_id: link_to_nodes.id,
                label: link_to_nodes.label,
                data: link_to_nodes.data,
            };
            nodes.push(link_node);
            link_to_nodes.node_links.forEach((link) => {
                links.push({
                    source_node_id: link.source.node_id,
                    target_node_id: link_node.node_id,
                    label: `${link.source.label} - ${link.target.label}: ${link_node.label}`,
                    value: link.value,
                    data: link,
                });
                links.push({
                    source_node_id: link_node.node_id,
                    target_node_id: link.target.node_id,
                    label: `${link.source.label} - ${link.target.label}: ${link_node.label}`,
                    value: link.value,
                    data: link,
                });
            });
        }

        let sankey_node_dict = {}; // key: node_id, value: sankey_node
        let sankey_nodes = [];
        nodes.map((n, index) => {
            let sankey_node = { index, node_id: n.node_id, label: n.label, data: n.data };
            sankey_node_dict[n.node_id] = sankey_node;
            sankey_nodes.push(sankey_node);
        });
        let sankey_links = links.map((l) => {
            let source = sankey_node_dict[l.source_node_id];
            let target = sankey_node_dict[l.target_node_id];
            return {
                source: source.index,
                target: target.index,
                source_node_id: source.index,
                target_node_id: target.index,
                value: l.value,
                label: l.label,
            };
        });
        return { nodes: sankey_nodes, links: sankey_links };
    }

    // remove all links with a not existing node
    function make_complete_graph(graph) {
        let f_log_link = (l) => console.error(`link (source: ${l.source_node_id}, target: ${l.target_node_id}) removed for not existing source/target node`);
        let links = get_links_with_existing_node(graph, f_log_link);
        return { nodes: graph.nodes, links };
    }

    function get_links_with_existing_node(graph, f_log) {
        let node_dict = {};
        graph.nodes.forEach((n) => node_dict[n.node_id] = true);
        let f_filter = (l) => node_dict[l.source_node_id] != null && node_dict[l.target_node_id] != null;
        let links = filter_log(graph.links, f_filter, f_log);
        return links;
    }

    // log elements deleted if f_log != null
    function filter_log(elements, f_filter, f_log) {
        return elements.filter((e) => {
            const take_element = f_filter(e);
            if (take_element == false && f_log != null) {
                f_log(e);
            }
            return take_element;
        });
    }

    function get_link_value(link) {
        return link.data?.info?.traffic;
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1I, [
        createBaseVNode("div", _hoisted_2$1w, [
          createBaseVNode("div", _hoisted_3$1m, [
            createBaseVNode("div", _hoisted_4$1j, [
              createBaseVNode("div", _hoisted_5$14, [
                createVNode(script$2H, {
                  selected_option: active_hosts_type.value,
                  "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((active_hosts_type).value = $event)),
                  options: sankey_format_list,
                  onSelect_option: update_sankey
                }, null, 8 /* PROPS */, ["selected_option"])
              ])
            ]),
            createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
            createBaseVNode("div", _hoisted_6$T, [
              createBaseVNode("div", _hoisted_7$O, [
                createVNode(script$1T, {
                  ref_key: "sankey_chart",
                  ref: sankey_chart,
                  onNode_click: on_node_click,
                  sankey_data: sankey_data.value
                }, null, 8 /* PROPS */, ["sankey_data"])
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1S.__file = "http_src/vue/page-host-details-flow-sankey.vue";

    const _hoisted_1$1H = {
      key: 0,
      class: "alert alert-info alert-dismissable"
    };
    const _hoisted_2$1v = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_3$1l = { class: "col-form-label col-sm-2" };
    const _hoisted_4$1i = { class: "col-sm-10" };
    const _hoisted_5$13 = {
      class: "btn-group btn-group-toggle",
      "data-bs-toggle": "buttons"
    };
    const _hoisted_6$S = {
      key: 1,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_7$N = { class: "col-form-label col-sm-2" };
    const _hoisted_8$H = { class: "col-sm-10" };
    const _hoisted_9$B = ["placeholder"];
    const _hoisted_10$w = {
      key: 2,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_11$t = { class: "col-form-label col-sm-2" };
    const _hoisted_12$p = { class: "col-sm-10" };
    const _hoisted_13$m = {
      key: 3,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_14$k = { class: "col-form-label col-sm-2" };
    const _hoisted_15$j = { class: "col-sm-10" };
    const _hoisted_16$j = {
      key: 4,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_17$h = { class: "col-form-label col-sm-2" };
    const _hoisted_18$e = { class: "col-10" };
    const _hoisted_19$d = {
      key: 5,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_20$d = { class: "col-form-label col-sm-2 mt-3" };
    const _hoisted_21$d = { class: "col-10 mt-3" };
    const _hoisted_22$d = { class: "col-form-label col-sm-2 mt-3" };
    const _hoisted_23$d = { class: "col-10 mt-3" };
    const _hoisted_24$c = {
      key: 6,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_25$b = { class: "col-form-label col-sm-2" };
    const _hoisted_26$a = { class: "col-10" };
    const _hoisted_27$a = {
      key: 7,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_28$8 = { class: "col-form-label col-sm-2" };
    const _hoisted_29$8 = { class: "col-10" };
    const _hoisted_30$8 = {
      key: 8,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_31$7 = { class: "col-form-label col-sm-2" };
    const _hoisted_32$7 = { class: "col-10" };
    const _hoisted_33$7 = {
      key: 9,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_34$6 = { class: "col-form-label col-sm-2" };
    const _hoisted_35$6 = {
      key: 0,
      class: "col-10"
    };
    const _hoisted_36$6 = {
      key: 1,
      class: "col-10"
    };
    const _hoisted_37$5 = {
      key: 2,
      class: "col-10"
    };
    const _hoisted_38$5 = {
      key: 3,
      class: "col-10"
    };
    const _hoisted_39$4 = {
      key: 4,
      class: "col-10"
    };
    const _hoisted_40$4 = {
      key: 5,
      class: "col-10"
    };
    const _hoisted_41$4 = {
      key: 6,
      class: "col-10"
    };
    const _hoisted_42$4 = {
      key: 7,
      class: "col-10"
    };
    const _hoisted_43$4 = {
      key: 10,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_44$4 = { class: "col-form-label col-sm-2" };
    const _hoisted_45$4 = { class: "col-10" };
    const _hoisted_46$4 = {
      class: "form-group ms-2 me-2 mt-3 row",
      style: {"margin-top":"3px"}
    };
    const _hoisted_47$3 = { class: "col-form-label col-sm-2" };
    const _hoisted_48$3 = { class: "col-sm-3" };
    const _hoisted_49$3 = {
      class: "btn-group float-end btn-group-toggle",
      "data-bs-toggle": "buttons"
    };
    const _hoisted_50$3 = ["value", "id"];
    const _hoisted_51$3 = ["id", "for"];
    const _hoisted_52$2 = ["value", "id"];
    const _hoisted_53$2 = ["id", "for"];
    const _hoisted_54$2 = ["value", "id"];
    const _hoisted_55$2 = ["id", "for"];
    const _hoisted_56$2 = {
      class: "col-sm-2 btn-group float-end btn-group-toggle",
      "data-bs-toggle": "buttons"
    };
    const _hoisted_57$2 = ["value", "id"];
    const _hoisted_58$2 = ["id", "for"];
    const _hoisted_59$2 = {
      key: 11,
      class: "message alert alert-warning mt-3"
    };
    const _hoisted_60$2 = ["disabled"];
    const _hoisted_61$2 = ["disabled"];

    const flow_exp_ts_available_url = "/lua/pro/rest/v2/get/flowdevice/timeseries_enabled.lua"; 


    var script$1R = {
      __name: 'modal-add-traffic-rules',
      props: {
        metric_list: Array,
        ifid_list: Array,
        flow_exporter_devices: Array,
        interface_metric_list: Array,
        flow_device_metric_list: Array,
        frequency_list: Array,
        has_vlans: Boolean,
        has_profiles: Boolean,
        has_asn: Boolean,
        init_func: Function,
        page_csrf: String,
        isExporterInterface: Boolean
    },
      emits: ['add', 'edit'],
      setup(__props, { expose: __expose, emit: __emit }) {

    ref("");
    ref("");
    const modal_size = ref(3);


    let has_host_pools = ref(false);
    let has_cidr = ref(false);
    const modal_id = ref(null);
    const emit = __emit;
    let title = i18n('if_stats_config.add_traffic_rules_title');
    const host_placeholder = i18n('if_stats_config.host_placeholder');
    const metrics_ready = ref(false);
    const _i18n = (t) => i18n(t);
    const metric_list = ref([]);
    const init_func = ref(null);
    const delete_row = ref(null);
    const ifid_list = ref([]);
    const flow_exporter_devices = ref([]);
    const flow_exporter_device_ifid_list = ref([]);
    const interface_metric_list = ref([]);
    const host_pool_metric_list = ref([]);
    const flow_device_metric_list = ref([]);
    const frequency_list = ref([]);
    const threshold_measure = ref(null);
    const threshold_sign = ref(null);
    const selected_metric = ref({});
    const selected_frequency = ref({});
    const selected_ifid = ref({});
    const selected_exporter_device = ref({});
    const selected_exporter_device_ifid = ref({});
    const selected_interface_metric = ref({});
    const selected_host_pool_metric = ref({});
    const selected_flow_device_metric = ref({});
    const disable_add = ref(true);
    const metric_type = ref({});
    const visible = ref(true);
    const rule_type = ref("hosts");
    const flow_device_timeseries_available = ref(false);
    const is_edit_page = ref(false);
    const page_csrf_ = ref(null);
    const row_to_edit_id = ref(null);
    const invalid_add = ref(false);
    const host_pool_list = ref(null);
    const network_list = ref(null);
    const selected_host_pool = ref({});
    const selected_network = ref({});
    const selected_network_metric = ref({});
    const network_metric_list = ref(null);
    const vlan_list = ref([]);
    const selected_vlan = ref({});
    const vlan_metric_list = ref(null);
    const selected_vlan_metric = ref({});
    const profiles_list = ref([]);
    const selected_profile = ref({});
    const profiles_metric_list = ref(null);
    const selected_profile_metric = ref({});
    const asn_list = ref([]);
    const selected_asn = ref({});
    const asn_metric_list = ref(null);
    const selected_asn_metric = ref({});

    let active_metric_type_list = ref([]);


    const note_list = [
        _i18n('if_stats_config.note_1'),
        _i18n('if_stats_config.note_2'),
        _i18n('if_stats_config.note_3'),
        _i18n('if_stats_config.note_4'),
    ];

    const sub_notes_list = [
        _i18n('if_stats_config.note_5')
    ];

    const metric_type_list = ref([
        { title: _i18n('volume'), label: _i18n('volume'), id: 'volume', active: true },
        { title: _i18n('throughput'), label: _i18n('throughput'), id: 'throughput', active: false },
        { title: _i18n('percentage'), label: _i18n('percentage'), id: 'percentage', active: false },
    ]);

    /* Currently disabled the percentage */
    const exporter_metric_type_list = ref([
        { title: _i18n('volume'), label: _i18n('volume'), id: 'volume', active: true },
        { title: _i18n('throughput'), label: _i18n('throughput'), id: 'throughput', active: false },
        { title: _i18n('percentage'), label: _i18n('percentage'), id: 'percentage', active: false },
    ]);

    const pool_metric_type_list = ref([
        { title: _i18n('volume'), label: _i18n('volume'), id: 'volume', active: true, measure_unit: 'bps' },
        { title: _i18n('throughput'), label: _i18n('throughput'), id: 'throughput', active: false, measure_unit: 'bps' },
        { title: _i18n('percentage'), label: _i18n('percentage'), id: 'percentage', active: false, measure_unit: 'number' },
        { title: _i18n('value'), label: _i18n('value'), id: 'value', active: false, measure_unit: 'number' }
    ]);

    const exporter_usage_type_list = ref([
        { title: _i18n('percentage'), label: _i18n('percentage'), id: 'absolute_percentage', active: false, measure_unit: 'number' },
    ]);

    const volume_threshold_list = ref([
        { title: _i18n('kb'), label: _i18n('kb'), id: 'kb', value: 1024, active: false },
        { title: _i18n('mb'), label: _i18n('mb'), id: 'mb', value: 1048576, active: false },
        { title: _i18n('gb'), label: _i18n('gb'), id: 'gb', value: 1073741824, active: true, default_active: true },
    ]);

    const throughput_threshold_list = ref([
        { title: _i18n('kbps'), label: _i18n('kbps'), id: 'kbps', value: 1000, active: false },
        { title: _i18n('mbps'), label: _i18n('mbps'), id: 'mbps', value: 1000000, active: false },
        { title: _i18n('gbps'), label: _i18n('gbps'), id: 'gbps', value: 1000000000, active: true, default_active: true },
    ]);

    const sign_threshold_list = ref([
        { title: "+", label: ">", id: 'plus', value: 1, active: false },
        { title: "-", label: "<", id: 'minus', value: -1, active: true, default_active: true },
    ]);

    const percentage_threshold_list = [
        { title: "+", label: "%", id: 'plus', value: 1, active: true },
    ];

    const host = ref(null);
    const threshold = ref(null);

    const showed = () => { };

    const props = __props;

    ({
        csrf: props.page_csrf
    });

    function reset_radio_selection(radio_array) {

        radio_array.forEach((item) => item.active = item.default_active == true);
    }

    /**
     * 
     * Reset fields in modal form 
     */
    const reset_modal_form = async function () {
        invalid_add.value = false;
        host.value = "";
        rule_type.value = "Host";
        selected_ifid.value = ifid_list.value[0];
        selected_metric.value = metric_list.value[0];
        selected_interface_metric.value = interface_metric_list.value[0];
        selected_flow_device_metric.value = flow_device_metric_list.value[0];

        is_edit_page.value = false;
        title = i18n('if_stats_config.add_traffic_rules_title');
        selected_frequency.value = frequency_list.value[0];
        metric_type.value = metric_type_list.value[0];
        selected_exporter_device.value = flow_exporter_devices.value[1];
        if (selected_exporter_device.value != null) {
            update_exporter_interfaces();
        }

        // reset metric_type_list
        metric_type_list.value.forEach((t) => t.active = false);
        metric_type_list.value[0].active = true;

        if (host_pool_list.value != null)
            selected_host_pool.value = host_pool_list.value[0];

        selected_host_pool_metric.value = host_pool_metric_list.value[0];

        if (network_list.value != null)
            selected_network.value = network_list.value[0];
        if (network_metric_list.value != null)
            selected_network_metric.value = network_metric_list.value[0];


        reset_radio_selection(volume_threshold_list.value);
        reset_radio_selection(throughput_threshold_list.value);
        reset_radio_selection(sign_threshold_list.value);

        rule_type.value = "Host";

        disable_add.value = true;

        threshold.value.value = 1;

        row_to_edit_id.value = null;

        active_metric_type_list.value = metric_type_list.value;

        if (rule_type == 'Host' || rule_type == 'interface') {
            metric_type.vale = metric_type_list.value[0];
        } else {
            metric_type.value = active_metric_type_list.value[0];
        }

        if (props.has_vlans) {
            selected_vlan.value = vlan_list.value[0];
            selected_vlan_metric.value = vlan_metric_list.value[0];
        }

    };

    const set_rule_type = (type) => {
        rule_type.value = type;

        active_metric_type_list.value = metric_type_list.value;

        if (type == "host_pool" || type == "CIDR") {
            change_metric_type_hp();

            if (type == "host_pool") {
                metric_type.value = active_metric_type_list.value[1];
            } else {
                metric_type.value = active_metric_type_list.value[0];
            }

            visible.value = true;

        } else {
            metric_type.value = metric_type_list.value[0];

            if (type == "Host") {
                change_threshold();
            } else if (type == "interface") {
                change_interface_threshold();
            } else if (type == "vlan") {
                change_vlan_threshold();
            } else if (type == "asn") {
                change_asn_threshold();
            } else {
                visible.value = true;
            }

        }
    };


    const change_metric_type_exporter = () => {
        let tmp_metric_type_list = [];
        if ((selected_flow_device_metric.value.id == "flowdev_port:usage")) {
            exporter_usage_type_list.value.forEach((item) => {
                if (item.measure_unit == 'number') {
                    tmp_metric_type_list.push(item);
                }
            });
            active_metric_type_list.value = tmp_metric_type_list;
        } else {
            exporter_metric_type_list.value.forEach((item) => {
                if (item.id != 'value') {
                    tmp_metric_type_list.push(item);
                }
            });
            active_metric_type_list.value = tmp_metric_type_list;
        }
        metric_type.value = active_metric_type_list.value[0];
    };


    const change_metric_type_hp = (set_active_one) => {
        let tmp_metric_type_list = [];
        if ((rule_type.value == "host_pool" && selected_host_pool_metric.value.measure_unit != "bps") || (rule_type.value == "CIDR" && selected_network_metric.value.measure_unit != "bps")) {

            pool_metric_type_list.value.forEach((item) => {
                if (item.measure_unit == 'number') {
                    tmp_metric_type_list.push(item);
                }
            });

            active_metric_type_list.value = tmp_metric_type_list;

        } else {

            pool_metric_type_list.value.forEach((item) => {
                if (item.id != 'value') {
                    tmp_metric_type_list.push(item);
                }
            });
            active_metric_type_list.value = tmp_metric_type_list;

        }
        if (set_active_one == null || set_active_one == false) {
            metric_type.value = active_metric_type_list.value[0];
        }

    };


    /**
     * 
     * Set row to edit 
     */
    const set_row_to_edit = (row) => {

        if (row != null) {
            title = _i18n('if_stats_config.edit_traffic_rules_title');
            is_edit_page.value = true;

            row_to_edit_id.value = row.row_id;

            disable_add.value = false;

            // set threshold sign
            sign_threshold_list.value.forEach((t) => {
                t.active = (t.value == row.threshold_sign);
            });

            // set metric_type
            metric_type_list.value.forEach((t) => {
                if (t.id == row.metric_type) {
                    t.active = true;
                    metric_type.value = t;
                } else {
                    t.active = false;
                }
            });

            active_metric_type_list.value = metric_type_list.value;

            // set threshold
            if (row.metric_type == 'volume')
                volume_threshold_list.value.forEach((t) => {
                    if ((row.threshold % t.value) == 0) {
                        let row_threshold_value = row.threshold / t.value;
                        if (row_threshold_value < 1024) {
                            t.active = true;
                            threshold.value.value = row_threshold_value == 0 ? 1 : row_threshold_value;
                        } else {
                            t.active = false;
                        }
                    } else {
                        t.active = false;
                    }
                });
            else
                if (row.metric_type == 'throughput') {
                    //row.threshold = row.threshold * 8;
                    throughput_threshold_list.value.forEach((t) => {
                        if ((row.threshold % t.value) == 0) {
                            let row_threshold_value = row.threshold / t.value;
                            if (row_threshold_value < 1000) {
                                t.active = true;
                                threshold.value.value = row_threshold_value == 0 ? 1 : row_threshold_value;
                            } else {
                                t.active = false;
                            }
                        } else {
                            t.active = false;
                        }
                    });
                } else if (row.metric_type == 'percentage') {

                    //percentage case
                    threshold.value.value = row.threshold;
                } else if (row.metric_type == 'value' || row.metric_type == 'absolute_percentage') {
                    threshold.value.value = row.threshold * (row.threshold_sign);
                }

            // set rule_type
            rule_type.value = row.rule_type;

            if (rule_type.value == 'interface') {

                // set ifid
                ifid_list.value.forEach((t) => {
                    if (t.id == row.target)
                        selected_ifid.value = t;
                });

                // set metric
                if (row.extra_metric != null) {
                    interface_metric_list.value.forEach((t) => {
                        if (t.id == row.metric && t.extra_metric == row.extra_metric) {
                            selected_interface_metric.value = t;
                        }
                    });

                } else {
                    interface_metric_list.value.forEach((t) => {
                        if (t.id == row.metric) {
                            selected_interface_metric.value = t;
                        }
                    });
                }
            } else if (rule_type.value == 'exporter') {

                flow_exporter_devices.value.forEach((item) => {
                    if (item.id == row.target)
                        selected_exporter_device.value = item;
                });
                flow_exporter_device_ifid_list.value.forEach((item) => {
                    if (item.id == row.flow_exp_ifid)
                        selected_exporter_device_ifid.value = item;
                });
            } else if (rule_type.value == 'Host') {

                //set host
                host.value = row.target;

                //set metric
                if (row.extra_metric != null) {

                    metric_list.value.forEach((t) => {
                        if (row.metric.contains(t.id) && t.extra_metric == row.extra_metric)
                            selected_metric.value = t;
                    });
                } else {
                    metric_list.value.forEach((t) => {
                        if (t.id == row.metric)
                            selected_metric.value = t;
                    });
                }
            } else if (rule_type.value == 'CIDR') {
                network_list.value.forEach((item) => {
                    if (item.id == row.target) {
                        selected_network.value = item;
                    }
                });

                network_metric_list.value.forEach((item) => {
                    if (item.label == row.metric_label) {
                        selected_network_metric.value = item;
                    }
                });

                change_metric_type_hp(true);

                active_metric_type_list.value.forEach((item) => {
                    if (item.id == row.metric_type) {
                        metric_type.value = item;
                    }
                });


            } else if (rule_type.value == 'host_pool') {
                host_pool_list.value.forEach((item) => {
                    if (item.id == row.target) {
                        selected_host_pool.value = item;
                    }
                });

                host_pool_metric_list.value.forEach((item) => {
                    if (item.label == row.metric_label) {
                        selected_host_pool_metric.value = item;
                    }
                });
                change_metric_type_hp();

                active_metric_type_list.value.forEach((item) => {
                    if (item.id == row.metric_type) {
                        metric_type.value = item;
                    }
                });

            } else if (rule_type.value == 'vlan') {
                selected_vlan.value = vlan_list.value.find((item) => item.id == row.target);

                vlan_metric_list.value.forEach((item) => {
                    if (item.schema == row.metric) {
                        selected_vlan_metric.value = item;
                    }
                });

            } else if (rule_type.value == 'profiles') {
                selected_profile.value = profiles_list.value.find((item) => item.id == row.target);

                selected_profile_metric.value = profiles_metric_list.value.find((item) =>
                    item.schema == row.metric
                );
            }
            else if (rule_type.value == 'asn') {
                selected_asn.value = asn_list.value.find((item) => item.id == row.target);
                if (row.extra_metric != null) {
                    selected_asn_metric.value = asn_metric_list.value.find((item) =>
                        item.schema == row.metric && item.extra_metric == row.extra_metric
                    );
                } else {
                    selected_asn_metric.value = asn_metric_list.value.find((item) =>
                        item.schema == row.metric
                    );
                }
            }
        }
    };

    const show = (row) => {
        if (row != null) {
            set_row_to_edit(row);
        } else {
            reset_modal_form();
        }


        modal_id.value.show();
    };

    const change_threshold = () => {
        (selected_metric.value.show_volume == true) ? visible.value = true : visible.value = false;
    };

    const change_interface_threshold = () => {
        (selected_interface_metric.value.show_volume == true) ? visible.value = true : visible.value = false;
    };

    const change_vlan_threshold = () => {
        (selected_vlan_metric.value.show_volume == true) ? visible.value = true : visible.value = false;
    };

    const change_asn_threshold = () => {
        (selected_asn_metric.value.show_volume == true) ? visible.value = true : visible.value = false;
    };

    const check_empty_host = () => {
        let regex = new RegExp(regexValidation.get_data_pattern('ip'));
        disable_add.value = !(regex.test(host.value) || host.value === '*');
    };

    const set_active_sign_radio = (selected_radio) => {
        const id = selected_radio.target.id;
        sign_threshold_list.value.forEach((measure) => {
            (measure.id === id) ? measure.active = true : measure.active = false;
        });

    };

    /**
     * 
     * Set the metric type
     */
    const set_active_radio = (selected_radio) => {
        const id = selected_radio.target.id;

        if (metric_type.value.id == 'throughput') {
            throughput_threshold_list.value.forEach((measure) => {
                (measure.id === id) ? measure.active = true : measure.active = false;
            });
        } else if (metric_type.value.id == 'volume') {
            volume_threshold_list.value.forEach((measure) => {
                (measure.id === id) ? measure.active = true : measure.active = false;
            });
        } else if (metric_type.value.id == 'percentage') {
            percentage_threshold_list.forEach((measure) => {
                (measure.id === id) ? measure.active = true : measure.active = false;
            });
        }

    };


    /**
     * Function to add rule to rules list
     */
    const add_ = (is_edit) => {

        let tmp_host = '';
        if (rule_type.value != 'interface')
            tmp_host = host.value;

        const tmp_frequency = selected_frequency.value.id;
        let tmp_metric = selected_metric.value.id;
        let tmp_metric_label = selected_metric.value.label;

        const tmp_rule_type = rule_type.value;

        let tmp_metric_type = metric_type.value.id;
        let tmp_extra_metric;
        let basic_value;
        let basic_sign_value;
        let tmp_threshold;
        // default for host score threshold
        let tmp_sign_value = 1;

        let tmp_edit_row_id = (is_edit) ? row_to_edit_id.value : null;

        if (visible.value === false) {
            tmp_metric_type = '';
            tmp_extra_metric = '';

            // with score the threshold is not visible, do not init to empty the metric type or score rules
            // will be invalid and not correctly added in the backend for checks as 'metric_type' is missing in the request
            if (tmp_metric === 'host:score') {
                tmp_metric_type = 'score';
            }

            tmp_threshold = threshold.value.value;
        }

        if (tmp_metric_type == 'throughput') {
            sign_threshold_list.value.forEach((measure) => { if (measure.active) basic_sign_value = measure.value; });
            tmp_sign_value = parseInt(basic_sign_value);
            throughput_threshold_list.value.forEach((measure) => { if (measure.active) basic_value = measure.value; });
            tmp_threshold = basic_value * parseInt(threshold.value.value);
            /* The throughput is in bit, the volume in Bytes!! */
        } else if (tmp_metric_type == 'volume') {
            sign_threshold_list.value.forEach((measure) => { if (measure.active) basic_sign_value = measure.value; });
            tmp_sign_value = parseInt(basic_sign_value);
            volume_threshold_list.value.forEach((measure) => { if (measure.active) basic_value = measure.value; });
            tmp_threshold = basic_value * parseInt(threshold.value.value);
        } else if (tmp_metric_type == 'percentage') {
            sign_threshold_list.value.forEach((measure) => { if (measure.active) basic_sign_value = measure.value; });
            tmp_sign_value = parseInt(basic_sign_value);
            tmp_threshold = parseInt(threshold.value.value);
        } else if (tmp_metric_type == 'value' || tmp_metric_type == 'absolute_percentage') {
            sign_threshold_list.value.forEach((measure) => { if (measure.active) basic_sign_value = measure.value; });
            tmp_sign_value = parseInt(basic_sign_value);
            tmp_threshold = tmp_sign_value * parseInt(threshold.value.value);
        }
        let emit_name = 'add';

        if (is_edit == true)
            emit_name = 'edit';


        if (rule_type.value == 'Host') {

            tmp_extra_metric = (selected_metric.value.extra_metric) ? selected_metric.value.extra_metric : null;

            emit(emit_name, {
                host: tmp_host,
                frequency: tmp_frequency,
                metric: tmp_metric,
                metric_label: tmp_metric_label,
                threshold: tmp_threshold,
                metric_type: tmp_metric_type,
                extra_metric: tmp_extra_metric,
                rule_type: tmp_rule_type,
                rule_threshold_sign: tmp_sign_value,
                rule_id: tmp_edit_row_id

            });
        } else if (rule_type.value == 'interface') {
            tmp_extra_metric = ((selected_interface_metric.value.extra_metric) ? selected_interface_metric.value.extra_metric : null);
            tmp_metric = selected_interface_metric.value.id;
            tmp_metric_label = selected_interface_metric.value.label;
            const tmp_interface_metric = selected_interface_metric.value.id;
            const tmp_interface = selected_ifid.value.id;
            emit(emit_name, {
                frequency: tmp_frequency,
                metric: tmp_interface_metric,
                metric_label: tmp_metric_label,
                threshold: tmp_threshold,
                metric_type: tmp_metric_type,
                extra_metric: tmp_extra_metric,
                rule_type: tmp_rule_type,
                interface: tmp_interface,
                rule_threshold_sign: tmp_sign_value,
                rule_id: tmp_edit_row_id

            });
        } else if (rule_type.value == "exporter") {
            let flow_device_ifindex = selected_exporter_device_ifid.value.id;
            selected_exporter_device_ifid.value.label;
            const flow_device_ip = selected_exporter_device.value.id;
            if (flow_device_ip == '*')
                flow_device_ifindex = '*';
            const ifid = selected_exporter_device.value.ifid;
            let metric_exp;

            if (!selected_flow_device_metric.value.id) {
                metric_exp = flow_device_metric_list.value.find((item) => item.id === null);
                /* In case no metric id is found, it means it's the traffic one */
                if (flow_device_ifindex != null && flow_device_ifindex != '*') {
                    metric_exp = selected_flow_device_metric.value;
                    metric_exp.id = "flowdev_port:traffic";
                }
                else {
                    metric_exp = selected_flow_device_metric.value;
                    metric_exp.id = "flowdev:traffic";
                }
            } else {
                metric_exp = flow_device_metric_list.value.find((item) => item.id == selected_flow_device_metric.value.id);
            }


            let metric_exp_label = metric_exp.label;

            emit(emit_name, {
                host: flow_device_ip,
                frequency: tmp_frequency,
                metric: metric_exp.id,
                metric_label: metric_exp_label,
                threshold: tmp_threshold,
                metric_type: tmp_metric_type,
                rule_type: tmp_rule_type,
                interface: flow_device_ifindex,
                rule_ifid: ifid,
                rule_threshold_sign: tmp_sign_value,
                rule_id: tmp_edit_row_id

            });
        } else if (rule_type.value == "CIDR") {

            tmp_metric = selected_network_metric.value.schema;
            tmp_metric_label = selected_network_metric.value.label;
            tmp_host = selected_network.value.id;
            const network_id = selected_network.value.network_id;
            emit(emit_name, {
                host: tmp_host,
                frequency: tmp_frequency,
                metric: tmp_metric,
                metric_label: tmp_metric_label,
                threshold: tmp_threshold,
                metric_type: tmp_metric_type,
                extra_metric: tmp_extra_metric,
                rule_type: tmp_rule_type,
                rule_threshold_sign: tmp_sign_value,
                rule_id: tmp_edit_row_id,
                network: network_id

            });
        } else if (rule_type.value == "host_pool") {

            tmp_metric = selected_host_pool_metric.value.schema;
            tmp_metric_label = selected_host_pool_metric.value.label;
            const tmp_host_pool_id = selected_host_pool.value.id;
            const tmp_host_pool_label = selected_host_pool.value.label;

            emit(emit_name, {
                host_pool_id: tmp_host_pool_id,
                host_pool_label: tmp_host_pool_label,
                frequency: tmp_frequency,
                metric: tmp_metric,
                metric_label: tmp_metric_label,
                threshold: tmp_threshold,
                metric_type: tmp_metric_type,
                extra_metric: tmp_extra_metric,
                rule_type: tmp_rule_type,
                rule_threshold_sign: tmp_sign_value,
                rule_id: tmp_edit_row_id

            });

        } else if (rule_type.value == "vlan") {

            tmp_metric = selected_vlan_metric.value.schema;
            tmp_metric_label = selected_vlan_metric.value.label;
            const tmp_vlan_id = selected_vlan.value.id;
            const tmp_vlan_label = selected_vlan.value.label;

            emit(emit_name, {
                vlan_id: tmp_vlan_id,
                vlan_label: tmp_vlan_label,
                frequency: tmp_frequency,
                metric: tmp_metric,
                metric_label: tmp_metric_label,
                threshold: tmp_threshold,
                metric_type: tmp_metric_type,
                extra_metric: tmp_extra_metric,
                rule_type: tmp_rule_type,
                rule_threshold_sign: tmp_sign_value,
                rule_id: tmp_edit_row_id

            });
        } else if (rule_type.value == "profiles") {

            tmp_metric = selected_profile_metric.value.schema;
            tmp_metric_label = selected_profile_metric.value.label;
            const tmp_profile = selected_profile.value.id;

            emit(emit_name, {
                profile: tmp_profile,
                frequency: tmp_frequency,
                metric: tmp_metric,
                metric_label: tmp_metric_label,
                threshold: tmp_threshold,
                metric_type: tmp_metric_type,
                extra_metric: tmp_extra_metric,
                rule_type: tmp_rule_type,
                rule_threshold_sign: tmp_sign_value,
                rule_id: tmp_edit_row_id
            });
        } else if (rule_type.value == "asn") {
            tmp_extra_metric = ((selected_asn_metric.value.extra_metric) ? selected_asn_metric.value.extra_metric : null);
            tmp_metric = selected_asn_metric.value.schema;
            tmp_metric_label = selected_asn_metric.value.label;
            const tmp_asn = selected_asn.value.id;

            emit(emit_name, {
                asn: tmp_asn,
                frequency: tmp_frequency,
                metric: tmp_metric,
                metric_label: tmp_metric_label,
                threshold: tmp_threshold,
                metric_type: tmp_metric_type,
                extra_metric: tmp_extra_metric,
                rule_type: tmp_rule_type,
                rule_threshold_sign: tmp_sign_value,
                rule_id: tmp_edit_row_id
            });
        }

    };


    const edit_ = () => {
        add_(true);
    };

    const close = () => {
        is_edit_page.value = false;
        invalid_add.value = false;
        modal_id.value.close();
    };

    const invalidAdd = () => {
        invalid_add.value = true;
    };

    const compare_labels = function (a, b) {
        let x = a.label.toLowerCase();
        let y = b.label.toLowerCase();

        if (x < y) { return -1; }
        if (x > y) { return 1; }
        return 0;
    };

    /**
     * 
     * Function to format ifid list
     */
    const format_ifid_list = function (data) {
        let _ifid_list = [];
        data.forEach((ifid) => {
            let item = { id: ifid.ifid, label: ifid.name };
            _ifid_list.push(item);
        });
        _ifid_list.sort((a, b) => compare_labels(a, b));
        return _ifid_list
    };

    const metricsLoaded = async (_metric_list, _ifid_list, _interface_metric_list, _flow_exporter_devices, _flow_exporter_devices_metric_list,
        page_csrf, _init_func, _delete_row, _host_pool_list, _network_list, _host_pool_metric_list, _network_metric_list,
        _vlan_list, _vlan_metric_list, _profiles_list, _profiles_metric_list, _asn_list, _asn_metric_list) => {
        metrics_ready.value = true;
        metric_list.value = _metric_list;
        interface_metric_list.value = _interface_metric_list;
        ifid_list.value = format_ifid_list(_ifid_list);

        frequency_list.value = props.frequency_list;
        selected_frequency.value = frequency_list.value[0];
        selected_metric.value = metric_list.value[0];
        selected_ifid.value = ifid_list.value[0];
        page_csrf_.value = page_csrf;
        if (_init_func) {
            init_func.value = _init_func;
        }

        if (_delete_row) {
            delete_row.value = _delete_row;
        }

        flow_exporter_devices.value = format_flow_exporter_device_list(_flow_exporter_devices);

        if (!dataUtils.isEmptyArrayOrNull(_host_pool_list)) {
            has_host_pools.value = true;
        }
        host_pool_list.value = _host_pool_list;
        host_pool_metric_list.value = _host_pool_metric_list;

        if (!dataUtils.isEmptyArrayOrNull(_network_list)) {
            has_cidr.value = true;
        }
        network_list.value = _network_list;
        network_metric_list.value = _network_metric_list;
        flow_device_metric_list.value = _flow_exporter_devices_metric_list;

        selected_exporter_device.value = flow_exporter_devices.value[1];
        if (selected_exporter_device.value != null) {
            update_exporter_interfaces();
        }
        if (props.has_vlans) {
            vlan_list.value = format_vlan_list(_vlan_list);
            selected_vlan.value = vlan_list.value[0];
            vlan_metric_list.value = _vlan_metric_list;
            selected_vlan_metric.value = vlan_metric_list.value[1];
        }
        if (props.has_profiles) {
            profiles_list.value = format_profile_list(_profiles_list);
            selected_profile.value = profiles_list.value[0];
            profiles_metric_list.value = _profiles_metric_list;
            selected_profile_metric.value = profiles_metric_list.value[0];
        }
        if (props.has_asn) {
            asn_list.value = format_asn_list(_asn_list);
            selected_asn.value = asn_list.value[0];
            asn_metric_list.value = _asn_metric_list;
            selected_asn_metric.value = asn_metric_list.value[0];
        }
    };

    /* *************************************************** */

    /* This function updates the exporter interfaces list, 
     * by requesting to the back end the list of interfaces for the selected exporter 
     */
    async function update_exporter_interfaces() {
        let interfaces_list = [];
        if (selected_exporter_device.value.id == '*') {
            return;
        }

        const url_device_exporter_details =
            NtopUtils$1.buildURL(`${http_prefix}/lua/pro/rest/v2/exporters/exporters_interfaces.lua?exporter_uuid=${selected_exporter_device.value.id}&ifid=${selected_exporter_device.value.ifid}&ip=${selected_exporter_device.value.ip}`);

        await $.get(url_device_exporter_details, function (response, status) {
            interfaces_list = response.rsp;
        });

        const exporter_interfaces = [
            { id: "*", value: "*", label: "*", timeseries_available: interfaces_list[0]?.timeseries_available }
        ];

        interfaces_list.forEach((rsp) => {
            exporter_interfaces.push({ id: rsp.ifindex, label: rsp.snmp_ifname, timeseries_available: rsp.timeseries_available });
        });
        flow_exporter_device_ifid_list.value = exporter_interfaces;
        selected_exporter_device_ifid.value = flow_exporter_device_ifid_list.value[1];
    }

    /* *************************************************** */


    /**
     * Function to format flow exporter device list 
     */
    const format_flow_exporter_device_list = function (data) {
        const _f_exp_dev_list = [
            { id: "*", value: "*", label: "*" }
        ];

        data.forEach((dev) => {
            const unique_source_id = dev.unique_source_id;
            _f_exp_dev_list.push({
                id: unique_source_id,
                label: dev.name,
                ip: dev.ip,
                value: unique_source_id,
                ifid: dev.ifid
            });
        });

        _f_exp_dev_list.sort((a, b) => sortingFunctions$1.sortByIP(
            a.label,
            b.label,
            1 /* by default asc */
        ));
        return _f_exp_dev_list;
    };

    /**
     * Function to format vlan list 
     */
    const format_vlan_list = function (data) {
        const f_vlan_list = [];
        data.forEach((vlan) => {
            if (vlan.key != 0) {
                let vlan_label = vlan.key;
                let tag_splitted = vlan.column_vlan.split(">");
                let graphs_splitterd = tag_splitted[1].split("[");
                if (graphs_splitterd.length > 1) {
                    vlan_label = tag_splitted[1].split("<")[0];
                }
                f_vlan_list.push({
                    id: vlan.key,
                    label: vlan_label,
                    value: vlan.key,
                });
            }

        });

        f_vlan_list.sort((a, b) => sortingFunctions$1.sortByName(
            a.label,
            b.label,
            1 /* by default asc */
        ));
        return f_vlan_list;
    };

    /**
     * Function to format profile list 
     */
    const format_profile_list = function (data) {
        const f_profile_list = [];
        data.forEach((profile) => {
            if (profile.profileName != "") {
                f_profile_list.push({
                    id: profile.profileName,
                    label: profile.profileName,
                    value: profile.profileName,
                });
            }

        });

        f_profile_list.sort((a, b) => sortingFunctions$1.sortByName(
            a.id,
            b.id,
            1 /* by default asc */
        ));
        return f_profile_list;
    };

    const format_asn_list = function (data) {
        const f_asn_list = [];
        data.forEach((asn) => {
            if (asn.asn != 0) {
                f_asn_list.push({
                    id: asn.asn,
                    label: asn.asn + " (" + asn.asname + ")",
                    value: asn.traffic,
                });
            }
        });
        return f_asn_list;
    };

    /* *************************************************** */

    /* This function is automatically called whenever a different exporter is selected
     * in order to update the interfaces select dropdown
     */
    const change_exporter_interfaces = function () {
        update_exporter_interfaces();
    };

    /* *************************************************** */

    onBeforeMount(async () => {
        metric_type_list.value.forEach((t) => {
            if (t.active) {
                metric_type.value = t;
            }

        });
        invalid_add.value = false;
        const rsp = await ntopng_utility.http_request(flow_exp_ts_available_url);
        if (rsp && rsp.exporterTimeseriesEnabled != null) {
            flow_device_timeseries_available.value = rsp.exporterTimeseriesEnabled;
        }
    });

    __expose({ show, close, metricsLoaded, invalidAdd });



    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[31] || (_cache[31] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id,
        size: modal_size.value
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          (invalid_add.value)
            ? (openBlock(), createElementBlock("div", _hoisted_1$1H, [
                _cache[32] || (_cache[32] = createBaseVNode("span", { class: "text-info me-1" }, null, -1 /* CACHED */)),
                createBaseVNode("span", null, toDisplayString(_i18n('rule_already_present')), 1 /* TEXT */)
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Target information, here an IP is put "),
          createBaseVNode("div", _hoisted_2$1v, [
            createBaseVNode("label", _hoisted_3$1l, [
              createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.add_rules_type")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_4$1i, [
              createBaseVNode("div", _hoisted_5$13, [
                createBaseVNode("label", {
                  class: normalizeClass(["btn no-wrap", [rule_type.value == 'Host' ? 'btn-primary active' : 'btn-secondary']])
                }, [
                  createBaseVNode("input", {
                    class: "btn-check",
                    type: "radio",
                    name: "rule_type",
                    value: "hosts",
                    onClick: _cache[0] || (_cache[0] = $event => (set_rule_type('Host')))
                  }),
                  createTextVNode(" " + toDisplayString(_i18n("if_stats_config.add_rules_type_host")), 1 /* TEXT */)
                ], 2 /* CLASS */),
                createBaseVNode("label", {
                  class: normalizeClass(["btn no-wrap", [rule_type.value == 'interface' ? 'btn-primary active' : 'btn-secondary']])
                }, [
                  createBaseVNode("input", {
                    onClick: _cache[1] || (_cache[1] = $event => (set_rule_type('interface'))),
                    class: "btn-check",
                    type: "radio",
                    name: "rule_type",
                    value: "interface"
                  }),
                  createTextVNode(" " + toDisplayString(_i18n("if_stats_config.add_rules_type_interface")), 1 /* TEXT */)
                ], 2 /* CLASS */),
                (flow_device_timeseries_available.value == true && __props.isExporterInterface == true)
                  ? (openBlock(), createElementBlock("label", {
                      key: 0,
                      class: normalizeClass(["btn no-wrap", [rule_type.value == 'exporter' ? 'btn-primary active' : 'btn-secondary']])
                    }, [
                      createBaseVNode("input", {
                        onClick: _cache[2] || (_cache[2] = $event => (set_rule_type('exporter'))),
                        class: "btn-check",
                        type: "radio",
                        name: "rule_type",
                        value: "exporter"
                      }),
                      createTextVNode(" " + toDisplayString(_i18n("if_stats_config.add_rules_type_flow_exporter")), 1 /* TEXT */)
                    ], 2 /* CLASS */))
                  : createCommentVNode("v-if", true),
                (unref(has_host_pools) == true)
                  ? (openBlock(), createElementBlock("label", {
                      key: 1,
                      class: normalizeClass(["btn no-wrap", [rule_type.value == 'host_pool' ? 'btn-primary active' : 'btn-secondary']])
                    }, [
                      createBaseVNode("input", {
                        onClick: _cache[3] || (_cache[3] = $event => (set_rule_type('host_pool'))),
                        class: "btn-check",
                        type: "radio",
                        name: "rule_type",
                        value: "host_pool"
                      }),
                      createTextVNode(" " + toDisplayString(_i18n("if_stats_config.add_rules_type_host_pool")), 1 /* TEXT */)
                    ], 2 /* CLASS */))
                  : createCommentVNode("v-if", true),
                (unref(has_cidr) == true)
                  ? (openBlock(), createElementBlock("label", {
                      key: 2,
                      class: normalizeClass(["btn no-wrap", [rule_type.value == 'CIDR' ? 'btn-primary active' : 'btn-secondary']])
                    }, [
                      createBaseVNode("input", {
                        onClick: _cache[4] || (_cache[4] = $event => (set_rule_type('CIDR'))),
                        class: "btn-check",
                        type: "radio",
                        name: "rule_type",
                        value: "CIDR"
                      }),
                      createTextVNode(" " + toDisplayString(_i18n("if_stats_config.add_rules_type_cidr")), 1 /* TEXT */)
                    ], 2 /* CLASS */))
                  : createCommentVNode("v-if", true),
                (props.has_vlans == true)
                  ? (openBlock(), createElementBlock("label", {
                      key: 3,
                      class: normalizeClass(["btn no-wrap", [rule_type.value == 'vlan' ? 'btn-primary active' : 'btn-secondary']])
                    }, [
                      createBaseVNode("input", {
                        onClick: _cache[5] || (_cache[5] = $event => (set_rule_type('vlan'))),
                        class: "btn-check",
                        type: "radio",
                        name: "rule_type",
                        value: "vlan"
                      }),
                      createTextVNode(" " + toDisplayString(_i18n("if_stats_config.add_rules_type_vlans")), 1 /* TEXT */)
                    ], 2 /* CLASS */))
                  : createCommentVNode("v-if", true),
                (props.has_profiles == true)
                  ? (openBlock(), createElementBlock("label", {
                      key: 4,
                      class: normalizeClass(["btn no-wrap", [rule_type.value == 'profiles' ? 'btn-primary active' : 'btn-secondary']])
                    }, [
                      createBaseVNode("input", {
                        onClick: _cache[6] || (_cache[6] = $event => (set_rule_type('profiles'))),
                        class: "btn-check",
                        type: "radio",
                        name: "rule_type",
                        value: "profiles"
                      }),
                      createTextVNode(" " + toDisplayString(_i18n("if_stats_config.add_rules_type_profiles")), 1 /* TEXT */)
                    ], 2 /* CLASS */))
                  : createCommentVNode("v-if", true),
                (props.has_asn == true)
                  ? (openBlock(), createElementBlock("label", {
                      key: 5,
                      class: normalizeClass(["btn no-wrap", [rule_type.value == 'asn' ? 'btn-primary active' : 'btn-secondary']])
                    }, [
                      createBaseVNode("input", {
                        onClick: _cache[7] || (_cache[7] = $event => (set_rule_type('asn'))),
                        class: "btn-check",
                        type: "radio",
                        name: "rule_type",
                        value: "asn"
                      }),
                      createTextVNode(" " + toDisplayString(_i18n("if_stats_config.add_rules_type_asn")), 1 /* TEXT */)
                    ], 2 /* CLASS */))
                  : createCommentVNode("v-if", true)
              ])
            ])
          ]),
          (rule_type.value == 'Host')
            ? (openBlock(), createElementBlock("div", _hoisted_6$S, [
                createBaseVNode("label", _hoisted_7$N, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_8$H, [
                  withDirectives(createBaseVNode("input", {
                    "onUpdate:modelValue": _cache[8] || (_cache[8] = $event => ((host).value = $event)),
                    onInput: check_empty_host,
                    class: "form-control",
                    type: "text",
                    placeholder: unref(host_placeholder),
                    required: ""
                  }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_9$B), [
                    [vModelText, host.value]
                  ])
                ])
              ]))
            : createCommentVNode("v-if", true),
          (rule_type.value == 'CIDR')
            ? (openBlock(), createElementBlock("div", _hoisted_10$w, [
                createBaseVNode("label", _hoisted_11$t, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_12$p, [
                  createVNode(script$2H, {
                    selected_option: selected_network.value,
                    "onUpdate:selected_option": _cache[9] || (_cache[9] = $event => ((selected_network).value = $event)),
                    options: network_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          (rule_type.value == 'host_pool')
            ? (openBlock(), createElementBlock("div", _hoisted_13$m, [
                createBaseVNode("label", _hoisted_14$k, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_15$j, [
                  createVNode(script$2H, {
                    selected_option: selected_host_pool.value,
                    "onUpdate:selected_option": _cache[10] || (_cache[10] = $event => ((selected_host_pool).value = $event)),
                    options: host_pool_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          (rule_type.value == 'interface')
            ? (openBlock(), createElementBlock("div", _hoisted_16$j, [
                createBaseVNode("label", _hoisted_17$h, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target_interface")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_18$e, [
                  createVNode(script$2H, {
                    selected_option: selected_ifid.value,
                    "onUpdate:selected_option": _cache[11] || (_cache[11] = $event => ((selected_ifid).value = $event)),
                    options: ifid_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          (rule_type.value == 'exporter' && flow_device_timeseries_available.value == true && __props.isExporterInterface == true)
            ? (openBlock(), createElementBlock("div", _hoisted_19$d, [
                createBaseVNode("label", _hoisted_20$d, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target_exporter_device")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_21$d, [
                  createVNode(script$2H, {
                    selected_option: selected_exporter_device.value,
                    "onUpdate:selected_option": _cache[12] || (_cache[12] = $event => ((selected_exporter_device).value = $event)),
                    options: flow_exporter_devices.value,
                    onSelect_option: change_exporter_interfaces
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ]),
                (selected_exporter_device.value != '*')
                  ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                      createBaseVNode("label", _hoisted_22$d, [
                        createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target_exporter_device_ifid")), 1 /* TEXT */)
                      ]),
                      createBaseVNode("div", _hoisted_23$d, [
                        createVNode(script$2H, {
                          selected_option: selected_exporter_device_ifid.value,
                          "onUpdate:selected_option": _cache[13] || (_cache[13] = $event => ((selected_exporter_device_ifid).value = $event)),
                          options: flow_exporter_device_ifid_list.value
                        }, null, 8 /* PROPS */, ["selected_option", "options"])
                      ])
                    ], 64 /* STABLE_FRAGMENT */))
                  : createCommentVNode("v-if", true)
              ]))
            : createCommentVNode("v-if", true),
          (rule_type.value == 'vlan')
            ? (openBlock(), createElementBlock("div", _hoisted_24$c, [
                createBaseVNode("label", _hoisted_25$b, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target_vlan")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_26$a, [
                  createVNode(script$2H, {
                    selected_option: selected_vlan.value,
                    "onUpdate:selected_option": _cache[14] || (_cache[14] = $event => ((selected_vlan).value = $event)),
                    options: vlan_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Traffic Profiles "),
          (rule_type.value == 'profiles')
            ? (openBlock(), createElementBlock("div", _hoisted_27$a, [
                createBaseVNode("label", _hoisted_28$8, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target_profile")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_29$8, [
                  createVNode(script$2H, {
                    selected_option: selected_profile.value,
                    "onUpdate:selected_option": _cache[15] || (_cache[15] = $event => ((selected_profile).value = $event)),
                    options: profiles_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" ASN "),
          (rule_type.value == 'asn')
            ? (openBlock(), createElementBlock("div", _hoisted_30$8, [
                createBaseVNode("label", _hoisted_31$7, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.target_asn")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_32$7, [
                  createVNode(script$2H, {
                    selected_option: selected_asn.value,
                    "onUpdate:selected_option": _cache[16] || (_cache[16] = $event => ((selected_asn).value = $event)),
                    options: asn_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Metric information, here a metric is selected (e.g. DNS traffic) "),
          (metrics_ready.value)
            ? (openBlock(), createElementBlock("div", _hoisted_33$7, [
                createBaseVNode("label", _hoisted_34$6, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.metric")), 1 /* TEXT */)
                ]),
                (rule_type.value == 'Host')
                  ? (openBlock(), createElementBlock("div", _hoisted_35$6, [
                      createVNode(script$2H, {
                        selected_option: selected_metric.value,
                        "onUpdate:selected_option": _cache[17] || (_cache[17] = $event => ((selected_metric).value = $event)),
                        onSelect_option: _cache[18] || (_cache[18] = $event => (change_threshold())),
                        options: metric_list.value
                      }, null, 8 /* PROPS */, ["selected_option", "options"])
                    ]))
                  : (rule_type.value == 'interface')
                    ? (openBlock(), createElementBlock("div", _hoisted_36$6, [
                        createVNode(script$2H, {
                          selected_option: selected_interface_metric.value,
                          "onUpdate:selected_option": _cache[19] || (_cache[19] = $event => ((selected_interface_metric).value = $event)),
                          onSelect_option: _cache[20] || (_cache[20] = $event => (change_interface_threshold())),
                          options: interface_metric_list.value
                        }, null, 8 /* PROPS */, ["selected_option", "options"])
                      ]))
                    : (rule_type.value == 'exporter')
                      ? (openBlock(), createElementBlock("div", _hoisted_37$5, [
                          createVNode(script$2H, {
                            selected_option: selected_flow_device_metric.value,
                            "onUpdate:selected_option": _cache[21] || (_cache[21] = $event => ((selected_flow_device_metric).value = $event)),
                            onSelect_option: change_metric_type_exporter,
                            options: flow_device_metric_list.value
                          }, null, 8 /* PROPS */, ["selected_option", "options"])
                        ]))
                      : (rule_type.value == 'host_pool')
                        ? (openBlock(), createElementBlock("div", _hoisted_38$5, [
                            createVNode(script$2H, {
                              selected_option: selected_host_pool_metric.value,
                              "onUpdate:selected_option": _cache[22] || (_cache[22] = $event => ((selected_host_pool_metric).value = $event)),
                              onSelect_option: _cache[23] || (_cache[23] = $event => (change_metric_type_hp())),
                              options: host_pool_metric_list.value
                            }, null, 8 /* PROPS */, ["selected_option", "options"])
                          ]))
                        : (rule_type.value == 'CIDR')
                          ? (openBlock(), createElementBlock("div", _hoisted_39$4, [
                              createVNode(script$2H, {
                                selected_option: selected_network_metric.value,
                                "onUpdate:selected_option": _cache[24] || (_cache[24] = $event => ((selected_network_metric).value = $event)),
                                onSelect_option: _cache[25] || (_cache[25] = $event => (change_metric_type_hp())),
                                options: network_metric_list.value
                              }, null, 8 /* PROPS */, ["selected_option", "options"])
                            ]))
                          : (rule_type.value == 'vlan')
                            ? (openBlock(), createElementBlock("div", _hoisted_40$4, [
                                createVNode(script$2H, {
                                  selected_option: selected_vlan_metric.value,
                                  "onUpdate:selected_option": _cache[26] || (_cache[26] = $event => ((selected_vlan_metric).value = $event)),
                                  options: vlan_metric_list.value,
                                  onSelect_option: change_vlan_threshold
                                }, null, 8 /* PROPS */, ["selected_option", "options"])
                              ]))
                            : (rule_type.value == 'profiles')
                              ? (openBlock(), createElementBlock("div", _hoisted_41$4, [
                                  createVNode(script$2H, {
                                    selected_option: selected_profile_metric.value,
                                    "onUpdate:selected_option": _cache[27] || (_cache[27] = $event => ((selected_profile_metric).value = $event)),
                                    options: profiles_metric_list.value
                                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                                ]))
                              : (rule_type.value == 'asn')
                                ? (openBlock(), createElementBlock("div", _hoisted_42$4, [
                                    createVNode(script$2H, {
                                      selected_option: selected_asn_metric.value,
                                      "onUpdate:selected_option": _cache[28] || (_cache[28] = $event => ((selected_asn_metric).value = $event)),
                                      options: asn_metric_list.value,
                                      onSelect_option: change_asn_threshold
                                    }, null, 8 /* PROPS */, ["selected_option", "options"])
                                  ]))
                                : createCommentVNode("v-if", true)
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Frequency information, a frequency of 1 day, 5 minute or 1 hour for example "),
          (metrics_ready.value)
            ? (openBlock(), createElementBlock("div", _hoisted_43$4, [
                createBaseVNode("label", _hoisted_44$4, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.frequency")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_45$4, [
                  createVNode(script$2H, {
                    selected_option: selected_frequency.value,
                    "onUpdate:selected_option": _cache[29] || (_cache[29] = $event => ((selected_frequency).value = $event)),
                    options: frequency_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Threshold information, maximum amount of bytes "),
          createBaseVNode("div", _hoisted_46$4, [
            createBaseVNode("label", _hoisted_47$3, [
              createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.threshold")), 1 /* TEXT */)
            ]),
            (visible.value)
              ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                  createBaseVNode("div", _hoisted_48$3, [
                    createVNode(script$2H, {
                      selected_option: metric_type.value,
                      "onUpdate:selected_option": _cache[30] || (_cache[30] = $event => ((metric_type).value = $event)),
                      options: unref(active_metric_type_list)
                    }, null, 8 /* PROPS */, ["selected_option", "options"])
                  ]),
                  createBaseVNode("div", {
                    class: normalizeClass(["col-3", [metric_type.value.id == 'throughput' ? 'p-0' : '']])
                  }, [
                    createBaseVNode("div", _hoisted_49$3, [
                      (metric_type.value.id == 'throughput')
                        ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(throughput_threshold_list.value, (measure) => {
                            return (openBlock(), createElementBlock(Fragment, null, [
                              createBaseVNode("input", {
                                value: measure.value,
                                id: measure.id,
                                type: "radio",
                                class: "btn-check",
                                autocomplete: "off",
                                ref_for: true,
                                ref_key: "threshold_measure",
                                ref: threshold_measure,
                                name: "threshold_measure"
                              }, null, 8 /* PROPS */, _hoisted_50$3),
                              createBaseVNode("label", {
                                class: normalizeClass(["btn", [measure.active ? 'btn-primary active' : 'btn-secondary']]),
                                id: measure.id,
                                onClick: set_active_radio,
                                for: measure.id
                              }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_51$3)
                            ], 64 /* STABLE_FRAGMENT */))
                          }), 256 /* UNKEYED_FRAGMENT */))
                        : createCommentVNode("v-if", true),
                      (metric_type.value.id == 'percentage')
                        ? (openBlock(), createElementBlock(Fragment, { key: 1 }, renderList(percentage_threshold_list, (measure) => {
                            return (openBlock(), createElementBlock(Fragment, null, [
                              createBaseVNode("input", {
                                value: measure.value,
                                id: measure.id,
                                type: "radio",
                                class: "btn-check",
                                autocomplete: "off",
                                ref_for: true,
                                ref_key: "threshold_measure",
                                ref: threshold_measure,
                                name: "threshold_measure"
                              }, null, 8 /* PROPS */, _hoisted_52$2),
                              createBaseVNode("label", {
                                class: normalizeClass(["btn", [measure.active ? 'btn-primary active' : 'btn-secondary']]),
                                id: measure.id,
                                onClick: set_active_radio,
                                for: measure.id
                              }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_53$2)
                            ], 64 /* STABLE_FRAGMENT */))
                          }), 64 /* STABLE_FRAGMENT */))
                        : createCommentVNode("v-if", true),
                      (metric_type.value.id == 'volume')
                        ? (openBlock(true), createElementBlock(Fragment, { key: 2 }, renderList(volume_threshold_list.value, (measure) => {
                            return (openBlock(), createElementBlock(Fragment, null, [
                              createBaseVNode("input", {
                                value: measure.value,
                                id: measure.id,
                                type: "radio",
                                class: "btn-check",
                                autocomplete: "off",
                                ref_for: true,
                                ref_key: "threshold_measure",
                                ref: threshold_measure,
                                name: "threshold_measure"
                              }, null, 8 /* PROPS */, _hoisted_54$2),
                              createBaseVNode("label", {
                                class: normalizeClass(["btn", [measure.active ? 'btn-primary active' : 'btn-secondary']]),
                                id: measure.id,
                                onClick: set_active_radio,
                                for: measure.id
                              }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_55$2)
                            ], 64 /* STABLE_FRAGMENT */))
                          }), 256 /* UNKEYED_FRAGMENT */))
                        : createCommentVNode("v-if", true)
                    ])
                  ], 2 /* CLASS */),
                  createBaseVNode("div", _hoisted_56$2, [
                    (openBlock(true), createElementBlock(Fragment, null, renderList(sign_threshold_list.value, (measure) => {
                      return (openBlock(), createElementBlock(Fragment, null, [
                        createBaseVNode("input", {
                          value: measure.value,
                          id: measure.id,
                          type: "radio",
                          class: "btn-check",
                          autocomplete: "off",
                          ref_for: true,
                          ref_key: "threshold_sign",
                          ref: threshold_sign,
                          name: "threshold_sign"
                        }, null, 8 /* PROPS */, _hoisted_57$2),
                        createBaseVNode("label", {
                          class: normalizeClass(["btn", [measure.active ? 'btn-primary active' : 'btn-secondary']]),
                          id: measure.id,
                          onClick: set_active_sign_radio,
                          for: measure.id
                        }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_58$2)
                      ], 64 /* STABLE_FRAGMENT */))
                    }), 256 /* UNKEYED_FRAGMENT */))
                  ])
                ], 64 /* STABLE_FRAGMENT */))
              : createCommentVNode("v-if", true),
            createBaseVNode("div", {
              class: normalizeClass([visible.value ? 'col-sm-2' : 'col-sm-8'])
            }, [
              (metric_type.value.id == 'percentage')
                ? (openBlock(), createElementBlock("input", {
                    key: 0,
                    value: "1",
                    ref_key: "threshold",
                    ref: threshold,
                    type: "number",
                    name: "threshold",
                    class: "form-control",
                    max: "100",
                    min: "1",
                    required: ""
                  }, null, 512 /* NEED_PATCH */))
                : (openBlock(), createElementBlock("input", {
                    key: 1,
                    value: "1",
                    ref_key: "threshold",
                    ref: threshold,
                    type: "number",
                    name: "threshold",
                    class: "form-control",
                    max: "1023",
                    min: "1",
                    required: ""
                  }, null, 512 /* NEED_PATCH */))
            ], 2 /* CLASS */)
          ]),
          (metric_type.value.id == 'percentage')
            ? (openBlock(), createElementBlock("div", _hoisted_59$2, toDisplayString(_i18n("show_alerts.traffic_rules_percentage")), 1 /* TEXT */))
            : createCommentVNode("v-if", true)
        ]),
        footer: withCtx(() => [
          createVNode(script$2u, {
            note_list: note_list,
            add_sub_notes: true,
            sub_note_list: sub_notes_list
          }),
          (is_edit_page.value == false)
            ? (openBlock(), createElementBlock("button", {
                key: 0,
                type: "button",
                onClick: add_,
                class: "btn btn-primary",
                disabled: disable_add.value && rule_type.value == 'Host'
              }, toDisplayString(_i18n('add')), 9 /* TEXT, PROPS */, _hoisted_60$2))
            : (openBlock(), createElementBlock("button", {
                key: 1,
                type: "button",
                onClick: edit_,
                class: "btn btn-primary",
                disabled: disable_add.value && rule_type.value == 'Host'
              }, toDisplayString(_i18n('apply')), 9 /* TEXT, PROPS */, _hoisted_61$2))
        ]),
        _: 1 /* STABLE */
      }, 8 /* PROPS */, ["size"]))
    }
    }

    };

    script$1R.__file = "http_src/vue/modal-add-traffic-rules.vue";

    const _hoisted_1$1G = { class: "row" };
    const _hoisted_2$1u = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1k = { class: "card" };
    const _hoisted_4$1h = { class: "card-body" };
    const _hoisted_5$12 = { class: "mb-4" };
    const _hoisted_6$R = { id: "traffic_rules" };
    const _hoisted_7$M = { class: "card-footer" };


    var script$1Q = {
      __name: 'page-traffic-rules',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const isExporterInterface = props.context.isExporterInterface;
    const table_traffic_rules = ref(null);
    const modal_delete_confirm = ref(null);
    const modal_add_host_rule = ref(null);
    const _i18n = (t) => i18n(t);
    const row_to_delete = ref({});
    const row_to_edit = ref({});
    const invalid_add = ref(false);
    const isLoading = ref(false);

    const metric_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/metrics.lua?rule_type=host`;
    const metric_ifname_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/metrics.lua?rule_type=interface`;

    const metric_host_pool_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/metrics.lua?rule_type=host_pool`;
    const metric_network_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/metrics.lua?rule_type=CIDR`;
    const metric_vlan_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/metrics.lua?rule_type=vlan`;
    const metric_profiles_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/metrics.lua?rule_type=profiles`;
    const metric_asn_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/metrics.lua?rule_type=asn`;
    const metric_flow_exp_device_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/metrics.lua?rule_type=exporter`;

    const flow_devices_url = `${http_prefix}/lua/pro/rest/v2/get/flowdevices/list.lua`;
`${http_prefix}/lua/pro/enterprise/flowdevice_details.lua`;
    const host_pool_url = `${http_prefix}/lua/rest/v2/get/host/pool/pools.lua`;
    const network_list_url = `${http_prefix}/lua/rest/v2/get/network/networks.lua`;
    const ifid_url = `${http_prefix}/lua/rest/v2/get/ntopng/interfaces.lua`;
    const vlans_url = `${http_prefix}/lua/get_vlans_data.lua`;
    const profiles_url = `${http_prefix}/lua/pro/get_traffic_profiles.lua`;
    const asn_url = `${http_prefix}/lua/rest/v2/get/asn/get_as_data.lua?sort=num_hosts&order=desc&show_as=all`;

    const data_url = `${http_prefix}/lua/pro/rest/v2/get/interface/traffic_rules/data.lua`;
    const add_rule_url = `${http_prefix}/lua/pro/rest/v2/add/interface/traffic_rules/rule.lua`;
    const remove_rule_url = `${http_prefix}/lua/pro/rest/v2/delete/interface/traffic_rules/rule.lua`;

    const note_list = [
      _i18n('if_stats_config.generic_notes_1'),
      _i18n('if_stats_config.generic_notes_2'),
      _i18n('if_stats_config.generic_notes_3'),
    ];

    const rest_params = {
      ifid: props.context.ifid,
      csrf: props.context.page_csrf,
      gui: true // Some API requires this to return html content for backward compatibility
    };

    let traffic_rules_table_config = {};
    let title_delete = _i18n('if_stats_config.delete_traffic_title');
    _i18n('if_stats_config.edit_local_network_rules');
    let body_delete = _i18n('if_stats_config.delete_traffic_description');
    let metric_list = [];
    let interface_metric_list = [];
    let host_pool_metric_list = [];
    let ifid_list = [];
    let flow_exporter_list = [];
    let flow_exporter_metric_list = [];
    let host_pool_list = [];
    let network_list = [];
    let network_metric_list = [];
    let vlan_list = [];
    let vlan_metric_list = [];
    let profiles_list = [];
    let profiles_metric_list = [];
    let asn_list = [];
    let asn_metric_list = [];


    const frequency_list = [
      { title: i18n('show_alerts.5_min'), label: i18n('show_alerts.5_min'), id: '5min', value: '5min' },
      { title: i18n('show_alerts.hourly'), label: i18n('show_alerts.hourly'), id: 'hour', value: 'hour' },
      { title: i18n('show_alerts.daily'), label: i18n('show_alerts.daily'), id: 'day', value: 'day' },
      { title: i18n('show_alerts.monthly'), label: i18n('show_alerts.monthly'), id: 'month', value: 'month' }
    ];

    const show_delete_dialog = function (row) {
      row_to_delete.value = row;
      modal_delete_confirm.value.show();
    };

    const load_selected_field = function (row) {
      row_to_edit.value = row;

      row_to_delete.value = row;

      modal_add_host_rule.value.show(row);

    };

    async function edit(params) {
      //await delete_row();

      await add_host_rule(params);
    }
    const init_edit = function () {
      const row = row_to_edit.value;
      row_to_edit.value = null;
      return row;
    };

    const destroy_table = function () {
      table_traffic_rules.value.destroy_table();
    };

    const reload_table = function () {
      table_traffic_rules.value.reload();
    };

    const delete_row = async function () {
      const row = row_to_delete.value;
      const url = NtopUtils$1.buildURL(remove_rule_url, {
        ...rest_params,
        ...{
          rule_id: row.id,
          rule_type: row.rule_type
        }
      });

      await $.post(url, function (rsp, status) {
        reload_table();
      });
    };

    const add_host_rule = async function (params) {

      params.csrf = props.context.page_csrf;
      params.ifid = props.context.ifid;
      const rsp = await ntopng_utility.http_post_request(add_rule_url, params);

      invalid_add.value = rsp.rsp;

      if (invalid_add.value == false) {
        modal_add_host_rule.value.close();
        reload_table();
      } else {
        modal_add_host_rule.value.invalidAdd();
      }

    };


    const add_action_column = function (rowData) {
      let delete_handler = {
        handlerId: "delete_host",
        onClick: () => {
          show_delete_dialog(rowData);
        },
      };

      let edit_handler = {
        handlerId: "edit_rule",
        onClick: () => {
          load_selected_field(rowData);
        },
      };

      return DataTableUtils.createActionButtons([
        { class: `pointer`, handler: edit_handler, icon: 'fa-edit', title: i18n('edit') },
        { class: `pointer`, handler: delete_handler, icon: 'fa-trash', title: i18n('delete') },
      ]);
    };

    const format_metric = function (data, rowData) {
      let metric_label = data;

      if (rowData.metric_label) {
        metric_label = rowData.metric_label;
      } else {
        if (rowData.rule_type != 'interface') {
          metric_list.forEach((metric) => {
            if (metric.id == data) {
              if (rowData.extra_metric) {
                if (rowData.extra_metric == metric.extra_metric)
                  metric_label = metric.label;
              } else {
                metric_label = metric.label;
              }
            }
          });
        } else {
          interface_metric_list.forEach((metric) => {
            if (metric.id == data) {
              if (rowData.extra_metric) {
                if (rowData.extra_metric == metric.extra_metric)
                  metric_label = metric.label;
              } else {
                metric_label = metric.label;
              }
            }
          });
        }
      }

      return metric_label
    };

    const format_frequency = function (data) {
      let frequency_title = '';
      frequency_list.forEach((frequency) => {
        if (data == frequency.id)
          frequency_title = frequency.title;
      });

      return frequency_title
    };

    const format_threshold = function (data, rowData) {
      let formatted_data = parseInt(data);
      let threshold_sign = "> ";

      if ((rowData.threshold_sign) && (rowData.threshold_sign == '-1'))
        threshold_sign = "< ";

      if ((rowData.metric_type) && (rowData.metric_type == 'throughput')) {
        formatted_data = threshold_sign + NtopUtils$1.bitsToSize(data);
      } else if ((rowData.metric_type) && (rowData.metric_type == 'volume')) {
        formatted_data = threshold_sign + NtopUtils$1.bytesToSize(data);
      } else if ((rowData.metric_type) && (rowData.metric_type.contains('percentage'))) {
        if (data < 0) {
          data = data * (-1);
        }
        formatted_data = threshold_sign + NtopUtils$1.fpercent(data);
      } else if ((rowData.metric_type) && (rowData.metric_type == 'value')) {
        if (data < 0) {
          data = data * (-1);
        }
        formatted_data = threshold_sign + data;
      }

      return formatted_data
    };

    const format_last_measurement = function (data, rowData) {
      let formatted_data = parseInt(data);
      if (rowData.target == "*") {
        return "";
      }

      if (data == null) {
        return "";
      }
      if ((rowData.metric_type) && (rowData.metric_type == 'throughput')) {
        formatted_data = NtopUtils$1.bitsToSize(data);
      } else if ((rowData.metric_type) && (rowData.metric_type == 'volume')) {
        formatted_data = NtopUtils$1.bytesToSize(data);
      } else if ((rowData.metric_type) && (rowData.metric_type.includes('percentage'))) {
        const sign_data = data < 0 ? -1 : 1;
        const absolute_value = NtopUtils$1.fpercent(data * sign_data);
        formatted_data = sign_data == -1 ? `<label title='${i18n("percentage_decrease")}'> (-) ${absolute_value} </label>` : `<label title='${i18n("percentage_increase")}'>${absolute_value}</label>`;
      }

      return formatted_data
    };

    const format_rule_type = function (data, rowData) {
      let formatted_data = '';
      if ((rowData.rule_type) && (rowData.rule_type == 'interface')) {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("interface") + " <i class='fas fa-ethernet'></i></span>";
      } else if ((rowData.rule_type) && (rowData.rule_type == 'Host')) {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("about.host_checks_directory") + " <i class='fas fa-laptop'></i></span>";
      } else if ((rowData.rule_type) && rowData.rule_type == 'host_pool') {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("alert_entities.host_pool") + " <i class='fas fa-laptop'></i></span>";

      } else if ((rowData.rule_type) && rowData.rule_type == 'CIDR') {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("network") + " <i class='fas fa-laptop'></i></span>";

      } else if ((rowData.rule_type) && (rowData.rule_type == 'exporter') && rowData.metric == "flowdev:traffic") {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("flow_exporter_device") + " <i class='fas fa-laptop'></i></span>";

      } else if ((rowData.rule_type) && (rowData.rule_type == 'exporter') && rowData.metric.includes("flowdev_port")) {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("interface_flow_exporter_device") + " <i class='fas fa-ethernet'></i></span>";
      } else if ((rowData.rule_type) && rowData.rule_type == 'vlan') {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("vlan") + " <i class='fas fa-ethernet'></i></span>";
      } else if ((rowData.rule_type) && rowData.rule_type == 'profiles') {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("if_stats_config.target_profile") + " <i class='fas fa-ethernet'></i></span>";
      }
      else if ((rowData.rule_type) && rowData.rule_type == 'asn') {
        formatted_data = "<span class='badge bg-secondary'>" + _i18n("if_stats_config.target_asn") + " <i class='fas fa-ethernet'></i></span>";
      }

      return formatted_data;
    };

    const format_target = function (data, rowData) {
      let formatted_data = '';
      if ((rowData.rule_type) && (rowData.rule_type == 'interface')) {
        formatted_data = rowData.selected_iface;
      } else if (rowData.rule_type && (rowData.rule_type == 'Host' || rowData.rule_type == 'CIDR')) {
        formatted_data = rowData.target;
      } else if (rowData.rule_type == 'host_pool') {
        formatted_data = rowData.host_pool_label;
      } else if (rowData.rule_type == 'vlan') {
        formatted_data = rowData.vlan_label;
      } else if (rowData.rule_type == 'profiles') {
        formatted_data = rowData.target;
      }  else if (rowData.rule_type == 'asn') {
        formatted_data = rowData.target + " (" + rowData.name + ")";
      }else if (rowData.rule_type && rowData.rule_type == 'exporter' && rowData.metric == "flowdev:traffic") {
        formatted_data = rowData.target;
      } else {
        let interface_label = rowData.flow_exp_ifid_name != "" && rowData.flow_exp_ifid_name != null ? rowData.flow_exp_ifid_name : rowData.flow_exp_ifid;
        formatted_data = rowData.target + " " + _i18n("on_interface") + ": " + interface_label;
      }
      return formatted_data;
    };

    const get_metric_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(metric_url, rest_params);
      metric_list = await ntopng_utility.http_request(url);
      isLoading.value = false;
    };


    const get_host_pool_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(host_pool_url, rest_params);
      
      let tmp_host_pool_list = await ntopng_utility.http_request(url);

      tmp_host_pool_list.sort((a, b) => (a.label > b.label) ? 1 : ((b.label > a.label) ? -1 : 0));
      host_pool_list = tmp_host_pool_list;
      isLoading.value = false;
    };

    const get_network_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(network_list_url, rest_params);

      let tmp_network_list = await ntopng_utility.http_request(url);

      tmp_network_list.sort((a, b) => (a.label > b.label) ? 1 : ((b.label > a.label) ? -1 : 0));
      network_list = tmp_network_list;
      isLoading.value = false;

    };

    const get_interface_metric_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(metric_ifname_url, rest_params);
      interface_metric_list = await ntopng_utility.http_request(url);
      isLoading.value = false;
    };

    const get_host_pool_metric_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(metric_host_pool_url, rest_params);

      let tmp_host_pool_metric_list = await ntopng_utility.http_request(url);

      tmp_host_pool_metric_list.sort((a, b) => (a.label > b.label) ? 1 : ((b.label > a.label) ? -1 : 0));
      host_pool_metric_list = tmp_host_pool_metric_list;
      isLoading.value = false;
    };


    const get_network_metric_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(metric_network_url, rest_params);

      let tmp_network_metric_list = await ntopng_utility.http_request(url);

      tmp_network_metric_list.sort((a, b) => (a.label > b.label) ? 1 : ((b.label > a.label) ? -1 : 0));
      network_metric_list = tmp_network_metric_list;
      isLoading.value = false;

    };

    const get_vlan_metric_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(metric_vlan_url, rest_params);

      let tmp_vlan_metric_list = await ntopng_utility.http_request(url);

      tmp_vlan_metric_list.sort((a, b) => (a.label > b.label) ? 1 : ((b.label > a.label) ? -1 : 0));
      vlan_metric_list = tmp_vlan_metric_list;
      isLoading.value = false;

    };

    const get_profiles_metric_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(metric_profiles_url, rest_params);

      let tmp_profiles_metric_list = await ntopng_utility.http_request(url);
      
      profiles_metric_list = tmp_profiles_metric_list;
      isLoading.value = false;
    };

    const get_asn_metric_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(metric_asn_url, rest_params);
      let tmp_asn_metric_list = await ntopng_utility.http_request(url);
      asn_metric_list = tmp_asn_metric_list;
      isLoading.value = false;
    };

    const get_flow_exporter_devices_metric_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(metric_flow_exp_device_url, {
        ...rest_params
      });
      flow_exporter_metric_list = await ntopng_utility.http_request(url);
      isLoading.value = false;

    };

    const get_flow_exporter_devices_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(flow_devices_url, {
        ...rest_params
      });
      flow_exporter_list = await ntopng_utility.http_request(url);
      isLoading.value = false;

    };

    const get_ifid_list = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(ifid_url, rest_params);
      ifid_list = await ntopng_utility.http_request(url);
      isLoading.value = false;
    };

    const get_vlans = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(vlans_url, rest_params);

      await $.get(url, function (rsp, status) {
        vlan_list = JSON.parse(rsp).data;
        isLoading.value = false;
      });
    };

    const get_profiles = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(profiles_url, rest_params);
      profiles_list = await ntopng_utility.http_request(url);
      isLoading.value = false;
    };

    const get_asn = async function () {
      isLoading.value = true;
      const url = NtopUtils$1.buildURL(asn_url, rest_params);
      asn_list = await ntopng_utility.http_request(url);
      isLoading.value = false;
    };

    const start_datatable = function () {
      const datatableButton = [];

      /* Manage the buttons close to the search box */
      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function () {
          reload_table();
        }
      }, {
        text: '<i class="fas fa-plus"></i>',
        className: 'btn-link',
        action: function () {
          modal_add_host_rule.value.show();
        }
      });

      const columns = [
        { columnName: _i18n("actions"), width: '5%', targets: 0, name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, render: function (_, type, rowData) { return add_action_column(rowData) } },
        { columnName: _i18n("id"), visible: false, targets: 1, name: 'id', data: 'id', className: 'text-nowrap', responsivePriority: 1 },
        { columnName: _i18n("if_stats_config.target"), targets: 2, width: '20', name: 'target', data: 'target', className: 'text-nowrap', responsivePriority: 1, render: function (data, _, rowData) { return format_target(data, rowData) } },
        { columnName: _i18n("if_stats_config.rule_type"), targets: 3, width: '20', name: 'rule_type', data: 'rule_type', className: 'text-center', responsivePriority: 1, render: function (data, _, rowData) { return format_rule_type(data, rowData) } },
        { columnName: _i18n("if_stats_config.metric"), targets: 4, width: '10', name: 'metric', data: 'metric', className: 'text-center', responsivePriority: 1, render: function (data, _, rowData) { return format_metric(data, rowData) } },
        { columnName: _i18n("if_stats_config.frequency"), targets: 5, width: '10', name: 'frequency', data: 'frequency', className: 'text-center', responsivePriority: 1, render: function (data) { return format_frequency(data) } },
        { columnName: _i18n("if_stats_config.last_measurement"), targets: 6, width: '10', name: 'last_measurement', data: 'last_measurement', className: 'text-center', responsivePriority: 1, render: function (data, _, rowData) { return format_last_measurement(data, rowData) } },
        { columnName: _i18n("if_stats_config.threshold"), targets: 7, width: '10', name: 'threshold', data: 'threshold', className: 'text-end', responsivePriority: 1, render: function (data, _, rowData) { return format_threshold(data, rowData) } },
        { columnName: _i18n("metric_type"), visible: false, targets: 8, name: 'metric_type', data: 'metric_type', className: 'text-nowrap', responsivePriority: 1 },
      ];

      const hostRulesTableConfig = {
        table_buttons: datatableButton,
        data_url: NtopUtils$1.buildURL(data_url, rest_params),
        enable_search: true,
        columns_config: columns,
        table_config: {
          scrollX: false,
          serverSide: false,
          order: [[1 /* target */, 'desc']],
          columnDefs: columns
        }
      };

      traffic_rules_table_config = hostRulesTableConfig;
    };

    onBeforeMount(async () => {
      start_datatable();
      await get_metric_list();
      await get_ifid_list();
      await get_interface_metric_list();
      await get_flow_exporter_devices_metric_list();
      await get_flow_exporter_devices_list();
      await get_host_pool_list();
      await get_host_pool_metric_list();
      await get_network_list();
      await get_network_metric_list();
      if (props.context.has_asn) {
        await get_asn_metric_list();
        await get_asn();
      }
      if (props.context.has_vlans) {
        await get_vlans();
        await get_vlan_metric_list();
      }
      if (props.context.has_profiles) {
        await get_profiles();
        await get_profiles_metric_list();
      }
      modal_add_host_rule.value.metricsLoaded(metric_list, ifid_list, interface_metric_list, flow_exporter_list, flow_exporter_metric_list, props.context.page_csrf, null, null, host_pool_list, network_list, host_pool_metric_list, network_metric_list, vlan_list, vlan_metric_list, profiles_list, profiles_metric_list, asn_list, asn_metric_list);
    });

    onUnmounted(() => {
      destroy_table();
    });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1G, [
        createBaseVNode("div", _hoisted_2$1u, [
          createBaseVNode("div", _hoisted_3$1k, [
            createBaseVNode("div", _hoisted_4$1h, [
              createBaseVNode("div", _hoisted_5$12, [
                createBaseVNode("h4", null, toDisplayString(_i18n('if_stats_config.traffic_rules')), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_6$R, [
                createVNode(script$2y, { isLoading: isLoading.value }, null, 8 /* PROPS */, ["isLoading"]),
                createVNode(script$2a, {
                  ref_key: "modal_delete_confirm",
                  ref: modal_delete_confirm,
                  title: unref(title_delete),
                  body: unref(body_delete),
                  onDelete: delete_row
                }, null, 8 /* PROPS */, ["title", "body"]),
                createVNode(script$1R, {
                  ref_key: "modal_add_host_rule",
                  ref: modal_add_host_rule,
                  metric_list: unref(metric_list),
                  interface_metric_list: unref(interface_metric_list),
                  frequency_list: frequency_list,
                  init_func: init_edit,
                  has_vlans: props.context.has_vlans,
                  has_profiles: props.context.has_profiles,
                  has_asn: props.context.has_asn,
                  isExporterInterface: unref(isExporterInterface),
                  onAdd: add_host_rule,
                  onEdit: edit
                }, null, 8 /* PROPS */, ["metric_list", "interface_metric_list", "has_vlans", "has_profiles", "has_asn", "isExporterInterface"]),
                createVNode(script$2e, {
                  ref_key: "table_traffic_rules",
                  ref: table_traffic_rules,
                  table_buttons: unref(traffic_rules_table_config).table_buttons,
                  columns_config: unref(traffic_rules_table_config).columns_config,
                  data_url: unref(traffic_rules_table_config).data_url,
                  enable_search: unref(traffic_rules_table_config).enable_search,
                  table_config: unref(traffic_rules_table_config).table_config
                }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "table_config"])
              ])
            ]),
            createBaseVNode("div", _hoisted_7$M, [
              createVNode(script$2u, { note_list: note_list })
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1Q.__file = "http_src/vue/page-traffic-rules.vue";

    const _hoisted_1$1F = { class: "row" };
    const _hoisted_2$1t = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1j = { class: "card" };
    const _hoisted_4$1g = { class: "card-body" };
    const _hoisted_5$11 = { id: "host_details_ports" };
    const _hoisted_6$Q = {
      class: "row mb-4 mt-4",
      id: "host_details_ports"
    };
    const _hoisted_7$L = { class: "col-6" };


    var script$1P = {
      __name: 'page-host-details-ports',
      props: {
      page_csrf: String,
    },
      setup(__props) {

    ref(null);
    ref([]);
    ref({});

    const _i18n = (t) => i18n(t);

    function print_stats_column(col) {
      return col.label;
    }

    function print_stats_row(col, row) {
      // debugger;
      let label = row.label;
      return label;
    }

    const destroy = () => {
      traffic_table.value.destroy_table();
    };
        
    onBeforeMount(async () => {
      await start_datatable();
    });

    onMounted(async () => {
      NtopUtils$1.hideOverlays();
    });

    onUnmounted(async () => {
      destroy();
    });

    [
      {
        title: i18n('graphs.cli_ports'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/port/cli_port_data.lua`,
        id: `cli_port_flows`,
      },
      {
        title: i18n('graphs.srv_ports'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/host/port/srv_port_data.lua`,
        id: `srv_port_flows`,
      },
    ];

    const table_options = [
      {
        url: `${http_prefix}/lua/rest/v2/get/host/port/table_port_data.lua`,
        id: `cli_port_flows`,
        extra_params: {
          mode: 'local',
          protocol: 'tcp'
        },
        columns: [
          { id: "active_server_tcp_ports", label: _i18n("ports_page.active_server_tcp_ports") },
          { id: "port_application", label: _i18n("port") },
        ],
        stats_rows: [],
      },
      {
        url: `${http_prefix}/lua/rest/v2/get/host/port/table_port_data.lua`,
        id: `cli_port_flows`,
        extra_params: {
          mode: 'local',
          protocol: 'udp'
        },
        columns: [
          { id: "active_server_udp_ports", label: _i18n("ports_page.active_server_udp_ports") },
          { id: "port_application", label: _i18n("port") },
        ],
        stats_rows: [],
      },
      {
        url: `${http_prefix}/lua/rest/v2/get/host/port/table_port_data.lua`,
        id: `srv_port_flows`,
        extra_params: {
          mode: 'remote',
          protocol: 'tcp'
        },
        columns: [
          { id: "client_contacted_server_tcp_ports", label: _i18n("ports_page.client_contacted_server_tcp_ports") },
          { id: "port_application", label: _i18n("port") },
        ],
        stats_rows: [],
      },
      {
        url: `${http_prefix}/lua/rest/v2/get/host/port/table_port_data.lua`,
        id: `srv_port_flows`,
        extra_params: {
          mode: 'remote',
          protocol: 'udp'
        },
        columns: [
          { id: "client_contacted_server_udp_ports", label: _i18n("ports_page.client_contacted_server_udp_ports") },
          { id: "port_application", label: _i18n("port") },
        ],
        stats_rows: [],
      },
    ];

    async function start_datatable() {
      let url_params = {};
      
      url_params["host"] = ntopng_url_manager$1.get_url_entry("host");
      url_params["vlan"] = ntopng_url_manager$1.get_url_entry("vlan");
      url_params["ifid"] = ntopng_url_manager$1.get_url_entry("ifid");
        
      table_options.forEach((table) => {
        let tmp_params = {
          ...table.extra_params,
          ...url_params,
        };

        $.get(NtopUtils$1.buildURL(table.url, tmp_params), async function(data, status){
          // debugger;
          let rows = [];
          data.rsp.forEach((data) => {
            const port = data.port_info.port;
            const proto = data.port_info.l7_proto;
            rows.push({ label: `${port} (${proto})` });
          });
          table.stats_rows = rows;
        });
      });
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1F, [
        createBaseVNode("div", _hoisted_2$1t, [
          createBaseVNode("div", _hoisted_3$1j, [
            _cache[0] || (_cache[0] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
              createBaseVNode("div", { class: "text-center" }, [
                createBaseVNode("div", {
                  class: "spinner-border text-primary mt-5",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
                ])
              ])
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_4$1g, [
              createBaseVNode("div", _hoisted_5$11, [
                createBaseVNode("div", _hoisted_6$Q, [
                  (openBlock(), createElementBlock(Fragment, null, renderList(table_options, (table_option) => {
                    return createBaseVNode("div", _hoisted_7$L, [
                      createVNode(script$2d, {
                        id: table_option.id,
                        columns: table_option.columns,
                        rows: table_option.stats_rows,
                        print_html_column: (col) => print_stats_column(col),
                        print_html_row: (col, row) => print_stats_row(col, row)
                      }, null, 8 /* PROPS */, ["id", "columns", "rows", "print_html_column", "print_html_row"])
                    ])
                  }), 64 /* STABLE_FRAGMENT */))
                ])
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1P.__file = "http_src/vue/page-host-details-ports.vue";

    const _hoisted_1$1E = { class: "row" };
    const _hoisted_2$1s = { class: "col-12" };
    const _hoisted_3$1i = { class: "card" };
    const _hoisted_4$1f = { class: "card-body" };
    const _hoisted_5$10 = {
      class: "align-items-center justify-content-end mb-3",
      style: {"height":"70vh"}
    };
    const _hoisted_6$P = { class: "d-flex ms-auto flex-row-reverse" };
    const _hoisted_7$K = {
      key: 0,
      class: "m-1"
    };
    const _hoisted_8$G = { style: {"min-width":"14rem"} };
    const _hoisted_9$A = { class: "my-auto me-1" };

    const widget_name$1 = 'alerts-map';

    var script$1O = {
      __name: 'page-alert-analysis',
      props: {
      ifid: String,
      page_csrf: String,
      charts_options: Array,
      available_filters: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const props = __props;

    const loading = ref(true);
    const chart_type = ntopChartApex.typeChart.BUBBLE;
    const rest_url = `${http_prefix}/lua/pro/rest/v2/charts/alert/analysis.lua`;
    const active_filter_list = {};
    const bubble_chart = ref(null);

    const format_request = function() {
      let params_string = '';
      const params = {
        bubble_mode: ntopng_url_manager$1.get_url_entry('bubble_mode'),
        timeframe: ntopng_url_manager$1.get_url_entry('timeframe'),
        vlan: ntopng_url_manager$1.get_url_entry('vlan'),
        ifid: ntopng_url_manager$1.get_url_entry('ifid'),
      };

      for (const [key, value] of Object.entries(params)) {
        params_string = `${params_string}&${key}=${value}`;
      }

      /* Return a custom string, containing custom options */
      return params_string
    };

    const reload = function() {
      loading.value = true;
      bubble_chart.value.update_chart(`${rest_url}?${format_request()}`);
      loading.value = false;
    };

    const format_options = function(mode_id) {
      let options = {};

      props.charts_options.forEach((option_list) => {
        if(option_list.mode_id == mode_id)
          options = option_list;
      });

      /* Add the correct event functions */
      if(options && options.chart && options.chart.ntop_events) {
        options.chart.events = options.chart.events || {};
        for(const [event, fun] of Object.entries(options.chart.ntop_events)) {
          if(fun == undefined) 
            continue

          options.chart.events[event] = NtopUtils$1[fun] || NtopUtils$1.fnone;
        }
      }

      /* Add the correct formatting function, given from the backend */
      if(options && options.xaxis && options.xaxis.labels && options.xaxis.labels.ntop_utils_formatter) {
        options.xaxis.labels.formatter = NtopUtils$1[options.xaxis.labels.ntop_utils_formatter] || NtopUtils$1.fnone;
      }

      /* Add the correct formatting function, given from the backend */
      if(options && options.yaxis && options.yaxis.labels && options.yaxis.labels.ntop_utils_formatter) {
        options.yaxis.labels.formatter = NtopUtils$1[options.yaxis.labels.ntop_utils_formatter] || NtopUtils$1.fnone;
      }

      /* Add the correct formatting function, given from the backend */
      if(options && options.tooltip && options.tooltip.ntop_utils_formatter)
        options.tooltip.custom = NtopUtils$1[options.tooltip.ntop_utils_formatter];

      return options
    };

    const get_f_get_custom_chart_options = function() {
      
      /* Return the list of formatted options of the chart */
      return async (url) => {
        let options = format_options(Number(active_filter_list['bubble_mode'].id));
        const data = await ntopng_utility.http_request(url);
        options.series = data.series || {};
        return options
      }
    };

    const click_item = function(item) {
      loading.value = true;
      ntopng_url_manager$1.set_key_to_url(item.filter_name, item.id);
      bubble_chart.value.update_chart(`${rest_url}?${format_request()}`);
      loading.value = false;
    };

    onBeforeMount(() => {
      /* Before mounting the various widgets, update the url to the correct one, by adding ifid, ecc. */
      const timeframe = ntopng_url_manager$1.get_url_entry('timeframe');
      const vlan = ntopng_url_manager$1.get_url_entry('vlan');
      const bubble_mode = ntopng_url_manager$1.get_url_entry('bubble_mode');
      
      if(!bubble_mode) ntopng_url_manager$1.set_key_to_url('bubble_mode', 0); /* First Entry */
      if(!timeframe) ntopng_url_manager$1.set_key_to_url('timeframe', 300); /* Default 5 min */
      if(!vlan) ntopng_url_manager$1.set_key_to_url('vlan', ''); /* Default no vlan */
      
      ntopng_url_manager$1.set_key_to_url('ifid', props.ifid); /* Current interface */

      for(const [name, filters] of Object.entries(props.available_filters)) {
        filters.forEach((filter) => {
          filter.filter_name = name;
          if(filter.currently_active)
            active_filter_list[name] = filter;
        });
      }
    });

    onMounted(() => {
      loading.value = false;
    });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1E, [
        createBaseVNode("div", _hoisted_2$1s, [
          createBaseVNode("div", _hoisted_3$1i, [
            createBaseVNode("div", _hoisted_4$1f, [
              createBaseVNode("div", _hoisted_5$10, [
                createBaseVNode("div", _hoisted_6$P, [
                  createBaseVNode("div", null, [
                    _cache[1] || (_cache[1] = createBaseVNode("label", { class: "my-auto me-1" }, null, -1 /* CACHED */)),
                    createBaseVNode("div", null, [
                      createBaseVNode("button", {
                        class: "btn btn-link m-1",
                        tabindex: "0",
                        type: "button",
                        onClick: reload
                      }, [...(_cache[0] || (_cache[0] = [
                        createBaseVNode("span", null, [
                          createBaseVNode("i", { class: "fas fa-sync" })
                        ], -1 /* CACHED */)
                      ]))])
                    ])
                  ]),
                  (openBlock(true), createElementBlock(Fragment, null, renderList(__props.available_filters, (value, key, index) => {
                    return (openBlock(), createElementBlock(Fragment, null, [
                      (value.length > 0)
                        ? (openBlock(), createElementBlock("div", _hoisted_7$K, [
                            createBaseVNode("div", _hoisted_8$G, [
                              createBaseVNode("label", _hoisted_9$A, toDisplayString(_i18n('bubble_map.' + key)) + ": ", 1 /* TEXT */),
                              createVNode(script$2H, {
                                selected_option: active_filter_list[key],
                                "onUpdate:selected_option": $event => ((active_filter_list[key]) = $event),
                                options: value,
                                onSelect_option: click_item
                              }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options"])
                            ])
                          ]))
                        : createCommentVNode("v-if", true)
                    ], 64 /* STABLE_FRAGMENT */))
                  }), 256 /* UNKEYED_FRAGMENT */))
                ]),
                createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
                createBaseVNode("div", {
                  id: widget_name$1,
                  style: {"height":"90%"},
                  class: normalizeClass([ loading.value ? 'ntopng-gray-out' : '' ])
                }, [
                  createVNode(script$2I, {
                    ref_key: "bubble_chart",
                    ref: bubble_chart,
                    id: widget_name$1,
                    chart_type: unref(chart_type),
                    base_url_request: rest_url,
                    get_params_url_request: format_request,
                    get_custom_chart_options: get_f_get_custom_chart_options(),
                    register_on_status_change: false
                  }, null, 8 /* PROPS */, ["chart_type", "get_custom_chart_options"])
                ], 2 /* CLASS */)
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1O.__file = "http_src/vue/page-alert-analysis.vue";

    const _hoisted_1$1D = { class: "row" };
    const _hoisted_2$1r = { class: "col-12" };
    const _hoisted_3$1h = { class: "card" };
    const _hoisted_4$1e = { class: "card-body" };
    const _hoisted_5$$ = {
      class: "align-items-center justify-content-end mb-3",
      style: {"height":"70vh"}
    };
    const _hoisted_6$O = { class: "d-flex ms-auto flex-row-reverse" };
    const _hoisted_7$J = { class: "m-1" };
    const _hoisted_8$F = {
      key: 0,
      style: {"min-width":"18rem"}
    };
    const _hoisted_9$z = { class: "my-auto me-1" };

    const widget_name = 'alerts-map';

    var script$1N = {
      __name: 'page-host-map',
      props: {
      ifid: String,
      page_csrf: String,
      charts_options: Array,
      available_filters: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const props = __props;

    /* By default use the first entry */
    const chart_type = ntopChartApex.typeChart.BUBBLE;
    const rest_url = `${http_prefix}/lua/pro/rest/v2/charts/host/map.lua`;
    const active_filter_list = {};
    const bubble_chart = ref(null);

    const format_request = function() {
      let params_string = '';
      const params = {
        bubble_mode: ntopng_url_manager$1.get_url_entry('bubble_mode'),
        ifid: ntopng_url_manager$1.get_url_entry('ifid'),
      };

      for (const [key, value] of Object.entries(params)) {
        params_string = `${params_string}&${key}=${value}`;
      }

      /* Return a custom string, containing custom options */
      return params_string
    };

    const format_options = function(mode_id) {
      let options = {};

      props.charts_options.forEach((option_list) => {
        if(option_list.mode_id == mode_id)
          options = option_list;
      });

      /* Add the corract event functions */
      if(options && options.chart && options.chart.ntop_events) {
        options.chart.events = options.chart.events || {};
        for(const [event, fun] of Object.entries(options.chart.ntop_events)) {
          if(fun == undefined) 
            continue

          options.chart.events[event] = NtopUtils$1[fun] || NtopUtils$1.fnone;
        }
      }

      /* Add the correct formatting function, given from the backend */
      if(options && options.xaxis && options.xaxis.labels && options.xaxis.labels.ntop_utils_formatter) {
        options.xaxis.labels.formatter = NtopUtils$1[options.xaxis.labels.ntop_utils_formatter] || NtopUtils$1.fnone;
      }

      /* Add the correct formatting function, given from the backend */
      if(options && options.yaxis && options.yaxis.labels && options.yaxis.labels.ntop_utils_formatter) {
        options.yaxis.labels.formatter = NtopUtils$1[options.yaxis.labels.ntop_utils_formatter] || NtopUtils$1.fnone;
      }

      /* Add the correct formatting function, given from the backend */
      if(options && options.tooltip && options.tooltip.ntop_utils_formatter)
        options.tooltip.custom = NtopUtils$1[options.tooltip.ntop_utils_formatter];

      return options
    };

    const get_f_get_custom_chart_options = function() {
      
      /* Return the list of formatted options of the chart */
      return async (url) => {
        let options = format_options(Number(active_filter_list['bubble_mode'].id));
        const data = await ntopng_utility.http_request(url);
        options.series = data.series || {};
        return options
      }
    };

    const click_item = function(item) {
      ntopng_url_manager$1.set_key_to_url(item.filter_name, item.id);
      bubble_chart.value.update_chart(`${rest_url}?${format_request()}`);
    };

    onBeforeMount(() => {
      /* Before mounting the various widgets, update the url to the correct one, by adding ifid, ecc. */
      const bubble_mode = ntopng_url_manager$1.get_url_entry('bubble_mode');
      
      if(!bubble_mode) ntopng_url_manager$1.set_key_to_url('bubble_mode', 0); /* First Entry */
      
      ntopng_url_manager$1.set_key_to_url('ifid', props.ifid); /* Current interface */

      for(const [name, filters] of Object.entries(props.available_filters)) {
        filters.forEach((filter) => {
          filter.filter_name = name;
          if(filter.currently_active)
            active_filter_list[name] = filter;
        });
      }
    });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1D, [
        createBaseVNode("div", _hoisted_2$1r, [
          createBaseVNode("div", _hoisted_3$1h, [
            createBaseVNode("div", _hoisted_4$1e, [
              createBaseVNode("div", _hoisted_5$$, [
                createBaseVNode("div", _hoisted_6$O, [
                  _cache[0] || (_cache[0] = createBaseVNode("label", { class: "my-auto me-1" }, null, -1 /* CACHED */)),
                  (openBlock(true), createElementBlock(Fragment, null, renderList(__props.available_filters, (value, key, index) => {
                    return (openBlock(), createElementBlock("div", _hoisted_7$J, [
                      (value.length > 0)
                        ? (openBlock(), createElementBlock("div", _hoisted_8$F, [
                            createBaseVNode("label", _hoisted_9$z, toDisplayString(_i18n('bubble_map.' + key)) + ": ", 1 /* TEXT */),
                            createVNode(script$2H, {
                              selected_option: active_filter_list[key],
                              "onUpdate:selected_option": $event => ((active_filter_list[key]) = $event),
                              options: value,
                              onSelect_option: click_item
                            }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options"])
                          ]))
                        : createCommentVNode("v-if", true)
                    ]))
                  }), 256 /* UNKEYED_FRAGMENT */))
                ]),
                createBaseVNode("div", {
                  id: widget_name,
                  style: {"height":"90%"}
                }, [
                  createVNode(script$2I, {
                    ref_key: "bubble_chart",
                    ref: bubble_chart,
                    id: widget_name,
                    chart_type: unref(chart_type),
                    base_url_request: rest_url,
                    get_params_url_request: format_request,
                    get_custom_chart_options: get_f_get_custom_chart_options(),
                    register_on_status_change: false
                  }, null, 8 /* PROPS */, ["chart_type", "get_custom_chart_options"])
                ])
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1N.__file = "http_src/vue/page-host-map.vue";

    const _hoisted_1$1C = { class: "button-group mb-2 d-flex align-items-center" };
    const _hoisted_2$1q = {
      key: 0,
      class: "dropdown me-2 d-flex"
    };
    const _hoisted_3$1g = { class: "no-wrap d-flex align-items-center filters-label me-2" };
    const _hoisted_4$1d = ["title"];
    const _hoisted_5$_ = { class: "m-2 mb-3" };
    const _hoisted_6$N = {
      class: "mb-3 d-flex flex-column",
      style: {"height":"70vh"}
    };
    const _hoisted_7$I = { class: "card-footer" };



    var script$1M = {
      __name: 'page-vlan-ports-sankey',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const _i18n = (t) => i18n(t);
    const max_entries_reached = ref(false);
    const max_entry_title = _i18n('ports_analysis.max_entries');
    const no_data_message = _i18n('ports_analysis.no_data');
    const sankey_chart = ref(null);
    const page_filters = ref([]);
    const page_filters_title = ref([]);
    const active_filter_list = ref([]);
    const sankey_data = ref({});
    ref('server_ports');
    const live_rest = `${http_prefix}/lua/pro/rest/v2/get/vlan/live_ports.lua`;
    const historical_rest = `${http_prefix}/lua/pro/rest/v2/get/vlan/historical_ports.lua`;
    let current_rest = live_rest;
    const loading = ref(true);
    const note_list = [
        _i18n("server_ports.notes"),
    ];

    onBeforeMount(() => {
        let index = 0;
        for (const [name, filters] of Object.entries(props.context.available_filters)) {
            filters.forEach((filter) => {
                filter.filter_name = name;
                if (filter.currently_active)
                    active_filter_list.value.push(filter);
            });
            page_filters.value.push(filters);
            page_filters_title.value.push(name);
            if (!active_filter_list.value[index]) {
                active_filter_list.value[index] = filters[0]; /* No filter, use the first one */
            }
            index = index + 1;
        }
    });

    onMounted(() => {
        updateSankeyData();
    });

    /* ************************************** */

    const updateSankeyData = async () => {
        loading.value = true;
        let data = await getSankeyData();
        sankey_data.value = data;
        loading.value = false;
    };

    /* ************************************** */

    const changedOption = (opt) => {
        if (opt.filter_name === "timeframe" && opt.id !== 'none') {
            current_rest = historical_rest;
        } else {
            current_rest = live_rest;
        }
        ntopng_url_manager$1.set_key_to_url(opt.filter_name, opt.id);
        updateSankeyData();
    };

    /* ************************************** */

    const getSankeyData = async () => {
        const url_request = getSankeyUrl();
        let graph = await ntopng_utility$1.http_request(url_request);
        graph.nodes.forEach((node, i) => {
            node.index = i;
        });
        graph.links.forEach((link, i) => {
            if (link.value === 0) {
                link.value = 1;
            }
            let node = graph.nodes.find((el) => el.node_id == link.source_node_id);
            link.source = node.index;
            node = graph.nodes.find((el) => el.node_id == link.target_node_id);
            link.target = node.index;
        });

        return graph
    };

    /* ***************************************************** */

    const getExtraParameters = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    const getSankeyUrl = () => {
        let params = {
            ifid: props.context.ifid,
            ...getExtraParameters()
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        let url_request = `${current_rest}?${url_params}`;
        return url_request;
    };

    /* ************************************** */

    function onNodeClick(_, node) {
        if (node.link) {
            ntopng_url_manager$1.go_to_url(node.link);
        }
    }

    /* ************************************** */

    const onAutoRefreshToggle = (enabled) => {
        if (enabled) {
            intervalId = setInterval(() => {
                updateSankeyData();
            }, 10000 /* 10 sec refresh */);
        } else {
            clearInterval(intervalId);
        }
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$1C, [
          (openBlock(true), createElementBlock(Fragment, null, renderList(page_filters.value, (value, index) => {
            return (openBlock(), createElementBlock(Fragment, null, [
              (value.length > 0)
                ? (openBlock(), createElementBlock("div", _hoisted_2$1q, [
                    createBaseVNode("span", _hoisted_3$1g, [
                      createBaseVNode("b", null, toDisplayString(_i18n('server_ports.' +
                            page_filters_title.value[index])) + ": ", 1 /* TEXT */)
                    ]),
                    createVNode(script$2H, {
                      selected_option: active_filter_list.value[index],
                      "onUpdate:selected_option": $event => ((active_filter_list.value[index]) = $event),
                      options: value,
                      onSelect_option: changedOption
                    }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options"])
                  ]))
                : createCommentVNode("v-if", true)
            ], 64 /* STABLE_FRAGMENT */))
          }), 256 /* UNKEYED_FRAGMENT */)),
          (max_entries_reached.value == true)
            ? (openBlock(), createElementBlock("div", {
                key: 0,
                class: "mt-auto m-1",
                title: unref(max_entry_title),
                style: {"cursor":"help"}
              }, [...(_cache[0] || (_cache[0] = [
                createBaseVNode("button", {
                  type: "button",
                  class: "btn btn-link",
                  disabled: ""
                }, [
                  createBaseVNode("i", { class: "text-danger fa-solid fa-triangle-exclamation" })
                ], -1 /* CACHED */)
              ]))], 8 /* PROPS */, _hoisted_4$1d))
            : createCommentVNode("v-if", true)
        ]),
        createBaseVNode("div", _hoisted_5$_, [
          createBaseVNode("div", _hoisted_6$N, [
            createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
            createVNode(script$1T, {
              ref_key: "sankey_chart",
              ref: sankey_chart,
              no_data_message: unref(no_data_message),
              sankey_data: sankey_data.value,
              onNode_click: onNodeClick,
              onAutorefresh_toggle: onAutoRefreshToggle
            }, null, 8 /* PROPS */, ["no_data_message", "sankey_data"])
          ]),
          createBaseVNode("div", _hoisted_7$I, [
            createVNode(script$2u, { note_list: note_list })
          ])
        ])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1M.__file = "http_src/vue/page-vlan-ports-sankey.vue";

    /*
      (C) 2013-24 - ntop.org
     */

    const flowInfo = {
        'Audio': "<i class='fa fa-lg fa-volume-up'></i>",
        'Video': "<i class='fa fa-lg fa-video'></i>",
        'Desktop Sharing': "<i class='fa fa-lg fa-binoculars'></i>",
    };

    /* *********************************** */

    const addFlowInfoIcon = function(info) {
        let formatted_info = info;

        if (info && flowInfo[info]) {
            formatted_info = `${flowInfo[info]} ${info}`;
        }
        return formatted_info;
    };

    /* *********************************** */

    const infoUtils = function () {
        return {
            addFlowInfoIcon
        };
    }();

    const _hoisted_1$1B = { class: "row" };
    const _hoisted_2$1p = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$1f = { class: "d-flex align-items-center mb-2" };
    const _hoisted_4$1c = {
      class: "d-flex no-wrap",
      style: {"text-align":"left","margin-right":"1rem","min-width":"25rem"}
    };
    const _hoisted_5$Z = { class: "my-auto me-1" };
    const _hoisted_6$M = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_7$H = { class: "no-wrap d-flex align-items-center filters-label" };
    const _hoisted_8$E = { class: "d-flex justify-content-center align-items-center" };


    var script$1L = {
      __name: 'page-aggregated-live-flows',
      props: {
        context: Object
    },
      setup(__props) {

    const props = __props;

    const csrf = ref(props.context.csrf);
    const _i18n = (t) => i18n(t);

    const criteria_list_def = [
        { label: _i18n("application_proto"), value: 1, param: "application_protocol", table_id: "aggregated_app_proto", enterprise_m: false, search_enabled: true, asn_mode: false },
        { label: _i18n("client"), value: 2, param: "client", table_id: "aggregated_client", enterprise_m: false, search_enabled: false, asn_mode: true },
        { label: _i18n("client_server"), value: 4, param: "client_server", table_id: "aggregated_client_server", enterprise_m: true, search_enabled: false, asn_mode: true },
        { label: _i18n("client_server_application_proto"), value: 5, param: "app_client_server", table_id: "aggregated_app_client_server", enterprise_m: true, search_enabled: true, asn_mode: false },
        { label: _i18n("client_server_srv_port"), value: 7, param: "client_server_srv_port", table_id: "aggregated_client_server_srv_port", enterprise_m: false, search_enabled: false, asn_mode: true },
        { label: _i18n("client_server_srv_port_app_proto"), value: 8, param: "client_server_srv_port_app_proto", table_id: "aggregated_client_server_srv_port_app_proto", enterprise_m: false, search_enabled: false, asn_mode: false },
        { label: _i18n("info"), value: 6, param: "info", table_id: "aggregated_info", enterprise_m: true, search_enabled: true, asn_mode: false },
        { label: _i18n("server"), value: 3, param: "server", table_id: "aggregated_server", enterprise_m: false, search_enabled: false, asn_mode: true },
        { label: _i18n("src_as_dst_as"), value: 9, param: "src_as_dst_as", table_id: "aggregated_src_as_dst_as", enterprise_m: true, search_enabled: false, asn_mode: true },
        { label: _i18n("src_as_transit_as_dst_as"), value: 10, param: "src_as_transit_as_dst_as", table_id: "aggregated_src_as_transit_as_dst_as", enterprise_m: true, search_enabled: false, asn_mode: true },
        { label: _i18n("db_explorer.host_data"), value: 11, param: "host", table_id: "host", enterprise_m: true, search_enabled: true, asn_mode: true },
    ];

    const loading = ref(false);
    const table_aggregated_live_flows = ref();
    const filter_table_array = ref([]);
    const filters = ref([]);

    const table_config_id = ref('aggregated_live_flows');
    const table_id = computed$1(() => {
        if (selected_criteria.value?.value == null) { return table_config_id.value; }
        let id = `${table_config_id.value}_${selected_criteria.value.value}`;
        return id;
    });
    const criteria_list = function () {
        let critera_list_def_com = [];
        criteria_list_def.forEach((c) => {
            let add = true;
            if (c.enterprise_m && !props.context.is_ntop_enterprise_m) {
                add = false;
            }
            if (props.context.asn_mode && !c.asn_mode) {
                add = false;
            }
            if (add) {
                critera_list_def_com.push(c);
            }
        });
        return ref(critera_list_def_com);
    }();
    const selected_criteria = ref({});

    onBeforeMount(async () => {
        init_selected_criteria();
        load_table_filters_array();
    });

    function init_selected_criteria() {
        let aggregation_criteria = ntopng_url_manager$1.get_url_entry("aggregation_criteria");
        if (aggregation_criteria == null || aggregation_criteria == "") {
            return;
        }
        const tmp_criteria = criteria_list.value.find((c) => c.param == aggregation_criteria);
        // In case it's not found, use the first available criteria
        if (!tmp_criteria) {
            selected_criteria.value = criteria_list.value[0];
            ntopng_url_manager$1.set_key_to_url("aggregation_criteria", selected_criteria.value.param);
        } else {
            selected_criteria.value = tmp_criteria;
        }
    }

    async function update_criteria() {
        ntopng_url_manager$1.set_key_to_url("aggregation_criteria", selected_criteria.value.param);
    }
    const get_extra_params_obj = () => {
        /*let params = get_url_params(active_page, per_page, columns_wrap, map_search, first_get_rows);
          set_params_in_url(params);*/
        let params = get_url_params();
        return params;
    };

    /* ************************************** */

    function reset_filters() {
        filter_table_array.value.forEach((el, index) => {
            /* Getting the currently selected filter */
            ntopng_url_manager$1.set_key_to_url(el.id, ``);
        });
        load_table_filters_array();
        table_aggregated_live_flows.value.refresh_table();
    }

    /* ************************************** */

    function set_filters_list(res) {
        if (!res) {
            filter_table_array.value = filters.value.filter((t) => {
                if (t.show_with_key) {
                    const key = ntopng_url_manager$1.get_url_entry(t.show_with_key);
                    if (key !== t.show_with_value) {
                        return false
                    }
                }
                return true
            });
        } else {
            filters.value = res.map((t) => {
                const key_in_url = ntopng_url_manager$1.get_url_entry(t.name);
                if (key_in_url === null) {
                    ntopng_url_manager$1.set_key_to_url(t.name, ``);
                }
                return {
                    id: t.name,
                    label: t.label,
                    title: t.tooltip,
                    options: t.value,
                    show_with_key: t.show_with_key,
                    show_with_value: t.show_with_value,
                };
            });
            set_filters_list();
            return;
        }
        set_filter_array_label();
    }

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            /* Setting the basic label */
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            /* Getting the currently selected filter */
            const url_entry = ntopng_url_manager$1.get_url_entry(el.id);
            el.options.forEach((option) => {
                if (option.value.toString() === url_entry) {
                    el.current_option = option;
                }
            });
        });
    }

    /* ************************************** */

    function add_table_filter(opt, opt2) {
        ntopng_url_manager$1.set_key_to_url(opt.key, `${opt.value}`);
        if (opt2) {
            ntopng_url_manager$1.set_key_to_url(opt2.key, `${opt2.value}`);
        }
        table_aggregated_live_flows.value.refresh_table();
        load_table_filters_array();
    }

    /* ************************************** */

    async function load_table_filters_array() {
        loading.value = true;
        let extra_params = get_extra_params_obj();
        let url_params = ntopng_url_manager$1.obj_to_url_params(extra_params);
        const url = `${http_prefix}/lua/rest/v2/get/flow/aggregated_live_flows_filters.lua?${url_params}`;
        const res = await ntopng_utility$1.http_request(url);
        set_filters_list(res);
        loading.value = false;
    }

    /* ************************************** */

    function get_url_params() {
        let actual_params = {
            ifid: ntopng_url_manager$1.get_url_entry("ifid") || props.context.ifid,
            vlan_id: ntopng_url_manager$1.get_url_entry("vlan_id")  /* No filter by default */,
            deviceIP: ntopng_url_manager$1.get_url_entry("deviceIP"),
            inIfIdx: ntopng_url_manager$1.get_url_entry("inIfIdx"),
            outIfIdx: ntopng_url_manager$1.get_url_entry("outIfIdx"),
            ifIdx: ntopng_url_manager$1.get_url_entry("ifIdx"),
            aggregation_criteria: ntopng_url_manager$1.get_url_entry("aggregation_criteria") || selected_criteria.value.param,
            host: ntopng_url_manager$1.get_url_entry("host") || props.context.host,
        };

        return actual_params;
    }


    const map_config = (config) => {
        config.enable_search = selected_criteria.value.search_enabled == true;
        return config;
    };

    /// methods to get columns config
    const map_table_def_columns = async (columns) => {
        columns = [];
        columns.push(
            {
                sortable: false, title_i18n: 'flows_page.live_flows', name: 'flows_icon', data_field: 'live_flows', class: ['text-center'], responsivePriority: 1, render_func: (data_field, rowData) => {
                    return format_flows_icon(data_field, rowData)
                }
            });

        if (selected_criteria.value.value == 1) {

            // application protocol case
            columns.push(
                {
                    title_i18n: "application_proto", sortable: true, name: 'application', data_field: 'application', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                        return format_application_proto_guessed(data_field, rowData)
                        //return `${data_field.label_with_icons}`
                    }
                },
                /*{
                    title_i18n: "application_proto_guessed",sortable: false, name: 'application', data_field: 'is_not_guessed', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                        return format_application_proto_guessed(data_field, rowData)
                    }
                }*/
            );
        }
        else if (selected_criteria.value.value == 2) {
            // client case
            columns.push(
                {
                    title_i18n: "client", sortable: true, name: 'client', data_field: 'client', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {

                        return format_client_name(data_field)
                    }
                });
        }
        else if (selected_criteria.value.value == 3) {
            // server case
            columns.push(
                {
                    title_i18n: "last_server", sortable: true, name: 'server', data_field: 'server', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                        return format_server_name(data_field, rowData)
                    }
                });
        }
        else if (selected_criteria.value.value == 7) {
            columns.push(
                {
                    title_i18n: "client", sortable: true, name: 'client', data_field: 'client', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                        return format_client_name(data_field)
                    }
                }, {
                title_i18n: "last_server", sortable: true, name: 'server', data_field: 'server', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                    return format_server_name(data_field, rowData);
                }
            });
        } else if (selected_criteria.value.value == 8) {
            columns.push(

                {
                    title_i18n: "client", sortable: true, name: 'client', data_field: 'client', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                        return format_client_name(data_field)
                    }
                }, {
                title_i18n: "last_server", sortable: true, name: 'server', data_field: 'server', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                    return format_server_name(data_field, rowData);
                }
            },
                {
                    title_i18n: "application_proto", sortable: true, name: 'application', data_field: 'application', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                        return format_application_proto_guessed(data_field, rowData);
                        //return `${data_field.label_with_icons}`
                    }
                }
            );
        }
        else if (props.context.is_ntop_enterprise_m) {
            if (selected_criteria.value.value == 4 || selected_criteria.value.value == 7) {
                columns.push(
                    {
                        title_i18n: "client", sortable: true, name: 'client', data_field: 'client', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                            return format_client_name(data_field)
                        }
                    }, {
                    title_i18n: "last_server", sortable: true, name: 'server', data_field: 'server', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                        return format_server_name(data_field, rowData);
                    }
                });
            } else if (selected_criteria.value.value == 5) {
                columns.push(
                    {
                        title_i18n: "client", sortable: true, name: 'client', data_field: 'client', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                            return format_client_name(data_field);
                        }
                    },
                    {
                        title_i18n: "last_server", sortable: true, name: 'server', data_field: 'server', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                            return format_server_name(data_field, rowData);
                        }
                    },
                    {
                        title_i18n: "application_proto", sortable: true, name: 'application', data_field: 'application', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                            return format_application_proto_guessed(data_field, rowData);
                            //return `${data_field.label_with_icons}`
                        }
                    });
            } else if (selected_criteria.value.value == 6) {
                columns.push(
                    {
                        title_i18n: "info", sortable: true, name: 'info', data_field: 'info', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field) => {
                            return `${infoUtils.addFlowInfoIcon(data_field.label)}`
                        }
                    });
            } else if (selected_criteria.value.value == 9) {
                // src_as_dst_as case
                columns.push(
                    {
                        title_i18n: "as_overview.src_as", sortable: true, name: 'src_as', data_field: 'src_as', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field) => {
                            return format_as_name(data_field);
                        }
                    },
                    {
                        title_i18n: "as_overview.dst_as", sortable: true, name: 'dst_as', data_field: 'dst_as', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field) => {
                            return format_as_name(data_field);
                        }
                    });
            } else if (selected_criteria.value.value == 10) {
                // src_as_transit_as_dst_as case
                columns.push(
                    {
                        title_i18n: "as_overview.src_as", sortable: true, name: 'src_as', data_field: 'src_as', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field) => {
                            return format_as_name(data_field);
                        }
                    },
                    {
                        title_i18n: "transit_as", sortable: true, name: 'transit_as', data_field: 'transit_as', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field) => {
                            return format_as_name(data_field);
                        }
                    },
                    {
                        title_i18n: "as_overview.dst_as", sortable: true, name: 'dst_as', data_field: 'dst_as', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field) => {
                            return format_as_name(data_field);
                        }
                    });
            } else if (selected_criteria.value.value == 11) {
                // src_as_transit_as_dst_as case
                columns.push(
                    {
                        title_i18n: "db_explorer.host_data", sortable: true, name: 'host', data_field: 'host', class: ['text-nowrap'], responsivePriority: 1, render_func: (data_field, rowData) => {
                            return format_host_name(data_field)
                        }
                    });
            }
        }

        if (props.context.vlans.length > 2) {
            columns.push({
                title_i18n: "vlan", sortable: true, name: 'vlan_id', data_field: 'vlan_id', class: ['text-nowrap ', 'text-center'], responsivePriority: 1, render_func: (data_field) => {
                    if (data_field.id === 0 || data_field.id == undefined) {
                        const label = i18n('no_vlan');
                        return `<a href="${http_prefix}/lua/flows_stats.lua?vlan=0">${label}</a>`
                    }
                    else {
                        return `<a href="${http_prefix}/lua/flows_stats.lua?vlan=${data_field.id}">${data_field.label}</a>`
                    }
                }
            });
        }
        columns.push({
            title_i18n: "flows", sortable: true, name: 'flows', data_field: 'flows', class: ['text-nowrap ', 'text-center'], responsivePriority: 1
        }, {
            title_i18n: "total_score", sortable: true, name: 'score', data_field: 'tot_score', class: ['text-center'], responsivePriority: 1
        });

        if (selected_criteria.value.value != 2 && selected_criteria.value.value != 4 && selected_criteria.value.value != 7 && selected_criteria.value.value != 9 && selected_criteria.value.value != 10 && selected_criteria.value.value != 11)
            columns.push({ title_i18n: "clients", sortable: true, name: 'num_clients', data_field: 'num_clients', class: ['text-nowrap ', 'text-center'], responsivePriority: 1 });

        if (selected_criteria.value.value != 3 && selected_criteria.value.value != 4 && selected_criteria.value.value != 7 && selected_criteria.value.value != 9 && selected_criteria.value.value != 10 && selected_criteria.value.value != 11)
            columns.push({ title_i18n: "servers", sortable: true, name: 'num_servers', data_field: 'num_servers', class: ['text-nowrap ', 'text-center'], responsivePriority: 1 });

        columns.push({
            title_i18n: "breakdown", sortable: false, name: 'breakdown', data_field: 'breakdown', class: ['text-nowrap', 'text-center'], responsivePriority: 1, render_func: (data_field) => {
                return NtopUtils$1.createBreakdown(data_field.percentage_bytes_sent, data_field.percentage_bytes_rcvd, i18n('sent'), i18n('rcvd'));
            }
        }, {
            title_i18n: "traffic_sent", sortable: true, name: 'bytes_sent', data_field: 'bytes_sent', class: ['text-nowrap', 'text-end'], responsivePriority: 1, render_func: (data_field) => {
                return NtopUtils$1.bytesToSize(data_field);
            }
        }, {
            title_i18n: "traffic_rcvd", sortable: true, name: 'bytes_rcvd', data_field: 'bytes_rcvd', class: ['text-nowrap', 'text-end'], responsivePriority: 1, render_func: (data_field) => {
                return NtopUtils$1.bytesToSize(data_field);
            }
        }, {
            title_i18n: "total_traffic", sortable: true, name: 'tot_traffic', data_field: 'tot_traffic', class: ['text-nowrap', 'text-end'], responsivePriority: 1, render_func: (data_field) => {
                return NtopUtils$1.bytesToSize(data_field);
            }
        });
        return columns;
    };

    /* FUNCTIONS TO FORMAT */
    const format_client_name = function (data, rowData) {
        let alert_label = '';
        if (data.is_alerted) {
            alert_label = `<i class='fas fa-exclamation-triangle' style='color: #B94A48;'></i>`;
        }

        if (!data.in_memory) {
            return `${data.label} ${alert_label} ${data.extra_labels}`;
        } else {
            return `<a href="${http_prefix}/lua/flows_stats.lua?client=${data.ip}&vlan=${data.vlan_id}">${data.label}</a> ${alert_label} ${data.extra_labels} <a href="${http_prefix}/lua/host_details.lua?host=${data.ip}&vlan=${data.vlan_id}" data-bs-toggle='tooltip' title=''><i class='fas fa-laptop'></i></a>`;
        }
    };
    const format_host_name = function (data, rowData) {
        if (!data.in_memory) {
            return `${data.extra_labels}`;
        } else {
            let title = '';
            if (data.ip !== data.label) {
                title = ` data-bs-toggle='tooltip' title='${data.ip}' `;
            }
            return `<a href="${http_prefix}/lua/flows_stats.lua?flowhosts_type=${data.ip}&vlan=${data.vlan_id}" ${title}>${data.label}</a> ${data.extra_labels} <a href="${http_prefix}/lua/host_details.lua?host=${data.ip}&vlan=${data.vlan_id}" data-bs-toggle='tooltip' title=''><i class='fas fa-laptop'></i></a>`;
        }
    };

    const format_server_name = function (data, rowData) {
        let alert_label = '';
        if (data.is_alerted) {
            alert_label = `<i class='fas fa-exclamation-triangle' style='color: #B94A48;'></i>`;
        }

        if (!data.in_memory) {
            if ((selected_criteria.value.value == 7 || selected_criteria.value.value == 8) && rowData.srv_port != null) {
                return `${data.label} ${alert_label} ${data.extra_labels}:${rowData.srv_port.label}`;
            } else {
                return `${data.label} ${alert_label} ${data.extra_labels}`;
            }

        } else {
            if ((selected_criteria.value.value == 7 || selected_criteria.value.value == 8) && rowData.srv_port != null) {
                return `<a href="${http_prefix}/lua/flows_stats.lua?server=${data.ip}&vlan=${data.vlan_id || ''}">${data.label}</a> ${alert_label} ${data.extra_labels} <a href="${http_prefix}/lua/host_details.lua?host=${data.ip}&vlan=${data.vlan_id || ''}" data-bs-toggle='tooltip' title=''><i class='fas fa-laptop'></i></a>:<a href="${http_prefix}/lua/flows_stats.lua?port=${rowData.srv_port.id}&vlan=${data.vlan_id || ''}">${rowData.srv_port.label}</a>`;
            } else {
                return `<a href="${http_prefix}/lua/flows_stats.lua?server=${data.ip}&vlan=${data.vlan_id || ''}">${data.label}</a> ${alert_label} ${data.extra_labels} <a href="${http_prefix}/lua/host_details.lua?host=${data.ip}&vlan=${data.vlan_id || ''}" data-bs-toggle='tooltip' title=''><i class='fas fa-laptop'></i></a>`;

            }
        }
    };

    const format_flows_icon = function (data, rowData) {
        let url = ``;
        let add_host = false;
        const exporter = ntopng_url_manager$1.get_url_entry("deviceIP");
        const in_interface = ntopng_url_manager$1.get_url_entry("inIfIdx");
        const out_interface = ntopng_url_manager$1.get_url_entry("outIfIdx");

        if (props.context.host != null && props.context.host != "")
            add_host = true;
        if (selected_criteria.value.value == 1) {
            url = `${http_prefix}/lua/flows_stats.lua?application=${rowData.application.id}`;
            if (add_host) url = url + `&host=` + props.context.host;
        }
        else if (selected_criteria.value.value == 2)
            url = `${http_prefix}/lua/flows_stats.lua?client=${rowData.client.ip}&vlan=${rowData.client.vlan_id || ''}`;
        else if (selected_criteria.value.value == 3)
            url = `${http_prefix}/lua/flows_stats.lua?server=${rowData.server.ip}&vlan=${rowData.server.vlan_id || ''}`;
        else if (selected_criteria.value.value == 4)
            url = `${http_prefix}/lua/flows_stats.lua?client=${rowData.client.ip}&server=${rowData.server.ip}&vlan=${rowData.vlan_id.id || ''}`;
        else if (selected_criteria.value.value == 5)
            url = `${http_prefix}/lua/flows_stats.lua?application=${rowData.application.id}&client=${rowData.client.ip}&server=${rowData.server.ip}&vlan=${rowData.vlan_id.id || ''}`;
        else if (selected_criteria.value.value == 6) {
            url = `${http_prefix}/lua/flows_stats.lua?flow_info=${NtopUtils$1.stripTags(rowData.info.id)}`;
            if (add_host) url = url + `&host=` + props.context.host;
        }
        else if (selected_criteria.value.value == 7) {
            url = `${http_prefix}/lua/flows_stats.lua?client=${rowData.client.ip}&server=${rowData.server.ip}&vlan=${rowData.vlan_id.id || ''}&srv_port=${rowData.srv_port.id}`;
        }
        else if (selected_criteria.value.value == 8) {
            url = `${http_prefix}/lua/flows_stats.lua?application=${rowData.application.id}&client=${rowData.client.ip}&server=${rowData.server.ip}&vlan=${rowData.vlan_id.id || ''}&srv_port=${rowData.srv_port.id}`;
        }
        else if (selected_criteria.value.value == 9) {
            url = `${http_prefix}/lua/flows_stats.lua?asn=${rowData.src_as?.asn || 0}`;
            if (add_host) url = url + `&host=` + props.context.host;
        }
        else if (selected_criteria.value.value == 10) {
            url = `${http_prefix}/lua/flows_stats.lua?asn=${rowData.src_as?.asn || 0}`;
            if (add_host) url = url + `&host=` + props.context.host;
        }
        else if (selected_criteria.value.value == 11) {
            url = `${http_prefix}/lua/flows_stats.lua?flowhosts_type=${rowData.host?.ip}&vlan=${rowData.host?.vlan_id}`;
        }

        if (exporter && exporter !== "") {
            url = `${url}&deviceIP=${exporter}`;
        }
        if (in_interface && in_interface !== "") {
            url = `${url}&inIfIdx=${in_interface}`;
        }
        if (out_interface && out_interface !== "") {
            url = `${url}&outIfIdx=${out_interface}`;
        }

        return `<a href=${url} class="btn btn-sm btn-info" ><i class= 'fas fa-stream'></i></a>`
    };

    const format_application_proto_guessed = function (data, rowData) {
        if (rowData.confidence == 0)
            return `${data.label_with_icons} <span class=\"badge bg-warning\" title=\" ` + rowData.confidence_name + `\">` + rowData.confidence_name + ` </span>`
        else if (rowData.confidence)
            return `${data.label_with_icons} <span class=\"badge bg-success\" title=\"` + rowData.confidence_name + ` \"> ` + rowData.confidence_name + `</span>`
    };

    const format_as_name = function (data) {

        // Link to AS details page if it exists, otherwise just show the AS number
        return `<a href="${http_prefix}/lua/hosts_stats.lua?asn=${data.asn}">${data.label}</a>`;
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1B, [
        createBaseVNode("div", _hoisted_2$1p, [
          createBaseVNode("div", _hoisted_3$1f, [
            createBaseVNode("div", _hoisted_4$1c, [
              createBaseVNode("label", _hoisted_5$Z, toDisplayString(_i18n('criteria_filter')) + ": ", 1 /* TEXT */),
              createVNode(script$2H, {
                selected_option: selected_criteria.value,
                "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_criteria).value = $event)),
                options: unref(criteria_list),
                onSelect_option: update_criteria
              }, null, 8 /* PROPS */, ["selected_option", "options"])
            ])
          ]),
          createBaseVNode("div", null, [
            createVNode(script$2w, {
              ref_key: "table_aggregated_live_flows",
              ref: table_aggregated_live_flows,
              csrf: csrf.value,
              table_id: table_id.value,
              table_config_id: table_config_id.value,
              f_map_columns: map_table_def_columns,
              get_extra_params_obj: get_extra_params_obj,
              f_map_config: map_config,
              showLoading: true
            }, {
              custom_header: withCtx(() => [
                (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
                  return (openBlock(), createElementBlock("div", _hoisted_6$M, [
                    createBaseVNode("span", _hoisted_7$H, [
                      createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                    ]),
                    createVNode(script$2H, {
                      selected_option: item['current_option'],
                      "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                      theme: "bootstrap-5",
                      dropdown_size: "small",
                      disabled: loading.value,
                      options: item['options'],
                      onSelect_option: add_table_filter
                    }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "disabled", "options"])
                  ]))
                }), 256 /* UNKEYED_FRAGMENT */)),
                createBaseVNode("div", _hoisted_8$E, [
                  createBaseVNode("div", {
                    class: "btn btn-sm btn-primary mt-2 me-3",
                    type: "button",
                    onClick: reset_filters
                  }, toDisplayString(_i18n('reset')), 1 /* TEXT */),
                  createVNode(script$2v, {
                    show: loading.value,
                    size: "1rem",
                    class: "me-1"
                  }, null, 8 /* PROPS */, ["show"])
                ])
              ]),
              _: 1 /* STABLE */
            }, 8 /* PROPS */, ["csrf", "table_id", "table_config_id"])
          ])
        ])
      ]))
    }
    }

    };

    script$1L.__file = "http_src/vue/page-aggregated-live-flows.vue";

    const _hoisted_1$1A = { id: "aggregated_live_flows" };


    var script$1K = {
      __name: 'page-test-table',
      props: {
        url: String,
        ifid: Number,
        columns_config: Array
    },
      setup(__props) {

    const props = __props;

    const table_config = ref({});
    const table_test = ref(null);

    onBeforeMount(() => {
        set_datatable_config();
    });

    function set_datatable_config() {
        const datatableButton = [];
        
        let params = { 
    	ifid: ntopng_url_manager.get_url_entry("ifid") || props.ifid,	
        };
        let url_params = ntopng_url_manager.obj_to_url_params(params);
        
        datatableButton.push({
    	text: '<i class="fas fa-sync"></i>',
    	className: 'btn-link',
    	action: function (e, dt, node, config) {
                table_test.value.reload();
    	}
        });
        
        let defaultDatatableConfig = {
    	table_buttons: datatableButton,
    	data_url: `${props.url}?${url_params}`,
    	enable_search: true,
        };
        
        defaultDatatableConfig.columns_config = props.columns_config;
        table_config.value = defaultDatatableConfig;
    }
        

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1A, [
        createVNode(script$2e, {
          ref_key: "table_test",
          ref: table_test,
          table_buttons: table_config.value.table_buttons,
          columns_config: table_config.value.columns_config,
          data_url: table_config.value.data_url,
          filter_buttons: table_config.value.table_filters,
          enable_search: table_config.value.enable_search,
          table_config: table_config.value.table_config
        }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "filter_buttons", "enable_search", "table_config"])
      ]))
    }
    }

    };

    script$1K.__file = "http_src/vue/page-test-table.vue";

    const _hoisted_1$1z = { class: "container-fluid" };
    const _hoisted_2$1o = { class: "row form-group mb-3" };
    const _hoisted_3$1e = { class: "col col-md-12" };
    const _hoisted_4$1b = { class: "form-label" };
    const _hoisted_5$Y = { class: "row form-group mb-3" };
    const _hoisted_6$L = { class: "col col-md-6" };
    const _hoisted_7$G = { class: "form-label" };
    const _hoisted_8$D = { class: "col col-md-6" };
    const _hoisted_9$y = { class: "form-label" };
    const _hoisted_10$v = { key: 0 };
    const _hoisted_11$s = { key: 1 };
    const _hoisted_12$o = ["pattern"];
    const _hoisted_13$l = { class: "row form-group mb-3" };
    const _hoisted_14$j = { class: "col col-md-6" };
    const _hoisted_15$i = { class: "form-label" };
    const _hoisted_16$i = { class: "col col-md-6" };
    const _hoisted_17$g = { class: "form-label" };
    const _hoisted_18$d = { key: 0 };
    const _hoisted_19$c = { key: 1 };
    const _hoisted_20$c = ["pattern"];
    const _hoisted_21$c = { class: "row form-group mb-3" };
    const _hoisted_22$c = { class: "col col-md-12" };
    const _hoisted_23$c = { class: "form-label" };
    const _hoisted_24$b = { class: "row form-group mb-3" };
    const _hoisted_25$a = { class: "col col-md-12" };
    const _hoisted_26$9 = { class: "form-label" };
    const _hoisted_27$9 = ["disabled"];


    var script$1J = {
      __name: 'modal-nedge-add-rule-config',
      props: {
    },
      emits: ['edit', 'add'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);

    const modal_id = ref(null);
    const emit = __emit;

    const showed = () => {};

    const title = ref("");

    const type_array = [
        { label: _i18n("nedge.page_rules_config.modal_rule_config.ip"), value: "ip", default: true },
        { label: _i18n("nedge.page_rules_config.modal_rule_config.cidr"), value: "cidr" },
        { label: _i18n("interface"), value: "interface" },
    ];

    let default_direction_value = "source_to_dest";
    const directions = [
        { label: _i18n("nedge.page_rules_config.bidirectional"), value: "bidirectional", bidirectional: true, },
        { label: _i18n("nedge.page_rules_config.source_to_dest"), value: "source_to_dest", bidirectional: false, },
    ];
    const selected_direction = ref({});

    let default_action_value = "accept";
    const actions = [
        { label: _i18n("nedge.page_rules_config.accept"), value: "accept" },
        { label: _i18n("nedge.page_rules_config.deny"), value: "deny" },
    ];
    const selected_action = ref({});

    const rule_id = ref(0);
    const new_rule_id = ref(0);

    const selected_source_type = ref({});
    const source_regex = ref("");
    const source = ref("");

    const selected_dest_type = ref({});
    const dest_regex = ref("");
    const dest = ref("");

    const interface_list_url = `${http_prefix}/lua/pro/rest/v2/get/nedge/interfaces.lua`;
    let interface_list;
    const interface_array = ref([]);

    const selected_source_interface = ref({});
    const selected_dest_interface = ref({});

    const button_text = ref("");

    const is_valid_source = computed$1(() => {
        return is_valid(selected_source_type.value, source.value, source_regex.value);
    });

    const is_valid_dest = computed$1(() => {
        return is_valid(selected_dest_type.value, dest.value, dest_regex.value);
    });

    const show = (row, default_action) => {
        init(row, default_action);
        modal_id.value.show();
    };

    const is_open_in_add = ref(true);

    function init(row, default_action) {
        is_open_in_add.value = row == null;
        if (default_action != null) {
    	default_action_value = default_action.value;
        }
        // check if we need open in edit
        if (is_open_in_add.value == false) {
    	title.value = _i18n("nedge.page_rules_config.modal_rule_config.title_edit");
    	button_text.value = _i18n("edit");
    	selected_source_type.value = type_array.find((s) => s.value == row.source.type);
    	selected_dest_type.value = type_array.find((s) => s.value == row.destination.type);
    	selected_direction.value = directions.find((d) => d.bidirectional == row.bidirectional);
    	selected_action.value = actions.find((a) => a.value == row.action);
    	rule_id.value = row.rule_id;
            new_rule_id.value = row.rule_id;
        } else {
    	title.value = _i18n("nedge.page_rules_config.modal_rule_config.title_add");
    	button_text.value = _i18n("add");
    	let default_type = type_array.find((s) => s.default == true);
    	selected_source_type.value = default_type;
    	selected_dest_type.value = default_type;
    	selected_direction.value = directions.find((d) => d.value == default_direction_value);
    	selected_action.value = actions.find((a) => a.value != default_action_value);
        }
        change_source_type(row);
        change_dest_type(row);
    }

    async function change_source_type(row) {
        let value = null;
        if (row != null) {
    	value = row.source.value;
        }
        if (selected_source_type.value.value == "interface") {
    	await set_interface_array();
    	if (value != null) {
    	    selected_source_interface.value = interface_array.value.find((i) => i.value == value);
    	} else {
    	    selected_source_interface.value = interface_array.value[0];
    	}
        } else {
    	if (row != null) {
    	    source.value = row.source.value;
    	} else {
    	    source.value = "";
    	}
    	set_regex(source_regex, selected_source_type.value.value);
        }    
    }

    async function change_dest_type(row) {
        let value = null;
        if (row != null) {
    	value = row.destination.value;
        }
        if (selected_dest_type.value.value == "interface") {
    	await set_interface_array();
    	if (value != null) {
    	    selected_dest_interface.value = interface_array.value.find((i) => i.value == value);
    	} else {
    	    selected_dest_interface.value = interface_array.value[0];
    	}
        } else {
    	if (row != null) {
    	    dest.value = row.destination.value;
    	} else {
    	    dest.value = "";
    	}
    	set_regex(dest_regex, selected_dest_type.value.value);
        }
    }

    function is_valid(selected_type, text, rg_text) {
        if (selected_type.value == "interface") {
    	return true;
        }
        let regex = new RegExp(rg_text);
        return regex.test(text);
    }

    let is_set_interface_array = false;
    async function set_interface_array() {
        if (is_set_interface_array == true) { return; }
    	if (interface_list == null) {
    	    interface_list = ntopng_utility$1.http_request(interface_list_url);
    	}
    	let res_interface_list = await interface_list;
    	interface_array.value = res_interface_list.filter((i) => i.role == "lan").map((i) => {
    	    return {
    		label: i.label,
    		value: i.ifname,
    	    };
    	});
        is_set_interface_array = true;
    }

    function set_regex(rg, type) {
        rg.value = regexValidation.get_data_pattern(type);
    }

    const apply = () => {
        let src_type = selected_source_type.value.value;    
        let src_value = source.value;
        if (src_type == "interface") {
    	src_value = selected_source_interface.value.value;
        }
        let dst_type = selected_dest_type.value.value;    
        let dst_value = dest.value;
        if (dst_type == "interface") {
    	dst_value = selected_dest_interface.value.value;
        }
        let policy = selected_action.value.value;
        let bidirectional = selected_direction.value.value == "bidirectional";
        let obj = {
    	src_type,
    	src_value,
    	dst_type,
    	dst_value,
    	policy,
    	bidirectional,
        };
        let event = "add";
        if (is_open_in_add.value == false) {
    	obj.rule_id = rule_id.value;
    	obj.new_rule_id = new_rule_id.value;
    	event = "edit";
        }
        
        emit(event, obj);
        close();
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[12] || (_cache[12] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$1z, [
            createCommentVNode(" Rule ID "),
            withDirectives(createBaseVNode("div", _hoisted_2$1o, [
              createBaseVNode("div", _hoisted_3$1e, [
                createBaseVNode("label", _hoisted_4$1b, toDisplayString(_i18n("nedge.page_rules_config.rule_id")), 1 /* TEXT */),
                withDirectives(createBaseVNode("input", {
                  type: "text",
                  class: "form-control",
                  "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((new_rule_id).value = $event))
                }, null, 512 /* NEED_PATCH */), [
                  [vModelText, new_rule_id.value]
                ]),
                withDirectives(createBaseVNode("input", {
                  type: "hidden",
                  class: "form-control",
                  "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((rule_id).value = $event))
                }, null, 512 /* NEED_PATCH */), [
                  [vModelText, rule_id.value]
                ])
              ])
            ], 512 /* NEED_PATCH */), [
              [vShow, !is_open_in_add.value]
            ]),
            createCommentVNode(" Source "),
            createBaseVNode("div", _hoisted_5$Y, [
              createBaseVNode("div", _hoisted_6$L, [
                createBaseVNode("label", _hoisted_7$G, toDisplayString(_i18n("nedge.page_rules_config.modal_rule_config.source_type")), 1 /* TEXT */),
                createVNode(script$2H, {
                  selected_option: selected_source_type.value,
                  "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((selected_source_type).value = $event)),
                  onSelect_option: _cache[3] || (_cache[3] = $event => (change_source_type())),
                  options: type_array
                }, null, 8 /* PROPS */, ["selected_option"])
              ]),
              createBaseVNode("div", _hoisted_8$D, [
                createBaseVNode("label", _hoisted_9$y, toDisplayString(_i18n("nedge.page_rules_config.source")), 1 /* TEXT */),
                (selected_source_type.value.value == 'interface')
                  ? (openBlock(), createElementBlock("div", _hoisted_10$v, [
                      createVNode(script$2H, {
                        selected_option: selected_source_interface.value,
                        "onUpdate:selected_option": _cache[4] || (_cache[4] = $event => ((selected_source_interface).value = $event)),
                        options: interface_array.value
                      }, null, 8 /* PROPS */, ["selected_option", "options"])
                    ]))
                  : (openBlock(), createElementBlock("div", _hoisted_11$s, [
                      withDirectives(createBaseVNode("input", {
                        type: "text",
                        class: "form-control",
                        pattern: source_regex.value,
                        "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((source).value = $event))
                      }, null, 8 /* PROPS */, _hoisted_12$o), [
                        [vModelText, source.value]
                      ])
                    ]))
              ])
            ]),
            createCommentVNode(" Dest "),
            createBaseVNode("div", _hoisted_13$l, [
              createBaseVNode("div", _hoisted_14$j, [
                createBaseVNode("label", _hoisted_15$i, toDisplayString(_i18n("nedge.page_rules_config.modal_rule_config.dest_type")), 1 /* TEXT */),
                createVNode(script$2H, {
                  selected_option: selected_dest_type.value,
                  "onUpdate:selected_option": _cache[6] || (_cache[6] = $event => ((selected_dest_type).value = $event)),
                  onSelect_option: _cache[7] || (_cache[7] = $event => (change_dest_type())),
                  options: type_array
                }, null, 8 /* PROPS */, ["selected_option"])
              ]),
              createBaseVNode("div", _hoisted_16$i, [
                createBaseVNode("label", _hoisted_17$g, toDisplayString(_i18n("nedge.page_rules_config.dest")), 1 /* TEXT */),
                (selected_dest_type.value.value == 'interface')
                  ? (openBlock(), createElementBlock("div", _hoisted_18$d, [
                      createVNode(script$2H, {
                        selected_option: selected_dest_interface.value,
                        "onUpdate:selected_option": _cache[8] || (_cache[8] = $event => ((selected_dest_interface).value = $event)),
                        options: interface_array.value
                      }, null, 8 /* PROPS */, ["selected_option", "options"])
                    ]))
                  : (openBlock(), createElementBlock("div", _hoisted_19$c, [
                      withDirectives(createBaseVNode("input", {
                        type: "text",
                        class: "form-control",
                        pattern: dest_regex.value,
                        "onUpdate:modelValue": _cache[9] || (_cache[9] = $event => ((dest).value = $event))
                      }, null, 8 /* PROPS */, _hoisted_20$c), [
                        [vModelText, dest.value]
                      ])
                    ]))
              ])
            ]),
            createCommentVNode(" Direction "),
            createBaseVNode("div", _hoisted_21$c, [
              createBaseVNode("div", _hoisted_22$c, [
                createBaseVNode("label", _hoisted_23$c, toDisplayString(_i18n("nedge.page_rules_config.direction")), 1 /* TEXT */),
                createVNode(script$2H, {
                  selected_option: selected_direction.value,
                  "onUpdate:selected_option": _cache[10] || (_cache[10] = $event => ((selected_direction).value = $event)),
                  options: directions
                }, null, 8 /* PROPS */, ["selected_option"])
              ])
            ]),
            createCommentVNode(" Action "),
            createBaseVNode("div", _hoisted_24$b, [
              createBaseVNode("div", _hoisted_25$a, [
                createBaseVNode("label", _hoisted_26$9, toDisplayString(_i18n("nedge.page_rules_config.action")), 1 /* TEXT */),
                createVNode(script$2H, {
                  selected_option: selected_action.value,
                  "onUpdate:selected_option": _cache[11] || (_cache[11] = $event => ((selected_action).value = $event)),
                  options: actions
                }, null, 8 /* PROPS */, ["selected_option"])
              ])
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            disabled: !is_valid_source.value || !is_valid_dest.value,
            onClick: apply,
            class: "btn btn-primary"
          }, toDisplayString(button_text.value), 9 /* TEXT, PROPS */, _hoisted_27$9)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$m = "\ninput[data-v-50692d68]:invalid {\n  border-color: #ff0000;\n}\n";
    styleInject(css_248z$m);

    script$1J.__scopeId = "data-v-50692d68";
    script$1J.__file = "http_src/vue/modal-nedge-add-rule-config.vue";

    const _hoisted_1$1y = { class: "container-fluid" };
    const _hoisted_2$1n = { class: "row form-group mb-3" };
    const _hoisted_3$1d = { class: "col col-md-12" };
    const _hoisted_4$1a = { class: "form-label" };


    var script$1I = {
      __name: 'modal-nedge-change-default-policy',
      emits: ['apply'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const emit = __emit;

    const modal_id = ref(null);

    const _i18n = (t) => i18n(t);

    const title = _i18n("nedge.page_rules_config.modal_change_default_policy.title");
    const actions = [
        { label: _i18n("nedge.page_rules_config.accept"), value: "accept" },
        { label: _i18n("nedge.page_rules_config.deny"), value: "deny" },
    ];
    const selected_action = ref({});

    const showed = () => {
    };

    const show = (policy) => {
        selected_action.value = actions.find((a) => a.value == policy.value);
        modal_id.value.show();
    };

    const close = () => {
        modal_id.value.close();
    };

    function apply() {
        emit('apply', selected_action.value.value);
        close();
    }

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[1] || (_cache[1] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$1y, [
            createCommentVNode(" Default Policy "),
            createBaseVNode("div", _hoisted_2$1n, [
              createBaseVNode("div", _hoisted_3$1d, [
                createBaseVNode("label", _hoisted_4$1a, toDisplayString(_i18n("nedge.page_rules_config.default policy")), 1 /* TEXT */),
                createVNode(script$2H, {
                  selected_option: selected_action.value,
                  "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_action).value = $event)),
                  options: actions
                }, null, 8 /* PROPS */, ["selected_option"])
              ])
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: apply,
            class: "btn btn-primary"
          }, toDisplayString(_i18n('apply')), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1I.__file = "http_src/vue/modal-nedge-change-default-policy.vue";

    const _hoisted_1$1x = { class: "mb-2" };
    const _hoisted_2$1m = { class: "d-inline-block" };
    const _hoisted_3$1c = {
      key: 0,
      style: {"color":"green"}
    };
    const _hoisted_4$19 = {
      key: 1,
      style: {"color":"red"}
    };
    const _hoisted_5$X = { id: "aggregated_live_flows" };

    const timeout_delete$1 = 1 * 500;


    var script$1H = {
      __name: 'page-nedge-rules-config',
      props: {
        url: String,
        ifid: Number,
        csrf: String,
        columns_config: Array
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const table_config = ref({});
    const table_rules = ref(null);
    const modal_add_rule_config = ref(null);
    const modal_change_default_policy = ref(null);
    const default_policy = ref({});

    onBeforeMount(async () => {
        set_datatable_config();
        load_default_policy();
    });

    function edit_rule(rule) {    
        const edit_url = `${http_prefix}/lua/pro/rest/v2/edit/nedge/policy/rule.lua`;
        set_rule(rule, edit_url);
    }

    function add_rule(rule) {
        const add_url = `${http_prefix}/lua/pro/rest/v2/add/nedge/policy/rule.lua`;
        set_rule(rule, add_url);
    }

    async function load_default_policy(policy) {
        if (policy == null) {
            const get_policy_url = `${http_prefix}/lua/pro/rest/v2/get/nedge/policy/default.lua`;
            let policy_res = await ntopng_utility$1.http_request(get_policy_url);
            policy = policy_res.default_policy;
        }
        if (policy == "accept") {
            default_policy.value = {
                value: policy,
                label: _i18n("nedge.page_rules_config.accept"),
            };
        } else {
            default_policy.value = {
                value: policy,
                label: _i18n("nedge.page_rules_config.deny"),
            };    
        }
    }

    async function set_default_policy(policy) {
        const set_policy_url = `${http_prefix}/lua/pro/rest/v2/set/nedge/policy/default.lua`;
        let headers = {
            'Content-Type': 'application/json'
        };
        let body = JSON.stringify({ default_policy: policy, csrf: props.csrf});
        await ntopng_utility$1.http_request(set_policy_url, { method: "post", headers, body});
        load_default_policy(policy);
        refresh_table();    
    }

    function set_rule(rule, url) {
        let headers = {
            'Content-Type': 'application/json'
        };
        let body = JSON.stringify({ ...rule, csrf: props.csrf});
        
        ntopng_utility$1.http_request(url, { method: "post", headers, body});
        refresh_table();    
    }

    function show_modal_change_policy() {    
        modal_change_default_policy.value.show(default_policy.value);
    }

    function set_datatable_config() {
        const datatableButton = [];
        
        let params = { 
    	    ifid: ntopng_url_manager$1.get_url_entry("ifid") || props.ifid,	
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        
        datatableButton.push({
            text: '<i class="fas fa-sync"></i>',
            className: 'btn-link',
            action: function (e, dt, node, config) {
                refresh_table();
                    // table_rules.value.reload();
            }
        }, {
            text: '<i class="fas fa-plus"></i>',
            className: 'btn-link',
            action: function () {
                modal_add_rule_config.value.show(null, default_policy.value);
            }
        });
        
        let defaultDatatableConfig = {
    	table_buttons: datatableButton,
    	data_url: `${props.url}?${url_params}`,
    	enable_search: false,
        };
        
        let columns = [
    	{ 
    	    columnName: _i18n("nedge.page_rules_config.rule_id"), targets: 0, name: 'rule_id', data: 'rule_id', className: 'text-nowrap text-center', responsivePriority: 1
    	}, { 
    	    columnName: _i18n("nedge.page_rules_config.source"), targets: 0, name: 'source', data: 'source', className: 'text-nowrap text-center', responsivePriority: 1, render: function(value, type, rowData) {
    		if (value.label != null && value.label != "") {
    		    return value.label;
    		} 
    		return value.value;
    	    }
    	}, { 
    	    columnName: _i18n("nedge.page_rules_config.dest"), targets: 0, name: 'dest', data: 'destination', className: 'text-nowrap text-center', responsivePriority: 1, render: function(value, type, rowData) {
    		if (value.label != null && value.label != "") {
    		    return value.label;
    		} 
    		return value.value;
    	    }
    	}, { 
    	    columnName: _i18n("nedge.page_rules_config.direction"), targets: 0, name: 'bidirectional', data: 'bidirectional', className: 'text-nowrap text-center', responsivePriority: 1, render: function(value, type, rowData) {
    		if (value == true) {
    		    return _i18n("nedge.page_rules_config.bidirectional");
    		} 
    		return _i18n("nedge.page_rules_config.source_to_dest");
    	    }
    	}, { 
    	    columnName: _i18n("nedge.page_rules_config.action"), targets: 0, name: 'action', data: 'action', className: 'text-nowrap text-center', responsivePriority: 1, render: function(value, type, rowData) {
    		let color = "red";
    		let name = _i18n(`nedge.page_rules_config.deny`);
    		if (value == "accept") {
    		    color = "green";
    		    name =  _i18n(`nedge.page_rules_config.accept`);
    		}
    		return `<span style="color:${color};">${name}</span>`;
    	    }
    	}
        ];
        let wrap_columns_config = columns.map((c) => c);
        // let wrap_columns_config = props.columns_config.map((c) => c);
        wrap_columns_config.push({ columnName: _i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, render: function (_, type, rowData) { return add_action_column(rowData) } });
        
        defaultDatatableConfig.columns_config = wrap_columns_config;
        table_config.value = defaultDatatableConfig;
    }

    const add_action_column = function (rowData) {
        let delete_handler = {
            handlerId: "delete_host",	  
            onClick: () => {
                delete_rule(rowData);
            },
        };
        
        let edit_handler = {
            handlerId: "edit_rule",
            onClick: () => {
                modal_add_rule_config.value.show(rowData);
            },
        };
        
        return DataTableUtils.createActionButtons([
            { class: `btn-secondary`, handler: edit_handler, handlerId: "edit_rule", icon: 'fa-edit', title: i18n('edit') },
            { class: `btn-danger`, handler: delete_handler, handlerId: "delete_rule", icon: 'fa-trash', title: i18n('delete') },
        ]);
    };

    async function delete_rule(rule) {
        const add_url = `${http_prefix}/lua/pro/rest/v2/delete/nedge/policy/rule.lua`;
        let headers = {
            'Content-Type': 'application/json'
        };
        let body = JSON.stringify({ rule_id: rule.rule_id, csrf: props.csrf});
        
        ntopng_utility$1.http_request(add_url, { method: "post", headers, body});
        refresh_table();    
    }

    function refresh_table() {
        setTimeout(() => {
    	ntopng_url_manager$1.reload_url();
     	// table_rules.value.reload();
        }, timeout_delete$1);
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$1x, [
          createBaseVNode("h2", null, toDisplayString(_i18n("nedge.rules_config_title")), 1 /* TEXT */),
          _cache[1] || (_cache[1] = createBaseVNode("br", null, null, -1 /* CACHED */)),
          createBaseVNode("h5", _hoisted_2$1m, [
            createTextVNode(toDisplayString(_i18n("nedge.page_rules_config.default policy")) + " ", 1 /* TEXT */),
            (default_policy.value?.value == 'accept')
              ? (openBlock(), createElementBlock("span", _hoisted_3$1c, toDisplayString(default_policy.value?.label), 1 /* TEXT */))
              : createCommentVNode("v-if", true),
            (default_policy.value?.value == 'deny')
              ? (openBlock(), createElementBlock("span", _hoisted_4$19, toDisplayString(default_policy.value?.label), 1 /* TEXT */))
              : createCommentVNode("v-if", true),
            createBaseVNode("small", null, [
              createBaseVNode("a", {
                href: "javascript:void(0)",
                style: {"margin-left":"0.5rem"},
                onClick: show_modal_change_policy
              }, [...(_cache[0] || (_cache[0] = [
                createBaseVNode("i", { class: "fas fa-cog" }, null, -1 /* CACHED */)
              ]))])
            ])
          ])
        ]),
        createBaseVNode("div", _hoisted_5$X, [
          createVNode(script$2e, {
            ref_key: "table_rules",
            ref: table_rules,
            table_buttons: table_config.value.table_buttons,
            columns_config: table_config.value.columns_config,
            data_url: table_config.value.data_url,
            filter_buttons: table_config.value.table_filters,
            enable_search: table_config.value.enable_search,
            table_config: table_config.value.table_config
          }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "filter_buttons", "enable_search", "table_config"])
        ]),
        createVNode(script$1J, {
          ref_key: "modal_add_rule_config",
          ref: modal_add_rule_config,
          onAdd: add_rule,
          onEdit: edit_rule
        }, null, 512 /* NEED_PATCH */),
        createVNode(script$1I, {
          ref_key: "modal_change_default_policy",
          ref: modal_change_default_policy,
          onApply: set_default_policy
        }, null, 512 /* NEED_PATCH */)
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1H.__file = "http_src/vue/page-nedge-rules-config.vue";

    const _hoisted_1$1w = {
      key: 0,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_2$1l = { class: "col-form-label col-sm-4" };
    const _hoisted_3$1b = { class: "col-8" };
    const _hoisted_4$18 = {
      key: 1,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_5$W = { class: "col-form-label col-sm-4" };
    const _hoisted_6$K = { class: "col-8" };
    const _hoisted_7$F = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_8$C = { class: "col-form-label col-sm-4" };
    const _hoisted_9$x = { class: "col-8" };
    const _hoisted_10$u = ["placeholder"];
    const _hoisted_11$r = ["disabled"];
    const _hoisted_12$n = ["disabled"];


    var script$1G = {
      __name: 'modal-add-application',
      props: {
      page_csrf: String,
      ifid: String,
    },
      emits: ['add'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const is_edit_page = ref(false);
    const _i18n = (t) => i18n(t);
    const disable_add = ref(true);
    let title = ref(i18n('add_application'));
    const comment = ref(i18n('details.custom_rules_placeholder'));
    const selected_category = ref({});
    const category_list = ref([]);
    const custom_rules = ref('');
    const application_name = ref('');
    const application_id = ref(null);

    ref({});

    const note_list = [
      _i18n("custom_categories.each_host_separate_line"),
      _i18n("custom_categories.allowed_rules"),
      _i18n("custom_categories.ip_address"),
      _i18n("custom_categories.ip_address_port"),
      _i18n("custom_categories.ipv6_address"),
      _i18n("custom_categories.ipv6_address_port"),
      _i18n("custom_categories.port"),
      _i18n("custom_categories.port_range"),
      _i18n("custom_categories.host_domain")
    ];

    const showed = () => { };

    function reset_modal_form() {
      application_name.value = '';
      selected_category.value = category_list.value[0];
      custom_rules.value = '';
    }

    const check_validation = () => {
      if (check_application_name() == true && check_custom_rules() == true)
        disable_add.value = false;
      else
        disable_add.value = true;
    };

    const check_application_name = () => {
      return (/^[A-Za-z0-9_-]*$/.test(application_name.value));
    };

    const check_custom_rules = () => {
      let check = true;

      let rules = custom_rules.value.split("\n");
      rules.forEach((rule) => {
        check = check && (/* tcp:1100 */(/^((tcp|udp):(6553[0-5]|655[0-2][0-9]\d|65[0-4](\d){2}|6[0-4](\d){3}|[1-5](\d){4}|[1-9](\d){0,3}))$/.test(rule)) ||
                          /* tcp:1000-1002*/(/^((tcp|udp):(6553[0-5]|655[0-2][0-9]\d|65[0-4](\d){2}|6[0-4](\d){3}|[1-5](\d){4}|[1-9](\d){0,3})-(6553[0-5]|655[0-2][0-9]\d|65[0-4](\d){2}|6[0-4](\d){3}|[1-5](\d){4}|[1-9](\d){0,3}))$/.test(rule)) ||
    //                      (/^((?!.* ).*)$/.test(rule)) ||
                          /* ip:1.1.1.1 */(/^(ip):(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(rule)) ||
                          /* ip:1.1.1.1:1010 */(/^(ip):(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):(6553[0-5]|655[0-2][0-9]\d|65[0-4](\d){2}|6[0-4](\d){3}|[1-5](\d){4}|[1-9](\d){0,3})$/.test(rule)) ||
                          /* ipv6:[1:1:1:1:1:1:1:1] */(/^(ipv6):(\[((([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})|(([0-9a-fA-F]{1,4}:){1,7}:)|(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})|(([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2})|(([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3})|(([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4})|(([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5})|([0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6}))|(:((:[0-9a-fA-F]{1,4}){1,7}|:)))\])$/.test(rule)) ||
                          /* ipv6:[1:1:1:1:1:1:1:1]:1010 */(/^(ipv6):\[((([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})|(([0-9a-fA-F]{1,4}:){1,7}:)|(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})|(([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2})|(([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3})|(([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4})|(([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5})|([0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6}))|(:((:[0-9a-fA-F]{1,4}){1,7}|:)))\]:(6553[0-5]|655[0-2][0-9]\d|65[0-4](\d){2}|6[0-4](\d){3}|[1-5](\d){4}|[1-9](\d){0,3})$/.test(rule)) ||
                          /* host:google */(/^((host):[a-zA-Z0-9]+)$/.test(rule)) ||
                          /* host:google.com */(/(host):[a-zA-Z0-9].[a-zA-Z]/g.test(rule)) ||
                          /* Empty string */rule === '');
      });

      return check
    };

    const populate_modal_form = (row) => {
      let edit_row_category = null;
      category_list.value.forEach((item) => {
        if(item.id == row.category_id) {
          edit_row_category = item;
        }
      });

      selected_category.value = edit_row_category;
      custom_rules.value = row.custom_rules?.replaceAll(',', '\n');
    };

    const show = (row) => {
      reset_modal_form();
      is_edit_page.value = false;
      title.value = i18n('add_application');

      if (row != null) {
        application_id.value = row.application_id;
        application_name.value = row.application;
        is_edit_page.value = true;
        title.value = `${i18n('edit_application')}: ${application_name.value}`;
        populate_modal_form(row);
      }
      modal_id.value.show();
      check_validation();
    };

    const add_ = () => {
      emit('add', {
        l7_proto_id: application_id.value,
        protocol_alias: application_name.value,
        category: selected_category.value.id,
        custom_rules: custom_rules.value,
        is_edit_page: is_edit_page.value,
      });

      close();
    };

    const close = () => {
      modal_id.value.close();
    };

    const format_category_list = (list) => {
      let formatted_list = [];
      list.forEach((item) => {
        formatted_list.push({
          id: item.cat_id,
          label: item.name,
          app_list: item.app_list

        });
      });

      // sort formatted categories;
      formatted_list = formatted_list.sort((a, b) => {
    		    if (a == null || a.label == null) { return -1; }
    		    if (b == null || b.label == null) { return 1; }
    		    return a.label.toString().localeCompare(b.label.toString());
      });

      return formatted_list;
    };

    const loadCategoryList = (list) => {
      category_list.value = format_category_list(list);
    };

    onBeforeMount(() => { });

    __expose({ show, close, loadCategoryList });



    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[4] || (_cache[4] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          withDirectives(createBaseVNode("input", {
            class: "form-control",
            type: "text",
            "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((application_id).value = $event)),
            spellcheck: "false",
            hidden: ""
          }, null, 512 /* NEED_PATCH */), [
            [vModelText, application_id.value]
          ]),
          (is_edit_page.value == false)
            ? (openBlock(), createElementBlock("div", _hoisted_1$1w, [
                createBaseVNode("label", _hoisted_2$1l, [
                  createBaseVNode("b", null, toDisplayString(_i18n("app_name")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_3$1b, [
                  withDirectives(createBaseVNode("input", {
                    class: "form-control",
                    type: "text",
                    "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((application_name).value = $event)),
                    onInput: check_validation,
                    spellcheck: "false"
                  }, null, 544 /* NEED_HYDRATION, NEED_PATCH */), [
                    [vModelText, application_name.value]
                  ])
                ])
              ]))
            : (openBlock(), createElementBlock("div", _hoisted_4$18, [
                createBaseVNode("label", _hoisted_5$W, [
                  createBaseVNode("b", null, toDisplayString(_i18n("category")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_6$K, [
                  createVNode(script$2H, {
                    selected_option: selected_category.value,
                    "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((selected_category).value = $event)),
                    options: category_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ])),
          createBaseVNode("div", _hoisted_7$F, [
            createBaseVNode("label", _hoisted_8$C, [
              createBaseVNode("b", null, toDisplayString(_i18n("category_custom_rule")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_9$x, [
              withDirectives(createBaseVNode("textarea", {
                class: "form-control",
                onInput: check_validation,
                placeholder: comment.value,
                rows: "6",
                "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((custom_rules).value = $event)),
                spellcheck: "false"
              }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_10$u), [
                [vModelText, custom_rules.value]
              ])
            ])
          ])
        ]),
        footer: withCtx(() => [
          createVNode(script$2u, { note_list: note_list }),
          (is_edit_page.value == false)
            ? (openBlock(), createElementBlock("button", {
                key: 0,
                type: "button",
                onClick: add_,
                class: "btn btn-primary",
                disabled: disable_add.value
              }, toDisplayString(_i18n('add')), 9 /* TEXT, PROPS */, _hoisted_11$r))
            : (openBlock(), createElementBlock("button", {
                key: 1,
                type: "button",
                onClick: add_,
                class: "btn btn-primary",
                disabled: disable_add.value
              }, toDisplayString(_i18n('apply')), 9 /* TEXT, PROPS */, _hoisted_12$n))
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1G.__file = "http_src/vue/modal-add-application.vue";

    var script$1F = {
      __name: 'modal-delete-application',
      emits: ['remove'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    const application_name = ref(null);

    let title = ref(i18n('custom_categories.delete_app'));
    const body = i18n('custom_categories.delete_app_confirm');

    const show = (row) => {
      if(row != null) {
        application_name.value = row.application;
        title.value = `${i18n('custom_categories.delete_app')}: ${application_name.value}`;
      }
      modal_id.value.show();
    };

    const _remove = () => {
      emit('remove', { 
        protocol_alias: application_name.value,
      });
        
      close();
    };

    const close = () => {
      modal_id.value.close();
    };

    __expose({ show, close });



    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(unref(body)), 1 /* TEXT */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: _remove,
            class: "btn btn-primary"
          }, toDisplayString(_i18n('delete')), 1 /* TEXT */)
        ]),
        default: withCtx(() => [
          withDirectives(createBaseVNode("input", {
            class: "form-control",
            type: "text",
            "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((application_name).value = $event)),
            spellcheck: "false",
            hidden: ""
          }, null, 512 /* NEED_PATCH */), [
            [vModelText, application_name.value]
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1F.__file = "http_src/vue/modal-delete-application.vue";

    const _hoisted_1$1v = { class: "mt-3" };


    var script$1E = {
      __name: 'page-edit-applications',
      props: {
      page_csrf: String,
      ifid: String,
      has_protos_file: Boolean,
    },
      setup(__props) {

    const applications_table = ref(null);
    const modal_delete_application = ref(null);
    const modal_add_application = ref(null);
    const config_applications_table = ref({});
    const category_list = ref([]);
    const update_message = ref(null);
    const hidden = ref(true);
    let message = '';

    const category_list_url = `${http_prefix}/lua/rest/v2/get/l7/category/consts.lua`;
    const add_application_url = `${http_prefix}/lua/rest/v2/edit/application/application.lua`;
    const delete_application_url = `${http_prefix}/lua/rest/v2/delete/application/application.lua`;

    const _i18n = (t) => i18n(t);
    const props = __props;

    const _remove = async (params) => {
      const url_params = {
        csrf: props.page_csrf,
        ifid: props.ifid
      };

      const url = NtopUtils.buildURL(delete_application_url, {
        ...url_params,
        ...params
      });

      await $.get(url, function (rsp, status) {
        show_message(i18n('custom_categories.succesfully_removed'));
      });

      reload_table();
    };

    const open_delete_modal = (row) => {
      modal_delete_application.value.show(row);
    };

    const _add = async (params) => {
      const is_edit_page = params.is_edit_page;
      params.is_edit_page = null;

      const url_params = {
        csrf: props.page_csrf,
        ifid: props.ifid
      };

      const url = NtopUtils.buildURL(add_application_url, {
        ...url_params,
        ...params
      });

      await $.get(url, function (rsp, status) {
        if (status == 'success') {
          if (is_edit_page)
            show_message(i18n('custom_categories.succesfully_edited'));
          else
            show_message(i18n('custom_categories.succesfully_added'));
        }
      });

      reload_table();
    };

    const open_add_modal = (row) => {
      modal_add_application.value.show(row);
    };

    const show_message = (_message) => {
      message = _message;
      hidden.value = false;
      setTimeout(() => {
        hidden.value = true;
        reload_table();
      }, 4000);
    };

    const destroy = () => {
      applications_table.value.destroy_table();
    };

    const reload_table = () => {
      applications_table.value.reload();
    };

    const load_categories = async () => {
      await $.get(category_list_url, function (rsp, status) {
        category_list.value = rsp.rsp;
      });
      modal_add_application.value.loadCategoryList(category_list.value);
    };

    const search = (filter_app) => {
      applications_table.value.search_value(filter_app);
    };

    onBeforeMount(async () => {
      start_datatable();
    });

    onMounted(async () => {
      await load_categories();
      const filter_app = ntopng_url_manager.get_url_entry("application");
      if (filter_app) {
        search(filter_app);
      }
    });

    onUnmounted(async () => {
      destroy();
    });


    const add_action_column = function (rowData) {
      let edit_handler = {
        handlerId: "edit_rule",
        onClick: () => {
          open_add_modal(rowData);
        },
      };

      const actions = [
        { class: `pointer ${props.has_protos_file ? '' : 'disabled'}`, handler: edit_handler, icon: 'fa-edit', title: i18n('edit') },
      ];

      if (rowData.is_custom) {
        let delete_handler = {
          handlerId: "delete_rule",
          onClick: () => {
            open_delete_modal(rowData);
          },
        };

        actions.push(
          { class: `pointer`, handler: delete_handler, icon: 'fa-trash', title: i18n('delete') },
        );
      }
      return DataTableUtils.createActionButtons(actions);
    };

    const download_categories = () => {
      const downloadUrl = `${http_prefix}/lua/rest/v2/get/ndpi/export/protocols.lua`;
      window.location.href = downloadUrl;
    };

    function start_datatable() {
      const datatableButton = [];

      if (props.has_protos_file) {
        datatableButton.push({
          text: '<i class="fas fa-plus"></i>',
          className: 'btn-link',
          action: function () {
            open_add_modal();
          }
        });
      }

      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function () {
          reload_table();
        }
      },
        {
          text: '<i class="fas fa-download"></i>',
          className: 'btn-link',
          action: function () {
            download_categories();
          }
        });

      let defaultDatatableConfig = {
        table_buttons: datatableButton,
        data_url: NtopUtils.buildURL(`${http_prefix}/lua/rest/v2/get/ntopng/applications.lua`, { ifid: props.ifid }),
        enable_search: true,
        table_config: {
          serverSide: false,
          order: [[0 /* application column */, 'asc']],
        }
      };

      /* Applications table configuration */

      let columns = [
        { columnName: i18n("application"), name: 'application', data: 'application', className: 'text-nowrap', responsivePriority: 1 },
        { columnName: i18n("category"), name: 'category', data: 'category', className: 'text-nowrap', responsivePriority: 1, render: function (data, type, rowData) { return data } },
        { columnName: i18n("custom_categories.custom_hosts"), name: 'custom_rules', data: 'custom_rules', className: 'text-nowrap', responsivePriority: 2 },
        { visible: false, name: 'application_hosts', data: 'application_hosts' },
        { visible: false, name: 'application_id', data: 'application_id' },
        { visible: false, name: 'category_id', data: 'category_id' },
        { columnName: _i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, render: function (_, type, rowData) { return add_action_column(rowData) } }
      ];

      let trafficConfig = ntopng_utility.clone(defaultDatatableConfig);
      trafficConfig.columns_config = columns;
      config_applications_table.value = trafficConfig;
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        _cache[1] || (_cache[1] = createBaseVNode("div", { class: "overlay justify-content-center align-items-center position-absolute h-100 w-100" }, [
          createBaseVNode("div", { class: "text-center" }, [
            createBaseVNode("div", {
              class: "spinner-border text-primary mt-5",
              role: "status"
            }, [
              createBaseVNode("span", { class: "sr-only position-absolute" }, "Loading...")
            ])
          ])
        ], -1 /* CACHED */)),
        withDirectives(createBaseVNode("div", {
          ref_key: "update_message",
          ref: update_message,
          class: "alert alert-info"
        }, toDisplayString(unref(message)), 513 /* TEXT, NEED_PATCH */), [
          [vShow, !hidden.value]
        ]),
        createVNode(script$1G, {
          ref_key: "modal_add_application",
          ref: modal_add_application,
          category_list: category_list.value,
          page_csrf: __props.page_csrf,
          ifid: __props.ifid,
          onAdd: _add
        }, null, 8 /* PROPS */, ["category_list", "page_csrf", "ifid"]),
        createVNode(script$1F, {
          ref_key: "modal_delete_application",
          ref: modal_delete_application,
          onRemove: _remove
        }, null, 512 /* NEED_PATCH */),
        createVNode(script$2e, {
          ref_key: "applications_table",
          ref: applications_table,
          table_buttons: config_applications_table.value.table_buttons,
          columns_config: config_applications_table.value.columns_config,
          data_url: config_applications_table.value.data_url,
          enable_search: config_applications_table.value.enable_search,
          table_config: config_applications_table.value.table_config
        }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "enable_search", "table_config"]),
        createBaseVNode("div", _hoisted_1$1v, [
          createBaseVNode("button", {
            type: "button",
            class: "btn btn-primary",
            onClick: download_categories
          }, [
            _cache[0] || (_cache[0] = createBaseVNode("i", { class: "fas fa-download" }, null, -1 /* CACHED */)),
            createTextVNode(" " + toDisplayString(_i18n("download_applications")), 1 /* TEXT */)
          ])
        ])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1E.__file = "http_src/vue/page-edit-applications.vue";

    /*
      (C) 2013-25 - ntop.org
    */

    const os = [
       { name: "Unknown", icon: '' },
       { name: "Windows", icon: '<i class=\'fab fa-windows fa-lg\'></i>' },
       { name: "macOS", icon: '<i class=\'fab fa-apple fa-lg\'></i>' },
       { name: "iOS", icon: '<i class=\'fab fa-apple fa-lg\'></i>' },
       { name: "Android", icon: '<i class=\'fab fa-android fa-lg\'></i>' },
       { name: "Linux", icon: '<i class=\'fab fa-linux fa-lg\'></i>' },
       { name: "FreeBSD", icon: '' }
    ];

    const asset_icons = [
       { id: 'unknown', icon: '', name: i18n("device_types.unknown") },
       { id: 'printer', icon: '<i class="fas fa-print fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.printer") },
       { id: 'video', icon: '<i class="fas fa-video fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.video") },
       { id: 'workstation', icon: '<i class="fas fa-desktop fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.workstation") },
       { id: 'laptop', icon: '<i class="fas fa-laptop fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.laptop") },
       { id: 'tablet', icon: '<i class="fas fa-tablet fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.tablet") },
       { id: 'phone', icon: '<i class="fas fa-mobile fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.phone") },
       { id: 'tv', icon: '<i class="fas fa-tv fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.tv") },
       { id: 'networking', icon: '<i class="fas fa-arrows-alt fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.networking") },
       { id: 'wifi', icon: '<i class="fas fa-wifi fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.wifi") },
       { id: 'nas', icon: '<i class="fas fa-database fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.nas") },
       { id: 'multimedia', icon: '<i class="fas fa-music fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.multimedia") },
       { id: 'iot', icon: '<i class="fas fa-thermometer fa-lg devtype-icon" aria-hidden="true"></i>', name: i18n("device_types.iot") },
    ];

    const getOSList = () => {
       return icons;
    };

    const getAssetIconsList = () => {
       return asset_icons
    };

    const getOS = (value) => {
       return os[value] || os[0];
    };

    const getAssetIcon = (value) => {
       if(asset_icons[value] != null) {
          return asset_icons[value]["icon"];
       }
       return ''
    };

    const osUtils$1 = function () {
       return {
          getOSList,
          getAssetIconsList,
          getOS,
          getAssetIcon
       };
    }();

    const _hoisted_1$1u = { class: "row" };
    const _hoisted_2$1k = { class: "col-12" };
    const _hoisted_3$1a = { class: "card card-shadow" };
    const _hoisted_4$17 = { class: "card-body" };
    const _hoisted_5$V = {
      key: 0,
      class: "alert alert-danger",
      role: "alert",
      id: "error-alert"
    };
    const _hoisted_6$J = {
      key: 1,
      class: "alert alert-info alert-dismissable"
    };
    const _hoisted_7$E = ["innerHTML"];
    const _hoisted_8$B = {
      key: 2,
      class: "alert alert-danger alert-dismissable"
    };
    const _hoisted_9$w = ["onClick"];
    const _hoisted_10$t = ["innerHTML"];
    const _hoisted_11$q = ["innerHTML"];


    var script$1D = {
      __name: 'page-network-discovery',
      props: {
      context: Object,
    },
      setup(__props) {


    const _i18n = (t) => i18n(t);

    const props = __props;

    const ifid = props.context.ifid;

    const error = ref(false);
    const error_message = i18n("map_page.fetch_error");
    const discovery_requested = ref(false);
    const network_discovery_table = ref(null);
    const progress_message = ref(null);
    const last_network_discovery = ref('');
    const discovery_requested_message = i18n('discover.network_discovery_not_enabled');
    const loading = ref(false);
    const networkDiscoveryPrefEnabled = ref(true);

    const ghost_message = i18n("discover.ghost_icon_descr");
    const too_many_devices_message = i18n("discover.too_many_devices_descr");
    const ghost_message_added = ref(false);

    const timeout_id = ref(null);

    const note_list = [
      i18n("discover.discovery_running"),
      i18n("discover.protocols_note")
    ];

    const run_network_discovery = `${http_prefix}/lua/rest/v2/get/network/discovery/run_discovery.lua`;
    const discovery_url = `${http_prefix}/lua/get_discover_progress.lua`;

    /********** NEW ADDITION **********/

    const table_id = ref("network_discovery");
    const csrf = props.context.csrf;

    const tableButtons = ref([
      {
        text: `${i18n('discover.start_discovery')} <i class="fa-solid fa-play"></i>`,
        className: 'btn btn-link',
        action: async ()  => {
          loading.value = false;

          const url = run_network_discovery + `?ifid=${ifid}`;
          await ntopng_utility$1.http_request(url);

          timeout_id.value = setInterval(checkDiscovery, 1000);
        }
      }
    ]);

    const map_table_def_columns = (columns) => {
      let map_columns = {
        "ip": (value, row) => {
          return `<a href="${http_prefix}/lua/host_details.lua?host=${value}">${value}</a>`;
        },
        "name": (value, row) => {
          return value;
        },
        "manufacturer": (value, row) => {
          return value;
        },
        "mac_address": (value, row) => {
          return value;
        },
        "os": (value, row) => {
          return osUtils$1.getOS(value)["icon"];
        },
        "info": (value, row) => {
          return value;
        },
        "device_type": (value, row) => {
          return osUtils$1.getAssetIcon(value);
        }
      };

      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];
      });

      return columns;
    };


    /********** **********/

    /*  This function add notes to the pages, like adding notes 
     *  to note_list or last network discovery note 
     */
    const add_notes = (rsp) => {
      if (rsp.ghost_found == true && ghost_message_added.value == false) {
        note_list.unshift(ghost_message);
        ghost_message_added.value = true;
      }
      if (rsp.too_many_devices_message == true && too_many_devices_message.value == false) {
        note_list.unshift(too_many_devices_message);
        too_many_devices_message.value = true;
      }
      if (rsp.ghost_found == false && ghost_message_added.value == true) {
        note_list.shift();
        ghost_message_added.value = false;
      }
      if (rsp.too_many_devices_message == false && too_many_devices_message.value == true) {
        note_list.shift();
        too_many_devices_message.value = false;
      }

      last_network_discovery.value = rsp.last_network_discovery;
    };

    /*  This function handle the discovery, asking the backend if  
     *  a new discovery was requested or not and in case updates the notes
     *  and the various messages
     */
    const checkDiscovery = async () => {
      loading.value = false;

      // check discovery process percent
      const url = discovery_url + `?ifid=${ifid}`;
      const rsp = await ntopng_utility$1.http_request(url);

      if (rsp.discovery_requested) {
        discovery_requested.value = true;
        progress_message.value = rsp.progress !== "" ? rsp.progress : "";
      } else {
        network_discovery_table.value.refresh_table();
        discovery_requested.value = false;
        progress_message.value = '';
        clearInterval(timeout_id.value);
      }

      add_notes(rsp);
    };

    // Function to redirect to a url
    function redirectToPreferencesPage() {
      const url = `${http_prefix}/lua/admin/prefs.lua?tab=discovery`;
      ntopng_url_manager.go_to_url(url);
    }

    // Function to get if network discovery is enabled in preferences
    async function checkNetworkDiscoveryEnabled() {
      const url = `${http_prefix}/lua/rest/v2/get/ntopng/get_preferences.lua`;
      const rsp = await ntopng_utility$1.http_request(url);

      networkDiscoveryPrefEnabled.value = rsp.network_discovery;
    }

    const destroy = () => {
      network_discovery_table.value.destroy_table();
    };

    onMounted(async () => {
      await checkNetworkDiscoveryEnabled();
    });

    onUnmounted(async () => {
      destroy();
    });

    function columns_sorting(col, r0, r1) {
      if (col != null) {
        if (col.id == "name") {
          return sortingFunctions$1.sortByName(r0.name, r1.name, col.sort);
        } else if (col.id == "ip") {
          return sortingFunctions$1.sortByIP(r0.ip, r1.ip, col.sort);
        } else if (col.id == "manufacturer") {
          return sortingFunctions$1.sortByName(r0.manufacturer, r1.manufacturer, col.sort);
        } else if (col.id == "mac_address") {
          return sortingFunctions$1.sortByMacAddress(r0.mac_address, r1.mac_address, col.sort);
        } else if (col.id == "os") {
          return sortingFunctions$1.sortByNumber(r0.os, r1.os, col.sort);
        } else if (col.id == "device") {
          return sortingFunctions$1.sortByNumber(r0.device, r1.device, col.sort);
        } 
      }
     
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1u, [
        createBaseVNode("div", _hoisted_2$1k, [
          createBaseVNode("div", _hoisted_3$1a, [
            createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
            createBaseVNode("div", _hoisted_4$17, [
              (error.value)
                ? (openBlock(), createElementBlock("div", _hoisted_5$V, toDisplayString(unref(error_message)), 1 /* TEXT */))
                : createCommentVNode("v-if", true),
              (discovery_requested.value)
                ? (openBlock(), createElementBlock("div", _hoisted_6$J, [
                    _cache[0] || (_cache[0] = createBaseVNode("span", { class: "spinner-border spinner-border-sm text-info" }, null, -1 /* CACHED */)),
                    createTextVNode(" " + toDisplayString(unref(discovery_requested_message)) + " ", 1 /* TEXT */),
                    createBaseVNode("span", { innerHTML: progress_message.value }, null, 8 /* PROPS */, _hoisted_7$E)
                  ]))
                : createCommentVNode("v-if", true),
              createCommentVNode(" Show message that discovery is not enabled in preferences "),
              (!networkDiscoveryPrefEnabled.value)
                ? (openBlock(), createElementBlock("div", _hoisted_8$B, [
                    createTextVNode(toDisplayString(_i18n("network_discovery_disabled")) + " ", 1 /* TEXT */),
                    createBaseVNode("button", {
                      class: "btn btn-small",
                      onClick: redirectToPreferencesPage
                    }, [...(_cache[1] || (_cache[1] = [
                      createBaseVNode("i", { class: "fa-solid fa-gear" }, null, -1 /* CACHED */)
                    ]))])
                  ]))
                : createCommentVNode("v-if", true),
              createBaseVNode("div", {
                class: normalizeClass([(discovery_requested.value || !networkDiscoveryPrefEnabled.value) ? 'ntopng-gray-out' : ''])
              }, [
                createVNode(script$2w, {
                  ref_key: "network_discovery_table",
                  ref: network_discovery_table,
                  table_id: table_id.value,
                  csrf: unref(csrf),
                  f_map_columns: map_table_def_columns,
                  f_sort_rows: columns_sorting
                }, {
                  custom_header: withCtx(() => [
                    (openBlock(true), createElementBlock(Fragment, null, renderList(tableButtons.value, (button, index) => {
                      return (openBlock(), createElementBlock("button", {
                        key: index,
                        class: normalizeClass(button.className),
                        onClick: button.action
                      }, [
                        createBaseVNode("span", {
                          innerHTML: button.text
                        }, null, 8 /* PROPS */, _hoisted_10$t)
                      ], 10 /* CLASS, PROPS */, _hoisted_9$w))
                    }), 128 /* KEYED_FRAGMENT */))
                  ]),
                  _: 1 /* STABLE */
                }, 8 /* PROPS */, ["table_id", "csrf"])
              ], 2 /* CLASS */),
              createVNode(script$2u, { note_list: note_list }),
              createCommentVNode(" Adding Extra Message "),
              createBaseVNode("div", {
                class: "p-1",
                innerHTML: last_network_discovery.value
              }, null, 8 /* PROPS */, _hoisted_11$q)
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1D.__file = "http_src/vue/page-network-discovery.vue";

    const _hoisted_1$1t = { class: "row" };
    const _hoisted_2$1j = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$19 = { class: "card card-shadow" };
    const _hoisted_4$16 = { class: "card-body" };


    var script$1C = {
      __name: 'page-manage-configurations-backup',
      props: {
      date_format: String,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const table_manage_configurations_backup = ref(null);
    const url = `${http_prefix}/lua/rest/v2/get/system/configurations/list_available_backups.lua`;
    const table_config = ref({});
    const loading = ref(true);

    const props = __props;


    const format_flows_icon = function (data, rowData) {
      const ms_data = data * 1000;
      let date_format = "HH:MM:SS";
      if (props.date_format == "little_endian") {
        date_format = "DD/MM/YYYY " + date_format;
      } else if (props.date_format == "middle_endian") {
        date_format = "MM/DD/YYYY " + date_format;
      } else {
        date_format = "YYYY/MM/DD " + date_format;
      }
      return ntopng_utility.from_utc_to_server_date_format(ms_data, date_format);
    };


    const reload_table = () => {
      loading.value = true;
      table_manage_configurations_backup.value.reload();
      loading.value = false;
    };

    onBeforeMount(async () => {
      await set_datatable_config();
    });

    const trigger_download = function (row) {
      window.open(`${http_prefix}/lua/rest/v2/get/system/configurations/download_backup.lua?epoch=${row.epoch}&download=true`);
    };

    const add_action_column = function (rowData) {

      let download_backup_handler = {
        handlerId: "download_backup_handler",
        onClick: () => {
          trigger_download(rowData);
        },
      };

      return DataTableUtils.createActionButtons([
        { class: `pointer`, handler: download_backup_handler, icon: 'fas fa-download fa-lg', title: i18n('download') },
      ]);
    };

    async function set_datatable_config() {
      const datatableButton = [];

      /* Manage the buttons close to the search box */
      datatableButton.push({
        text: '<i class="fas fa-sync"></i>',
        className: 'btn-link',
        action: function (e, dt, node, config) {
          reload_table();
        }
      });


      let defaultDatatableConfig = {
        table_buttons: datatableButton,
        data_url: `${url}`,
        enable_search: true,
        id: "manage_configurations_backup",
        table_config: {
          serverSide: true,
          responsive: false,
          scrollX: true,
          columnDefs: [
            { type: "file-size", targets: 0 },
          ]
        }
      };

      let columns = [];

      columns.push(
        {
          columnName: _i18n("backup_date"), orderable: true, targets: 0, name: 'epoch', data: 'epoch', className: 'text-left', responsivePriority: 1, render: (data, _, rowData) => {
            return format_flows_icon(data)
          }
        }, {
        columnName: _i18n("actions"), orderable: false, width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, render: function (_, type, rowData) { return add_action_column(rowData) }
      }
        ,
      );



      defaultDatatableConfig.columns_config = columns;
      table_config.value = defaultDatatableConfig;
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1t, [
        createBaseVNode("div", _hoisted_2$1j, [
          createBaseVNode("div", _hoisted_3$19, [
            createBaseVNode("div", _hoisted_4$16, [
              createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
              createBaseVNode("div", {
                id: "manage_configurations_backup",
                class: normalizeClass([ loading.value ? 'ntopng-gray-out' : '' ])
              }, [
                (openBlock(), createBlock(script$2e, {
                  ref_key: "table_manage_configurations_backup",
                  ref: table_manage_configurations_backup,
                  id: table_config.value.id,
                  key: table_config.value.data_url,
                  table_buttons: table_config.value.table_buttons,
                  columns_config: table_config.value.columns_config,
                  data_url: table_config.value.data_url,
                  table_config: table_config.value.table_config
                }, {
                  menu: withCtx(() => [...(_cache[0] || (_cache[0] = [
                    createBaseVNode("div", { class: "d-flex align-items-center" }, null, -1 /* CACHED */)
                  ]))]),
                  _: 1 /* STABLE */
                }, 8 /* PROPS */, ["id", "table_buttons", "columns_config", "data_url", "table_config"]))
              ], 2 /* CLASS */)
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1C.__file = "http_src/vue/page-manage-configurations-backup.vue";

    const _hoisted_1$1s = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$1i = { class: "col-form-label col-sm-2" };
    const _hoisted_3$18 = { class: "col-10" };
    const _hoisted_4$15 = {
      key: 0,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_5$U = { class: "col-form-label col-sm-2" };
    const _hoisted_6$I = { class: "col-10" };
    const _hoisted_7$D = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_8$A = { class: "col-form-label col-sm-2" };
    const _hoisted_9$v = { class: "col-10" };
    const _hoisted_10$s = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_11$p = { class: "col-form-label col-sm-2" };
    const _hoisted_12$m = { class: "col-10" };
    const _hoisted_13$k = {
      class: "form-group ms-2 me-2 mt-3 row",
      style: {"margin-top":"3px"}
    };
    const _hoisted_14$i = { class: "col-form-label col-sm-2" };
    const _hoisted_15$h = {
      key: 0,
      class: "col-sm-4"
    };
    const _hoisted_16$h = {
      key: 1,
      class: "col-sm-3"
    };
    const _hoisted_17$f = {
      class: "btn-group float-end btn-group-toggle",
      "data-bs-toggle": "buttons"
    };
    const _hoisted_18$c = ["value", "id"];
    const _hoisted_19$b = ["id", "for"];
    const _hoisted_20$b = ["hidden"];
    const _hoisted_21$b = ["value", "id"];
    const _hoisted_22$b = ["id", "for"];
    const _hoisted_23$b = {
      class: "btn-group float-end btn-group-toggle",
      "data-bs-toggle": "buttons"
    };
    const _hoisted_24$a = ["value", "id"];
    const _hoisted_25$9 = ["id", "for"];
    const _hoisted_26$8 = {
      key: 5,
      class: "col-sm-2 btn-group float-end btn-group-toggle",
      "data-bs-toggle": "buttons"
    };
    const _hoisted_27$8 = ["value", "id"];
    const _hoisted_28$7 = ["id", "for"];
    const _hoisted_29$7 = {
      key: 6,
      class: "col-sm-2 btn-group float-end btn-group-toggle",
      "data-bs-toggle": "buttons"
    };
    const _hoisted_30$7 = ["value", "id"];
    const _hoisted_31$6 = ["id", "for"];
    const _hoisted_32$6 = {
      key: 1,
      class: "message alert alert-warning mt-3"
    };
    const _hoisted_33$6 = {
      key: 2,
      class: "message alert alert-info mt-3"
    };


    var script$1B = {
      __name: 'modal-add-snmp-device-rules',
      props: {
      ifid_list: Array,
      snmp_devices_list: Array,
      snmp_metric_list: Array,
      frequency_list: Array,
      init_func: Function,
      page_csrf: String,
    },
      emits: ['add', 'edit'],
      setup(__props, { expose: __expose, emit: __emit }) {

    ref("");
    ref("");

    const modal_id = ref(null);
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    ref(null);
    ref(null);
    const snmp_metric_list = ref([]);
    const snmp_devices_list = ref([]);
    let snmp_interfaces_list = ref([]);
    let enable_interfaces = ref(true);
    const snmp_interfaces_url = `${http_prefix}/lua/pro/rest/v2/get/snmp/device/available_interfaces.lua`;

    const frequency_list = ref([]);
    const threshold_measure = ref(null);
    const threshold_sign = ref(null);
    const selected_metric = ref({});
    const selected_snmp_device = ref({});
    const selected_snmp_interface = ref({});

    const selected_snmp_device_metric = ref({});
    const selected_frequency = ref({});
    const disable_add = ref(true);
    const metric_type = ref({});
    const visible = ref(true);
    const rule_type = ref("snmp");
    const is_edit_page = ref(false);
    const page_csrf_ = ref(null);
    let metric_type_active_list = ref([]);

    let title = _i18n('if_stats_config.add_host_rules_title');


    const note_list = [
      _i18n('if_stats_config.note_snmp_device_rules.note_1'),
      _i18n('if_stats_config.note_snmp_device_rules.note_2'),
      _i18n('if_stats_config.note_snmp_device_rules.note_3'),
      _i18n('if_stats_config.note_3'),
      _i18n('if_stats_config.note_4')
    ];

    const sub_notes_list = [
      _i18n('if_stats_config.note_5'),
      _i18n('if_stats_config.note_6')
    ];

    const metric_type_list = ref([
      { title: _i18n('volume'), label: _i18n('volume'), id: 'volume', active: true },
      { title: _i18n('throughput'), label: _i18n('throughput'), id: 'throughput', active: false },
      { title: _i18n('packets'), label: _i18n('packets'), id: 'packets', active: false },
      { title: _i18n('percentage_change'), label: _i18n('percentage_change'), id: 'percentage_change', active: false },
      { title: _i18n('percentage_absolute'), label: _i18n('percentage_absolute'), id: 'percentage_absolute', active: false }

    ]);

    const volume_threshold_list = ref([
      { title: _i18n('kb'), label: _i18n('kb'), id: 'kb', value: 1024, active: false },
      { title: _i18n('mb'), label: _i18n('mb'), id: 'mb', value: 1048576, active: false },
      { title: _i18n('gb'), label: _i18n('gb'), id: 'gb', value: 1073741824, active: true, default_active: true },
    ]);

    const throughput_threshold_list = ref([
      { title: _i18n('kbps'), label: _i18n('kbps'), id: 'kbps', value: 1000, active: false },
      { title: _i18n('mbps'), label: _i18n('mbps'), id: 'mbps', value: 1000000, active: false },
      { title: _i18n('gbps'), label: _i18n('gbps'), id: 'gbps', value: 1000000000, active: true, default_active: true },
    ]);

    const sign_threshold_list = ref([
      { title: "+", label: ">", id: 'plus', value: 1, active: false, absolute_value: true },
      { title: "-", label: "<", id: 'minus', value: -1, active: true, default_active: true },
    ]);

    const sign_absolute_value = ref([
      { title: "+", label: ">", id: 'plus', value: 1, active: true, absolute_value: true },
    ]);

    const percentage_threshold_list = [
      { title: "+", label: "%", id: 'plus', value: 1, active: true },
    ];


    const host = ref(null);
    const threshold = ref(null);

    const showed = () => { };

    const props = __props;

    const rest_params = {
      csrf: props.page_csrf
    };

    function reset_radio_selection(radio_array) {

      radio_array.forEach((item) => item.active = item.default_active == true);
    }

    /**
     * 
     * Reset fields in modal form 
     */
    const reset_modal_form = async function () {

      host.value = "";
      selected_metric.value = snmp_metric_list.value[0];
      selected_snmp_device.value = null;
      selected_snmp_device.value = snmp_devices_list.value[0];
      change_interfaces();

      selected_snmp_device_metric.value = snmp_metric_list.value[0];
      change_active_threshold();

      selected_frequency.value = frequency_list.value[0];
      metric_type.value = metric_type_list.value[0];

      // reset metric_type_list
      metric_type_list.value.forEach((t) => t.active = false);
      metric_type_list.value[0].active = true;

      reset_radio_selection(volume_threshold_list.value);
      reset_radio_selection(throughput_threshold_list.value);
      reset_radio_selection(sign_threshold_list.value);

      rule_type.value = "snmp";

      disable_add.value = true;
      enable_interfaces.value = false;

      threshold.value.value = 1;
      is_edit_page.value = false;
      title = _i18n('if_stats_config.add_host_rules_title');

    };




    /**
     * 
     * Set row to edit 
     */
    const set_row_to_edit = (row) => {
      if (row != null) {
        is_edit_page.value = true;
        title = _i18n('if_stats_config.edit_host_rules_title');

        disable_add.value = false;

        snmp_devices_list.value.forEach((item) => {
          if (item.label_to_insert == row.device.id)
            selected_snmp_device.value = item;
        });

        // set threshold sign
        sign_threshold_list.value.forEach((t) => {
          t.active = (t.value == row.threshold.sign);
        });

        snmp_metric_list.value.forEach((t) => {
          if (t.id == row.metric.id)
            selected_snmp_device_metric.value = t;
        });

        // set threshold
        if (row.metric.type == 'volume')
          volume_threshold_list.value.forEach((t) => {
            if ((row.threshold.value % t.value) == 0) {
              let row_threshold_value = row.threshold.value / t.value;
              if (row_threshold_value < 1024) {
                t.active = true;
                threshold.value.value = row_threshold_value == 0 ? 1 : row_threshold_value;
              } else {
                t.active = false;
              }
            } else {
              t.active = false;
            }
          });
        else if (row.metric.type == 'throughput') {
          row.threshold.value = row.threshold.value * 8;
          throughput_threshold_list.value.forEach((t) => {
            if ((row.threshold % t.value) == 0) {
              let row_threshold_value = row.threshold.value / t.value;
              if (row_threshold_value < 1000) {
                t.active = true;
                threshold.value.value = row_threshold_value == 0 ? 1 : row_threshold_value;
              } else {
                t.active = false;
              }
            } else {
              t.active = false;
            }
          });
        } else {

          //percentage case
          threshold.value.value = row.threshold.value * row.threshold.sign;

        }
        change_active_threshold();
        metric_type_active_list.value.forEach((item) => {
          if (item.id == row.metric.type) {
            metric_type.value = item;
            item.active = true;
          } else
            item.active = false;
        });

        snmp_devices_list.value.forEach((t) => {
          if (t.label == row.device.id)
            selected_snmp_device.value = t;
        });

        frequency_list.value.forEach((item) => {
          if (item.id == row.frequency)
            selected_frequency.value = item;
        });

        change_interfaces(row.interface.id);

      }
    };

    const show = (row) => {
      if (row != null) {
        set_row_to_edit(row.row);
      } else {
        reset_modal_form();
      }
      modal_id.value.show();
    };


    const set_active_sign_radio = (selected_radio) => {
      const id = selected_radio.target.id;
      sign_threshold_list.value.forEach((measure) => {
        (measure.id === id) ? measure.active = true : measure.active = false;
      });

    };

    /**
     * 
     * Set the metric type
     */
    const set_active_radio = (selected_radio) => {
      const id = selected_radio.target.id;

      if (metric_type.value.id == 'throughput') {
        throughput_threshold_list.value.forEach((measure) => {
          (measure.id === id) ? measure.active = true : measure.active = false;
        });
      } else if (metric_type.value.id == 'volume') {
        volume_threshold_list.value.forEach((measure) => {
          (measure.id === id) ? measure.active = true : measure.active = false;
        });
      } else if (metric_type.value.id == 'percentage_change' || metric_type.value.id == 'percentage_absolute') {
        percentage_threshold_list.forEach((measure) => {
          (measure.id === id) ? measure.active = true : measure.active = false;
        });
      } else if (metric_type.value.id == 'packets') ;

    };


    async function change_interfaces(interface_id) {
      const url = NtopUtils$1.buildURL(snmp_interfaces_url + "?host=" + selected_snmp_device.value.label_to_insert, rest_params);
      let interfaces_list = [];
      await $.get(url, function (rsp, status) {
        interfaces_list = rsp.rsp;
      });
      let result_interfaces = [];

      interfaces_list.forEach(iface => {
        if (iface.name != null && iface.name != "" && iface.name != iface.id)
          result_interfaces.push({ label: iface.name + " (" + iface.id + ")", id: iface.id, name: iface.name });
        else
          result_interfaces.push({ label: iface.id, id: iface.id, name: iface.id });
      });
      result_interfaces.push({ label: "*", id: "*", name: "*" });
      result_interfaces.sort(function (a, b) { return (a.label.toLowerCase() > b.label.toLowerCase() ? 1 : (a.label.toLowerCase() < b.label.toLowerCase()) ? -1 : 0); });

      if (interface_id != null)
        result_interfaces.forEach((t) => {
          if (t.id == interface_id)
            selected_snmp_interface.value = t;
        });
      snmp_interfaces_list.value = result_interfaces;
      // debugger;
      if (selected_snmp_device.value.label_to_insert == "all")
        enable_interfaces.value = false;
      else
        enable_interfaces.value = true;

    }

    function change_active_threshold() {
      let list_metrics_active = [];
      if (selected_snmp_device_metric.value.id == 'packets' || selected_snmp_device_metric.value.id == 'usage') {
        metric_type_list.value.forEach((t) => {
          if (t.id != 'percentage_absolute')
            t.active = false;
          else {
            t.active = true;
            list_metrics_active.push(t);
            metric_type.value = t;
          }
        });
      } else if (selected_snmp_device_metric.value.id == 'errors') {
        metric_type_list.value.forEach((t) => {
          if (t.id != 'packets')
            t.active = false;
          else {
            t.active = true;
            list_metrics_active.push(t);
            metric_type.value = t;
          }
        });

      } else {
        metric_type_list.value.forEach((t) => {
          if (t.id == 'packets')
            t.active = false;
          else {
            list_metrics_active.push(t);
          }
        });
      }


      metric_type_active_list.value = list_metrics_active;
    }



    /**
     * Function to add rule to rules list
     */
    const add_ = (is_edit) => {
      rule_type.value = 'snmp';
      host.value;

      const tmp_frequency = selected_frequency.value.id;
      const tmp_metric = selected_snmp_device_metric.value.id;
      const tmp_metric_label = selected_snmp_device_metric.value.label;
      const tmp_device = selected_snmp_device.value.label_to_insert;
      const tmp_device_label = selected_snmp_device.value.label;
      const tmp_device_ifid = selected_snmp_interface.value == null || Object.entries(selected_snmp_interface.value).length === 0 || tmp_device_label === "*" ? "*" : selected_snmp_interface.value.id;
      const tmp_device_ifid_label = selected_snmp_interface.value == null || Object.entries(selected_snmp_interface.value).length === 0 || tmp_device_label === "*" ? "*" : selected_snmp_interface.value.label;

      let tmp_metric_type = metric_type.value.id;
      let basic_value;
      let measure_unit_label;
      let basic_sign_value;
      let tmp_threshold;
      let tmp_sign_value;

      if (visible.value === false) {
        tmp_metric_type = '';
        tmp_extra_metric = '';
        tmp_threshold = threshold.value.value;
      }
      if (tmp_metric_type == 'throughput') {

        sign_threshold_list.value.forEach((measure) => { if (measure.active) basic_sign_value = measure.value; });
        tmp_sign_value = parseInt(basic_sign_value);
        throughput_threshold_list.value.forEach((measure) => { if (measure.active) { basic_value = measure.value; measure_unit_label = measure.label; } });
        tmp_threshold = basic_value * parseInt(threshold.value.value) / 8;
        /* The throughput is in bit, the volume in Bytes!! */
      } else if (tmp_metric_type == 'volume') {
        sign_threshold_list.value.forEach((measure) => { if (measure.active) basic_sign_value = measure.value; });
        tmp_sign_value = parseInt(basic_sign_value);
        volume_threshold_list.value.forEach((measure) => { if (measure.active) { basic_value = measure.value; measure_unit_label = measure.label; } });
        tmp_threshold = basic_value * parseInt(threshold.value.value);
      } else if (tmp_metric_type == 'percentage_change' || tmp_metric_type == 'percentage_absolute') {
        sign_threshold_list.value.forEach((measure) => { if (measure.active) basic_sign_value = measure.value; });
        tmp_sign_value = parseInt(basic_sign_value);
        tmp_threshold = tmp_sign_value * parseInt(threshold.value.value);
        measure_unit_label = "%";
      } else {
        tmp_sign_value = 1;
        tmp_threshold = parseInt(threshold.value.value);
      }
      let emit_name = 'add';

      if (is_edit == true)
        emit_name = 'edit';

      emit(emit_name, {
        frequency: tmp_frequency,
        metric: tmp_metric,
        metric_label: tmp_metric_label,
        threshold: tmp_threshold,
        metric_type: tmp_metric_type,
        snmp_device: tmp_device,
        snmp_device_label: tmp_device_label,
        snmp_device_port: tmp_device_ifid,
        snmp_device_port_label: tmp_device_ifid_label,
        rule_threshold_sign: tmp_sign_value,
        snmp_threshold_value: threshold.value.value,
        snmp_threshold_unit: measure_unit_label,
        snmp_metric_type_label: metric_type.value.title
      });



      close();
    };


    const edit_ = () => {
      add_(true);
    };

    const close = () => {
      modal_id.value.close();
    };

    const format_snmp_devices_list = function (_snmp_devices_list) {
      let devices_list = [];
      _snmp_devices_list.forEach(item => {
        if (item.name != null && item.name != "" && item.name != "all")
          devices_list.push({ label: item.name + " (" + item.key + ")", label_to_insert: item.key });
        else {
          if (item.name == "all")
            devices_list.push({ label: item.key, label_to_insert: item.name });
          else
            devices_list.push({ label: item.key, label_to_insert: item.key });
        }

      });

      devices_list.sort(function (a, b) { return (a.label.toLowerCase() > b.label.toLowerCase() ? 1 : (a.label.toLowerCase() < b.label.toLowerCase()) ? -1 : 0); });
      return devices_list;
    };

    const metricsLoaded = (_snmp_devices_list, _snmp_metric_list, page_csrf) => {

      snmp_devices_list.value = format_snmp_devices_list(_snmp_devices_list);
      snmp_metric_list.value = _snmp_metric_list;
      frequency_list.value = props.frequency_list;
      selected_frequency.value = frequency_list.value[0];
      selected_metric.value = snmp_metric_list.value[0];
      page_csrf_.value = page_csrf;


    };


    onBeforeMount(() => {
      metric_type_list.value.forEach((t) => {
        if (t.active) {
          metric_type.value = t;
        }

      });
    });

    __expose({ show, close, metricsLoaded });



    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[8] || (_cache[8] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Target information, here an IP is put "),
          createBaseVNode("div", _hoisted_1$1s, [
            createBaseVNode("label", _hoisted_2$1i, [
              createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.snmp_device")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$18, [
              createVNode(script$2H, {
                selected_option: selected_snmp_device.value,
                "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_snmp_device).value = $event)),
                onSelect_option: _cache[1] || (_cache[1] = $event => (change_interfaces())),
                options: snmp_devices_list.value
              }, null, 8 /* PROPS */, ["selected_option", "options"])
            ])
          ]),
          (unref(enable_interfaces) == true)
            ? (openBlock(), createElementBlock("div", _hoisted_4$15, [
                createBaseVNode("label", _hoisted_5$U, [
                  createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.snmp_interface")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_6$I, [
                  createVNode(script$2H, {
                    selected_option: selected_snmp_interface.value,
                    "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((selected_snmp_interface).value = $event)),
                    options: unref(snmp_interfaces_list)
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", _hoisted_7$D, [
            createBaseVNode("label", _hoisted_8$A, [
              createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.metric")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_9$v, [
              createVNode(script$2H, {
                selected_option: selected_snmp_device_metric.value,
                "onUpdate:selected_option": _cache[3] || (_cache[3] = $event => ((selected_snmp_device_metric).value = $event)),
                onSelect_option: _cache[4] || (_cache[4] = $event => (change_active_threshold())),
                options: snmp_metric_list.value
              }, null, 8 /* PROPS */, ["selected_option", "options"])
            ])
          ]),
          createCommentVNode(" Frequency information, a frequency of 1 day, 5 minute or 1 hour for example "),
          createBaseVNode("div", _hoisted_10$s, [
            createBaseVNode("label", _hoisted_11$p, [
              createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.frequency")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_12$m, [
              createVNode(script$2H, {
                selected_option: selected_frequency.value,
                "onUpdate:selected_option": _cache[5] || (_cache[5] = $event => ((selected_frequency).value = $event)),
                options: frequency_list.value
              }, null, 8 /* PROPS */, ["selected_option", "options"])
            ])
          ]),
          createCommentVNode(" Threshold information, maximum amount of bytes "),
          createBaseVNode("div", _hoisted_13$k, [
            createBaseVNode("label", _hoisted_14$i, [
              createBaseVNode("b", null, toDisplayString(_i18n("if_stats_config.threshold")), 1 /* TEXT */)
            ]),
            (visible.value)
              ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                  (metric_type.value.id == 'percentage_change' || metric_type.value.id == 'percentage_absolute')
                    ? (openBlock(), createElementBlock("div", _hoisted_15$h, [
                        createVNode(script$2H, {
                          selected_option: metric_type.value,
                          "onUpdate:selected_option": _cache[6] || (_cache[6] = $event => ((metric_type).value = $event)),
                          options: unref(metric_type_active_list)
                        }, null, 8 /* PROPS */, ["selected_option", "options"])
                      ]))
                    : (openBlock(), createElementBlock("div", _hoisted_16$h, [
                        createVNode(script$2H, {
                          selected_option: metric_type.value,
                          "onUpdate:selected_option": _cache[7] || (_cache[7] = $event => ((metric_type).value = $event)),
                          options: unref(metric_type_active_list)
                        }, null, 8 /* PROPS */, ["selected_option", "options"])
                      ])),
                  (metric_type.value.id == 'throughput' && metric_type.value.id != 'packets')
                    ? (openBlock(), createElementBlock("div", {
                        key: 2,
                        class: normalizeClass(["col-3", [metric_type.value.id == 'throughput' ? 'p-0' : '']])
                      }, [
                        createBaseVNode("div", _hoisted_17$f, [
                          (openBlock(true), createElementBlock(Fragment, null, renderList(throughput_threshold_list.value, (measure) => {
                            return (openBlock(), createElementBlock(Fragment, null, [
                              createBaseVNode("input", {
                                value: measure.value,
                                id: measure.id,
                                type: "radio",
                                class: "btn-check",
                                autocomplete: "off",
                                ref_for: true,
                                ref_key: "threshold_measure",
                                ref: threshold_measure,
                                name: "threshold_measure"
                              }, null, 8 /* PROPS */, _hoisted_18$c),
                              createBaseVNode("label", {
                                class: normalizeClass(["btn", [measure.active ? 'btn-primary active' : 'btn-secondary']]),
                                id: measure.id,
                                onClick: set_active_radio,
                                for: measure.id
                              }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_19$b)
                            ], 64 /* STABLE_FRAGMENT */))
                          }), 256 /* UNKEYED_FRAGMENT */))
                        ])
                      ], 2 /* CLASS */))
                    : createCommentVNode("v-if", true),
                  ((metric_type.value.id == 'percentage_change' || metric_type.value.id == 'percentage_absolute') && metric_type.value.id != 'packets')
                    ? (openBlock(), createElementBlock("div", {
                        key: 3,
                        class: normalizeClass(["col-2", [metric_type.value.id == 'throughput' ? 'p-0' : '']])
                      }, [
                        createBaseVNode("div", {
                          class: "btn-group float-end btn-group-toggle",
                          "data-bs-toggle": "buttons",
                          hidden: metric_type.value.id == 'percentage_absolute'
                        }, [
                          (openBlock(), createElementBlock(Fragment, null, renderList(percentage_threshold_list, (measure) => {
                            return (openBlock(), createElementBlock(Fragment, null, [
                              createBaseVNode("input", {
                                value: measure.value,
                                id: measure.id,
                                type: "radio",
                                class: "btn-check",
                                autocomplete: "off",
                                ref_for: true,
                                ref_key: "threshold_measure",
                                ref: threshold_measure,
                                name: "threshold_measure"
                              }, null, 8 /* PROPS */, _hoisted_21$b),
                              createBaseVNode("label", {
                                class: normalizeClass(["btn", [measure.active ? 'btn-primary active' : 'btn-secondary']]),
                                id: measure.id,
                                onClick: set_active_radio,
                                for: measure.id
                              }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_22$b)
                            ], 64 /* STABLE_FRAGMENT */))
                          }), 64 /* STABLE_FRAGMENT */))
                        ], 8 /* PROPS */, _hoisted_20$b)
                      ], 2 /* CLASS */))
                    : createCommentVNode("v-if", true),
                  (metric_type.value.id == 'volume' && metric_type.value.id != 'packets')
                    ? (openBlock(), createElementBlock("div", {
                        key: 4,
                        class: normalizeClass(["col-3", [metric_type.value.id == 'throughput' ? 'p-0' : '']])
                      }, [
                        createBaseVNode("div", _hoisted_23$b, [
                          (openBlock(true), createElementBlock(Fragment, null, renderList(volume_threshold_list.value, (measure) => {
                            return (openBlock(), createElementBlock(Fragment, null, [
                              createBaseVNode("input", {
                                value: measure.value,
                                id: measure.id,
                                type: "radio",
                                class: "btn-check",
                                autocomplete: "off",
                                ref_for: true,
                                ref_key: "threshold_measure",
                                ref: threshold_measure,
                                name: "threshold_measure"
                              }, null, 8 /* PROPS */, _hoisted_24$a),
                              createBaseVNode("label", {
                                class: normalizeClass(["btn", [measure.active ? 'btn-primary active' : 'btn-secondary']]),
                                id: measure.id,
                                onClick: set_active_radio,
                                for: measure.id
                              }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_25$9)
                            ], 64 /* STABLE_FRAGMENT */))
                          }), 256 /* UNKEYED_FRAGMENT */))
                        ])
                      ], 2 /* CLASS */))
                    : createCommentVNode("v-if", true),
                  (metric_type.value.id != 'packets')
                    ? (openBlock(), createElementBlock("div", _hoisted_26$8, [
                        (openBlock(true), createElementBlock(Fragment, null, renderList(sign_threshold_list.value, (measure) => {
                          return (openBlock(), createElementBlock(Fragment, null, [
                            createBaseVNode("input", {
                              value: measure.value,
                              id: measure.id,
                              type: "radio",
                              class: "btn-check",
                              autocomplete: "off",
                              ref_for: true,
                              ref_key: "threshold_sign",
                              ref: threshold_sign,
                              name: "threshold_sign"
                            }, null, 8 /* PROPS */, _hoisted_27$8),
                            createBaseVNode("label", {
                              class: normalizeClass(["btn", [measure.active ? 'btn-primary active' : 'btn-secondary']]),
                              id: measure.id,
                              onClick: set_active_sign_radio,
                              for: measure.id
                            }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_28$7)
                          ], 64 /* STABLE_FRAGMENT */))
                        }), 256 /* UNKEYED_FRAGMENT */))
                      ]))
                    : (openBlock(), createElementBlock("div", _hoisted_29$7, [
                        (openBlock(true), createElementBlock(Fragment, null, renderList(sign_absolute_value.value, (measure) => {
                          return (openBlock(), createElementBlock(Fragment, null, [
                            createBaseVNode("input", {
                              value: measure.value,
                              id: measure.id,
                              type: "radio",
                              class: "btn-check",
                              autocomplete: "off",
                              ref_for: true,
                              ref_key: "threshold_sign",
                              ref: threshold_sign,
                              name: "threshold_sign"
                            }, null, 8 /* PROPS */, _hoisted_30$7),
                            createBaseVNode("label", {
                              class: normalizeClass(["btn", [measure.absolute_value ? 'btn-primary active' : 'btn-secondary']]),
                              id: measure.id,
                              for: measure.id
                            }, toDisplayString(measure.label), 11 /* TEXT, CLASS, PROPS */, _hoisted_31$6)
                          ], 64 /* STABLE_FRAGMENT */))
                        }), 256 /* UNKEYED_FRAGMENT */))
                      ]))
                ], 64 /* STABLE_FRAGMENT */))
              : createCommentVNode("v-if", true),
            createBaseVNode("div", {
              class: normalizeClass([visible.value ? 'col-sm-2' : 'col-sm-8'])
            }, [
              (metric_type.value.id == 'percentage_change' || metric_type.value.id == 'percentage_absolute')
                ? (openBlock(), createElementBlock("input", {
                    key: 0,
                    value: "1",
                    ref_key: "threshold",
                    ref: threshold,
                    type: "number",
                    name: "threshold",
                    class: "form-control",
                    max: "100",
                    min: "1",
                    required: ""
                  }, null, 512 /* NEED_PATCH */))
                : (openBlock(), createElementBlock("input", {
                    key: 1,
                    value: "1",
                    ref_key: "threshold",
                    ref: threshold,
                    type: "number",
                    name: "threshold",
                    class: "form-control",
                    max: "1023",
                    min: "1",
                    required: ""
                  }, null, 512 /* NEED_PATCH */))
            ], 2 /* CLASS */)
          ]),
          (selected_snmp_device_metric.value.id != 'usage' && metric_type.value.id == 'percentage_change')
            ? (openBlock(), createElementBlock("div", _hoisted_32$6, toDisplayString(_i18n("show_alerts.host_rules_percentage")), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (metric_type.value.id == 'percentage_absolute')
            ? (openBlock(), createElementBlock("div", _hoisted_33$6, toDisplayString(_i18n("show_alerts.host_rules_percentage_absolute")), 1 /* TEXT */))
            : createCommentVNode("v-if", true)
        ]),
        footer: withCtx(() => [
          createVNode(script$2u, {
            note_list: note_list,
            add_sub_notes: true,
            sub_note_list: sub_notes_list
          }),
          (is_edit_page.value == false)
            ? (openBlock(), createElementBlock("button", {
                key: 0,
                type: "button",
                onClick: add_,
                class: "btn btn-primary"
              }, toDisplayString(_i18n('add')), 1 /* TEXT */))
            : (openBlock(), createElementBlock("button", {
                key: 1,
                type: "button",
                onClick: edit_,
                class: "btn btn-primary"
              }, toDisplayString(_i18n('apply')), 1 /* TEXT */))
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1B.__file = "http_src/vue/modal-add-snmp-device-rules.vue";

    const _hoisted_1$1r = { class: "row" };
    const _hoisted_2$1h = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$17 = {
      key: 0,
      class: "alert alert-warning alert-dismissable"
    };
    const _hoisted_4$14 = { class: "card" };
    const _hoisted_5$T = { class: "card-body" };
    const _hoisted_6$H = { class: "m-2 mb-3" };
    const _hoisted_7$C = ["title"];
    const _hoisted_8$z = { class: "card-footer" };


    var script$1A = {
      __name: 'page-snmp-device-rules',
      props: {
      context: Object,
    },
      setup(__props) {

    const props = __props;

    const context = ref({
      csrf: props.context.csrf,
      ifid: props.context.ifid,
      is_check_enabled: props.context.is_check_enabled
    });

    ref(null);
    const modal_delete_confirm = ref(null);
    const modal_add_snmp_device_rule = ref(null);
    const _i18n = (t) => i18n(t);
    const row_to_delete = ref({});
    const row_to_edit = ref({});

    const snmp_metric_url = `${http_prefix}/lua/pro/rest/v2/get/snmp/metric/rule_metrics.lua`;
    const snmp_devices_url = `${http_prefix}/lua/pro/rest/v2/get/snmp/list.lua`;

    const add_rule_url = `${http_prefix}/lua/pro/rest/v2/add/snmp/device/rule.lua`;
    const remove_rule_url = `${http_prefix}/lua/pro/rest/v2/delete/snmp/device/rule.lua`;
    const table_snmp_rules = ref();
    const table_id = ref("snmp_device_rules");

    const note_list = [
      _i18n('if_stats_config.generic_notes_1'),
      _i18n('if_stats_config.generic_notes_2'),
      _i18n('if_stats_config.generic_notes_3'),
    ];

    const rest_params = {
      ifid: context.value.ifid,
      csrf: context.value.csrf,
    };

    let title_delete = _i18n('if_stats_config.delete_host_rules_title');
    let body_delete = _i18n('if_stats_config.delete_host_rules_description');
    const active_alert_text = _i18n('snmp.snmp_devices_rules_active_alert_warning');
    let snmp_metric_list = [];
    let snmp_devices_list = [];


    const frequency_list = [
      { title: i18n('show_alerts.5_min'), label: i18n('show_alerts.5_min'), id: '5min' },
      { title: i18n('show_alerts.hourly'), label: i18n('show_alerts.hourly'), id: 'hour' },
      { title: i18n('show_alerts.daily'), label: i18n('show_alerts.daily'), id: 'day' }
    ];

    /* ******************************************************************** */

    const click_button_delete = function (row) {
      row_to_delete.value = row;
      modal_delete_confirm.value.show();
    };

    /* ******************************************************************** */

    const click_button_edit = function (row) {
      row_to_edit.value = row;
      row_to_delete.value = row;
      modal_add_snmp_device_rule.value.show(row);
    };

    /* ******************************************************************** */

    const get_snmp_metric_list = async function () {
      const url = NtopUtils$1.buildURL(snmp_metric_url, rest_params);

      await $.get(url, function (rsp, status) {
        snmp_metric_list = rsp.rsp;
      });
    };

    /* ******************************************************************** */

    const get_snmp_devices_list = async function () {
      rest_params.verbose = true;
      const url = NtopUtils$1.buildURL(snmp_devices_url, rest_params);
      await $.get(url, function (rsp, status) {
        snmp_devices_list = rsp.rsp;
      });
      snmp_devices_list.push({ key: "*", name: "all" });
    };

    /* ******************************************************************** */

    const format_threshold = function (data, row) {
      let threshold;
      let formatted_data;

      (data.sign && data.sign == '-1') ?
        threshold = "<" : threshold = ">";

      if (row.metric.type == 'volume') {
        formatted_data = formatterUtils.getFormatter("bytes")(data.value);
      } else if (row.metric.type == 'throughput') {
        formatted_data = formatterUtils.getFormatter("bps_no_scale")(data.value);
      } else {
        formatted_data = formatterUtils.getFormatter("percentage")(data.value);
      }

      return `${threshold} ${formatted_data}`
    };

    /* ******************************************************************** */

    /* Function to map columns data */
    const map_table_def_columns = (columns) => {
      let map_columns = {
        "device": (data, row) => {
          return data.label;
        },
        "interface": (data, row) => {
          return data.label;
        },
        "metric": (data, row) => {
          return data.label;
        },
        "frequency": (data, row) => {
          return frequency_list.find((el) => el.id == data).label;
        },
        "threshold": (data, row) => {
          return format_threshold(data, row);
        },
      };

      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];
        if (c.id == "actions") {
          const visible_dict = {
            edit_rule: true,
            delete_host: true,
          };
          c.button_def_array.forEach((b) => {
            if (!visible_dict[b.id]) {
              b.class.push("disabled");
            }
          });
        }
      });

      return columns;
    };

    /* ************************************** */

    function on_table_custom_event(event) {
      let events_managed = {
        "click_button_edit": click_button_edit,
        "click_button_delete": click_button_delete,
      };
      if (events_managed[event.event_id] == null) {
        return;
      }
      events_managed[event.event_id](event);
    }

    /* ******************************************** */

    /* Function used to sort the columns of the table */
    function columns_sorting(col, r0, r1) {
      if (col != null) {
        let r0_col = column_data(col, r0);
        let r1_col = column_data(col, r1);

        if (col.id == "device") {
          return sortingFunctions$1.sortByName(r0_col.label, r1_col.label, col.sort);
        } else if (col.id == "interface") {
          return sortingFunctions$1.sortByName(r0_col.label, r1_col.label, col.sort);
        } else if (col.id == "metric") {
          return sortingFunctions$1.sortByName(r0_col.label, r1_col.label, col.sort);
        } else if (col.id == "frequency") {
          return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
        } else if (col.id == "threshold") {
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col.value, r1_col.value, col.sort);
        }
      }
    }

    /* ******************************************************************** */

    const open_add_host_rule_modal = function() {
      modal_add_snmp_device_rule.value.show();
    };

    /* ******************************************************************** */

    const reload_table = function () {
      table_snmp_rules.value.refresh_table();
    };

    /* ******************************************************************** */

    const delete_row = async function () {
      const row = row_to_delete.value.row;
      const url = NtopUtils$1.buildURL(remove_rule_url, {
        ...rest_params,
        ...{
          rule_id: row.id,
        }
      });

      await $.post(url, function (rsp, status) {
        reload_table();
      });
    };

    /* ******************************************************************** */

    async function edit(params) {
      await delete_row();
      await add_host_rule(params);
    }

    /* ******************************************************************** */

    const init_edit = function () {
      const row = row_to_edit.value;
      row_to_edit.value = null;
      return row;
    };

    /* ******************************************************************** */

    const add_host_rule = async function (params) {
      const url = NtopUtils$1.buildURL(add_rule_url, {
        ...rest_params,
        ...params
      });

      await $.post(url, function (rsp, status) {
        reload_table();
      });
    };

    /* ******************************************** */

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager.get_url_object();
      return extra_params;
    };

    /* ******************************************** */

    onBeforeMount(async () => {
      await get_snmp_metric_list();
      await get_snmp_devices_list();
      modal_add_snmp_device_rule.value.metricsLoaded(snmp_devices_list, snmp_metric_list, context.csrf);
    });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1r, [
        createVNode(script$2a, {
          ref_key: "modal_delete_confirm",
          ref: modal_delete_confirm,
          title: unref(title_delete),
          body: unref(body_delete),
          onDelete: delete_row
        }, null, 8 /* PROPS */, ["title", "body"]),
        createVNode(script$1B, {
          ref_key: "modal_add_snmp_device_rule",
          ref: modal_add_snmp_device_rule,
          frequency_list: frequency_list,
          init_func: init_edit,
          onAdd: add_host_rule,
          onEdit: edit
        }, null, 512 /* NEED_PATCH */),
        createBaseVNode("div", _hoisted_2$1h, [
          (!context.value.is_check_enabled)
            ? (openBlock(), createElementBlock("div", _hoisted_3$17, [
                _cache[0] || (_cache[0] = createBaseVNode("span", { class: "text-warning me-1" }, null, -1 /* CACHED */)),
                createBaseVNode("span", null, toDisplayString(unref(active_alert_text)), 1 /* TEXT */)
              ]))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", _hoisted_4$14, [
            createBaseVNode("div", _hoisted_5$T, [
              createBaseVNode("div", _hoisted_6$H, [
                createVNode(script$2w, {
                  ref_key: "table_snmp_rules",
                  ref: table_snmp_rules,
                  table_id: table_id.value,
                  csrf: context.value.csrf,
                  showLoading: true,
                  f_map_columns: map_table_def_columns,
                  get_extra_params_obj: get_extra_params_obj,
                  f_sort_rows: columns_sorting,
                  onCustom_event: on_table_custom_event
                }, {
                  custom_buttons: withCtx(() => [
                    createBaseVNode("button", {
                      class: "btn btn-link",
                      type: "button",
                      ref: "add_host",
                      onClick: open_add_host_rule_modal
                    }, [
                      createBaseVNode("i", {
                        class: "fas fa-plus",
                        "data-bs-toggle": "tooltip",
                        "data-bs-placement": "top",
                        title: _i18n('add_vs_host')
                      }, null, 8 /* PROPS */, _hoisted_7$C)
                    ], 512 /* NEED_PATCH */)
                  ]),
                  _: 1 /* STABLE */
                }, 8 /* PROPS */, ["table_id", "csrf"])
              ])
            ]),
            createBaseVNode("div", _hoisted_8$z, [
              createVNode(script$2u, { note_list: note_list })
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1A.__file = "http_src/vue/page-snmp-device-rules.vue";

    const _hoisted_1$1q = { class: "row" };
    const _hoisted_2$1g = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$16 = { class: "card card-shadow" };
    const _hoisted_4$13 = { class: "card-body" };
    const _hoisted_5$S = { class: "d-flex mb-3" };
    const _hoisted_6$G = { class: "d-flex align-items-center ml-2 mb-2" };
    const _hoisted_7$B = {
      class: "d-flex no-wrap",
      style: {"text-align":"left","margin-right":"1rem","min-width":"25rem"}
    };
    const _hoisted_8$y = { class: "my-auto me-1" };
    const _hoisted_9$u = { class: "d-flex align-items-center mb-2" };
    const _hoisted_10$r = {
      class: "d-flex no-wrap",
      style: {"text-align":"left","margin-right":"1rem","min-width":"25rem"}
    };
    const _hoisted_11$o = { class: "my-auto me-1" };
    const _hoisted_12$l = { class: "d-flex align-items-center mb-2" };
    const _hoisted_13$j = {
      class: "d-flex no-wrap",
      style: {"text-align":"left","margin-right":"1rem","min-width":"25rem"}
    };
    const _hoisted_14$h = { class: "my-auto me-1" };
    const _hoisted_15$g = ["title"];
    const _hoisted_16$g = ["onClick", "title"];

    var script$1z = {
      __name: 'page-server-ports',
      props: {
        is_ntop_enterprise_m: Boolean,
        csrf: String,
        vlans: Array,
        ifid: Number,
        aggregation_criteria: String,
        page: Number,
        sort: String,
        order: String,
        start: Number,
        length: Number,
        host: String,
    },
      setup(__props) {

    const filter_table_array = ref([]);
    const filter_table_dropdown_array = ref([]);

    const props = __props;
    ref({
        csrf: props.csrf,
        ifid: props.ifid
    });
    const _i18n = (t) => i18n(t);

    /* L4 Protocol List */
    const criteria_list_def = [
        { label: _i18n("tcp"), value: 6, param: "tcp", table_id: "tcp_ports_analysis", enterprise_m: false },
        { label: _i18n("udp"), value: 17, param: "udp", table_id: "udp_ports_analysis", enterprise_m: false },
    ];


    /* Consts */
    const selected_criteria = ref(criteria_list_def[0]);
    const table_id = ref('server_ports');
    const selected_port = ref({});
    const selected_application = ref({});
    const table_server_ports = ref();

    let port_list = ref([]);
    let application_list = ref([]);

    const criteria_list = function () {
        if (props.is_ntop_enterprise_m) {
            return ref(criteria_list_def);
        }
        else {
            let critera_list_def_com = [];
            criteria_list_def.forEach((c) => {
                if (!c.enterprise_m)
                    critera_list_def_com.push(c);
            });
            return ref(critera_list_def_com);
        }
    }();

    onMounted(async () => {
        let port = ntopng_url_manager$1.get_url_entry('port');
        let l4_proto = ntopng_url_manager$1.get_url_entry('protocol');
        const l7_proto = ntopng_url_manager$1.get_url_entry('application');  
        ntopng_url_manager$1.set_key_to_url('ifid', props.ifid); /* Current interface */


        if (port != null && port.localeCompare("") != 0 &&
            l4_proto != null && l4_proto.localeCompare("") != 0 &&
            l7_proto != null && l7_proto.localeCompare("") != 0) {
            
            port = Number(port);
            l4_proto = Number(l4_proto);
            selected_criteria.value = criteria_list_def.find((proto) => (proto.value == l4_proto));
            

            await update_dropdown_menus(false, l7_proto, port);

        } else {
            selected_criteria.value = criteria_list_def[0];
            await update_dropdown_menus(false);

        }

        load_table_filters_overview();
        table_server_ports.value.refresh_table();


    });


    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    /* Function to update L4 Protocol */
    async function update_criteria() {
        await update_dropdown_menus(false);
        table_server_ports.value.refresh_table();

    }
    /* Function to update Application */
    async function update_port_list() {
        await update_dropdown_menus(true);
        table_server_ports.value.refresh_table();
    }

    /* Function to update port */
    function update_port() {
        set_port_in_url();
        table_server_ports.value.refresh_table();
    }

    function set_port_in_url() {
        ntopng_url_manager$1.set_key_to_url("port", selected_port.value.id);
    }


    /* Function to load filters (Just VLANs) */
    async function load_table_filters_array(action, filter) {
        let extra_params = get_extra_params_obj();
        let url_params = ntopng_url_manager$1.obj_to_url_params(extra_params);
        const url = `${http_prefix}/lua/pro/rest/v2/get/host/hosts_details_by_port_filters.lua?action=${action}&${url_params}`;
        let res = await ntopng_utility$1.http_request(url);
        return res.map((t) => {
            return {
                id: t.action || t.name,
                label: t.label,
                title: t.tooltip,
                data_loaded: action != 'overview',
                options: t.value,
                hidden: (t.value.length == 1)
            };
        });
    }

    const get_open_filter_table_dropdown = (filter, filter_index) => {
        return (_) => {
            load_table_filters(filter, filter_index);
        };
    };

    async function load_table_filters(filter, filter_index) {
        filter.show_spinner = true;
        await nextTick();
        if (filter.data_loaded == false) {
            let new_filter_array = await load_table_filters_array(filter.id);
            filter.options = new_filter_array.find((t) => t.id == filter.id).options;
            await nextTick();
            let dropdown = filter_table_dropdown_array.value[filter_index];
            dropdown.load_menu();
        }
        filter.show_spinner = false;
    }

    async function load_table_filters_overview(action) {
        filter_table_array.value = await load_table_filters_array("overview");
        set_filter_array_label();
    }

    /* Function to handle actions entries */
    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_flows": click_button_flows,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    function click_button_flows(event) {
        live_flows(event.row.ip);
    }

    const live_flows = function (data) {

        let params = {
            l4proto: selected_criteria.value.value,
            server: data,
            port: selected_port.value.id,
            vlan: ntopng_url_manager$1.get_url_entry('vlan_id')
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        const url = `${http_prefix}/lua/flows_stats.lua?${url_params}`;
        ntopng_url_manager$1.go_to_url(`${url}`);
    };

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            const url_entry = ntopng_url_manager$1.get_url_entry(el.id);
            if (url_entry != null) {
                el.options.forEach((option) => {
                    if (option.value.toString() === url_entry) {
                        el.label = `${el.basic_label}: ${option.label || option.value}`;
                    }
                });
            }
        });
    }

    function add_table_filter(opt, event) {
        event.stopPropagation();
        ntopng_url_manager$1.set_key_to_url(opt.key, `${opt.value}`);
        set_filter_array_label();
        table_server_ports.value.refresh_table();
    }

    /* Function to update dropdown menus */
    async function update_dropdown_menus(is_application_selected, app, port) {
        ntopng_url_manager$1.set_key_to_url("protocol", selected_criteria.value.value);
        const vlan_id = ntopng_url_manager$1.get_url_entry("vlan_id") || "";
        const url = `${http_prefix}/lua/pro/rest/v2/get/host/server_ports.lua?protocol=${selected_criteria.value.value}&ifid=${props.ifid}&vlan_id=${vlan_id}`;
        let res = await ntopng_utility$1.http_request(url, null, null, true);
        let ports = [];
        application_list.value = [];
        port_list.value = [];

        res.rsp.forEach((item) => {
            let name = item.l7_proto_name;
            ports.push({ label: `${item.srv_port}/${name} (${item.n_hosts})`, id: item.srv_port, application: name, application_id: item.proto_id,num_hosts: item.n_hosts, vlan_id:item.vlan_id });
        });

        ports.forEach((port) => {
            let proto_id = port.application_id;
            if (! application_list.value.find(item => item.id == proto_id)) {
                application_list.value.push({ label: port.application, id: port.application_id, value: proto_id });
            }
        });

        application_list.value.sort((a, b) => {
            let x = a.label.toLowerCase();
            let y = b.label.toLowerCase();

            if (x < y) { return -1; }
            if (x > y) { return 1; }
            return 0;
        });

        if (!is_application_selected) {
          // by default select first l7_proto
          selected_application.value =  (app == null) ? 
                                        application_list.value[0] :
                                        application_list.value.find((item) => (item.id == app));
        }
        ntopng_url_manager$1.set_key_to_url("application", selected_application.value.id);
        ports.forEach((item) => {
            if (item.application == selected_application.value.label)
                port_list.value.push({ label: item.id + " (" + item.num_hosts + ")", id: item.id, value: item.id, vlan_id: item.vlan_id, n_hosts: item.num_hosts });
        });

        port_list.value.sort((a, b) => {
            let x = a.id;
            let y = b.id;

            if (x < y) { return -1; }
            if (x > y) { return 1; }
            return 0;
        });

        if (port != null) {
            selected_port.value = port_list.value.find((item) => (item.id == port));
        } else {
            selected_port.value = port_list.value[0];
        }

        set_port_in_url();
    }

    /* Function to format data */
    const map_table_def_columns = async (columns) => {
        let map_columns = {
            "ip": (ip, row) => {
                if (ip !== undefined) {
                    return format_ip(ip, row);
                }
            },
            "name": (name, row) => {
                if (name !== undefined) {
                    return format_host_name(name, row);
                }
            },
            "mac": (mac, row) => {
                if (mac !== undefined) {
                    return format_mac(mac);
                }
            },
            "tot_traffic": (tot_traffic, row) => {
                if (tot_traffic !== undefined) {
                    return NtopUtils$1.bytesToSize(tot_traffic);
                }
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });
        // console.log(columns);
        return columns;
    };

    /* Function to format IP label */
    const format_ip = function (data, rowData) {
        if (data != null) {
            if (rowData.vlan_id != 0)
                return `<a href="${http_prefix}/lua/flows_stats.lua?server=${data}&vlan=${rowData.vlan_id}&port=${selected_port.value.id}">${data}@${rowData.vlan_id}</a>`;
            else
                return `<a href="${http_prefix}/lua/flows_stats.lua?server=${data}&port=${selected_port.value.id}">${data}</a>`;
        }
        return data;

    };

    /* Function to format MAC Address label */
    const format_mac = function (data, rowData) {
        if (data != null)
            return `<a href="${http_prefix}/lua/mac_details.lua?host=${data}">${data}</a>`;
        return data;
    };

    /* Function to format Host Name label */
    const format_host_name = function (data, rowData) {
        if (data != null) {
            if (rowData.vlan_id != 0)
                return `<a href="${http_prefix}/lua/host_details.lua?host=${rowData.ip}&vlan=${rowData.vlan_id}">${data}</a>`
            else
                return `<a href="${http_prefix}/lua/host_details.lua?host=${rowData.ip}">${data}</a>`
        }
        return data;
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1q, [
        createBaseVNode("div", _hoisted_2$1g, [
          createBaseVNode("div", _hoisted_3$16, [
            createBaseVNode("div", _hoisted_4$13, [
              createBaseVNode("div", _hoisted_5$S, [
                createBaseVNode("div", _hoisted_6$G, [
                  createBaseVNode("div", _hoisted_7$B, [
                    createBaseVNode("label", _hoisted_8$y, toDisplayString(_i18n('protocol')) + ": ", 1 /* TEXT */),
                    createVNode(script$2H, {
                      selected_option: selected_criteria.value,
                      "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_criteria).value = $event)),
                      options: unref(criteria_list),
                      onSelect_option: update_criteria
                    }, null, 8 /* PROPS */, ["selected_option", "options"])
                  ])
                ]),
                createBaseVNode("div", _hoisted_9$u, [
                  createBaseVNode("div", _hoisted_10$r, [
                    createBaseVNode("label", _hoisted_11$o, toDisplayString(_i18n('application')) + ": ", 1 /* TEXT */),
                    createVNode(script$2H, {
                      selected_option: selected_application.value,
                      "onUpdate:selected_option": _cache[1] || (_cache[1] = $event => ((selected_application).value = $event)),
                      options: unref(application_list),
                      onSelect_option: update_port_list
                    }, null, 8 /* PROPS */, ["selected_option", "options"])
                  ])
                ]),
                createBaseVNode("div", _hoisted_12$l, [
                  createBaseVNode("div", _hoisted_13$j, [
                    createBaseVNode("label", _hoisted_14$h, toDisplayString(_i18n('db_search.tags.srv_port')) + ": ", 1 /* TEXT */),
                    createVNode(script$2H, {
                      selected_option: selected_port.value,
                      "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((selected_port).value = $event)),
                      options: unref(port_list),
                      onSelect_option: update_port
                    }, null, 8 /* PROPS */, ["selected_option", "options"])
                  ])
                ])
              ]),
              createBaseVNode("div", null, [
                createVNode(script$2w, {
                  ref_key: "table_server_ports",
                  ref: table_server_ports,
                  csrf: __props.csrf,
                  table_id: table_id.value,
                  showLoading: true,
                  f_map_columns: map_table_def_columns,
                  get_extra_params_obj: get_extra_params_obj,
                  onCustom_event: on_table_custom_event
                }, {
                  custom_header: withCtx(() => [
                    (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (t, t_index) => {
                      return (openBlock(), createBlock(script$2B, {
                        f_on_open: get_open_filter_table_dropdown(t, t_index),
                        ref_for: true,
                        ref: el => { filter_table_dropdown_array.value[t_index] = el; },
                        hidden: t.hidden
                      }, {
                        title: withCtx(() => [
                          createVNode(script$2v, {
                            show: t.show_spinner,
                            size: "1rem",
                            class: "me-1"
                          }, null, 8 /* PROPS */, ["show"]),
                          createBaseVNode("a", {
                            class: "ntopng-truncate",
                            title: t.title
                          }, toDisplayString(t.label), 9 /* TEXT, PROPS */, _hoisted_15$g)
                        ]),
                        menu: withCtx(() => [
                          (openBlock(true), createElementBlock(Fragment, null, renderList(t.options, (opt) => {
                            return (openBlock(), createElementBlock("a", {
                              style: {"cursor":"pointer","display":"block"},
                              onClick: $event => (add_table_filter(opt, $event)),
                              class: "ntopng-truncate tag-filter",
                              title: opt.value
                            }, [
                              (opt.count == null)
                                ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                                    createTextVNode(toDisplayString(opt.label), 1 /* TEXT */)
                                  ], 64 /* STABLE_FRAGMENT */))
                                : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                                    createTextVNode(toDisplayString(opt.label + " (" + opt.count + ")"), 1 /* TEXT */)
                                  ], 64 /* STABLE_FRAGMENT */))
                            ], 8 /* PROPS */, _hoisted_16$g))
                          }), 256 /* UNKEYED_FRAGMENT */))
                        ]),
                        _: 2 /* DYNAMIC */
                      }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["f_on_open", "hidden"]))
                    }), 256 /* UNKEYED_FRAGMENT */))
                  ]),
                  _: 1 /* STABLE */
                }, 8 /* PROPS */, ["csrf", "table_id"])
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1z.__file = "http_src/vue/page-server-ports.vue";

    const _hoisted_1$1p = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$1f = { class: "col-form-label col-sm-3" };
    const _hoisted_3$15 = { class: "col-7" };
    const _hoisted_4$12 = { key: 0 };
    const _hoisted_5$R = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_6$F = { class: "col-form-label col-sm-3" };
    const _hoisted_7$A = { class: "col-7" };
    const _hoisted_8$x = ["placeholder"];
    const _hoisted_9$t = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_10$q = { class: "col-form-label col-sm-3" };
    const _hoisted_11$n = { class: "col-7" };
    const _hoisted_12$k = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_13$i = { class: "col-form-label col-sm-3" };
    const _hoisted_14$g = { class: "col-7" };
    const _hoisted_15$f = { class: "form-text text-muted" };
    const _hoisted_16$f = ["disabled"];


    var script$1y = {
      __name: 'modal-nedge-add-repeater-config',
      props: {},
      emits: ['edit', 'add'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);
    const host_placeholder = i18n('if_stats_config.multicast_ip_placeholder');
    const modal_id = ref(null);
    const selected_interfaces = ref([]);
    const selected_restricted_interfaces = ref([]);
    const ip = ref(null);
    const repeater_type = ref({value: "mdns", label: "MDNS" });
    const emit = __emit;

    const interfaces_search = ref(null);
    const restricted_interfaces_search = ref(null);

    const showed = () => {};

    const disable_add = ref(true);
    const invalid_iface_number = ref(true);
    const not_valid_ip = ref(true);

    const check_empty_host = () => {
      let regex = new RegExp(regexValidation.get_data_pattern('ip'));
      not_valid_ip.value = !(regex.test(ip.value) || ip.value === '*');
    	disable_add.value = update_disable_add();
    };


    const update_disable_add = () => {
    	if (repeater_type.value.value == 'custom') {
    		return (invalid_iface_number.value || not_valid_ip.value);
    	} else {
    		return (invalid_iface_number.value);
    	}
    };

    const title = ref("");

    const repeater_type_array = [
    	{ label: _i18n("nedge.page_repeater_config.modal_repeater_config.mdns"), value: "mdns", default: true },
    	{ label: _i18n("nedge.page_repeater_config.modal_repeater_config.custom"), value: "custom" },
    ];

    const repeater_id = ref(0);

    const selected_repeater_type = ref({});

    const interface_list_url = `${http_prefix}/lua/pro/rest/v2/get/nedge/interfaces.lua`;
    let interface_list;
    const interface_array = ref([]);

    const button_text = ref("");

    const all_criteria = (item) => {
    	invalid_iface_number.value = ((selected_interfaces.value.length + selected_restricted_interfaces.value.length) < 2);
    	disable_add.value = update_disable_add();
    };

    const update_interfaces_selected = (items) => {
    	selected_interfaces.value = items;
    };

    const remove_interfaces_selected = (item_to_delete) => {
    	selected_interfaces.value = selected_interfaces.value.filter((item) => item.label != item_to_delete.label);
    };

    const update_restricted_interfaces_selected = (items) => {
    	selected_restricted_interfaces.value = items;
    };

    const remove_restricted_interfaces_selected = (item_to_delete) => {
    	selected_restricted_interfaces.value = selected_restricted_interfaces.value.filter((item) => item.label != item_to_delete.label);
    };

    const reset_modal = () => {
    	selected_repeater_type.value = {};
    	ip.value = "";
    	selected_interfaces.value = [];
    	selected_restricted_interfaces.value = [];
    	not_valid_ip.value = true;
    	invalid_iface_number.value = true;
    };

    const show = (row ) => {
    	reset_modal();
    	init(row);
    	modal_id.value.show();
    };

    const is_open_in_add = ref(true);

    function init(row) {
    	is_open_in_add.value = row == null;

    	// check if we need open in edit
    	if (is_open_in_add.value == false) {
    			not_valid_ip.value = false;
    			invalid_iface_number.value = false;
    			disable_add.value = false;
    			title.value = _i18n("nedge.page_rules_config.modal_rule_config.title_edit");
    			button_text.value = _i18n("edit");
    			repeater_id.value = row.repeater_id;
    			selected_repeater_type.value = repeater_type_array.find((s) => (s.value == row.type));
    			if (selected_repeater_type.value.value == 'custom') {
    				ip.value = row.ip;
    			}
    			change_repeater_type();

    	} else {
    			title.value = _i18n("nedge.page_rules_config.modal_rule_config.title_add");
    			button_text.value = _i18n("add");
    			repeater_type_array.find((s) => s.default == true);
    	}
    		
    	if (is_open_in_add.value == false) {
    		const row_interfaces = row.interfaces.split(",");
    		let tmp_selected_interfaces = [];
    		row_interfaces.forEach((row_iface) => {
    			if (row_iface != '' && row_iface != null) {
    				tmp_selected_interfaces.push(interface_array.value.find((iface) => iface.value == row_iface));
    			}
    		});
    		selected_interfaces.value = tmp_selected_interfaces;

    		const row_restricted_interfaces = row.restricted_interfaces.split(",");
    		let tmp_selected_restricted_interfaces = [];
    		row_restricted_interfaces.forEach((row_iface) => {
    			if (row_iface != '' && row_iface != null) {
    				tmp_selected_restricted_interfaces.push(interface_array.value.find((iface) => iface.value == row_iface));
    			}
    		});
    		selected_restricted_interfaces.value = tmp_selected_restricted_interfaces;
    	}
    }

    async function change_repeater_type(type) {
    	repeater_type.value = selected_repeater_type.value;
    	if (repeater_type.value.value == "custom") {
    		await set_interface_array();
    	}
    }

    let is_set_interface_array = false;
    async function set_interface_array() {
    	if (is_set_interface_array == true) { return; }
    	if (interface_list == null) {
    		interface_list = ntopng_utility$1.http_request(interface_list_url);
    	}
    	let res_interface_list = await interface_list;
    	interface_array.value = res_interface_list.filter(i => i.role == "lan").map((i) => {
    		return {
    			label: i.label,
    			value: i.ifname,
    		};
    	});
    	is_set_interface_array = true;
    }

    const apply = () => {
    	let repeater_t = repeater_type.value.value;
    	let obj = {
    			repeater_type: repeater_t,
    	};
    	if (repeater_type.value.value == "custom") {
    		let ip_t = ip.value;
    		obj = {
    			repeater_type: repeater_t,
    			ip: ip_t,
    		};
    	}

    	let event = "add";
    	if (is_open_in_add.value == false) {
    		obj.repeater_id = repeater_id.value;
    		event = "edit";
    	}

    	let interfaces = [];
    	let details = [];
    	selected_interfaces.value.forEach((i) => {
    		interfaces.push(i.value);
    		
    		if(i.value != i.label && !i.label.includes(i.value)) {
    			details.push(i.label+" ("+i.value+")");
    		} else {
    			details.push(i.label);
    		}
    	});
    	const tmp_interfaces = interfaces.join(",");
    	const tmp_details = details.join(",");
    	obj.interfaces = tmp_interfaces;
    	obj.interface_details = tmp_details;

    	let restricted_interfaces = [];
    	let restricted_details = [];
    	selected_restricted_interfaces.value.forEach((i) => {
    		restricted_interfaces.push(i.value);
    		
    		if(i.value != i.label && !i.label.includes(i.value)) {
    			restricted_details.push(i.label+" ("+i.value+")");
    		} else {
    			restricted_details.push(i.label);
    		}
    	});
    	const tmp_restricted_interfaces = restricted_interfaces.join(",");
    	const tmp_restricted_details = restricted_details.join(",");
    	obj.restricted_interfaces = tmp_restricted_interfaces;
    	obj.restricted_interface_details = tmp_restricted_details;

    	emit(event, obj);
    	close();
    };

    const close = () => {
    	modal_id.value.close();
    };

    __expose({ show, close });

    onMounted(async () => {
    	await set_interface_array();

    });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[5] || (_cache[5] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Repeater Type "),
          createBaseVNode("div", _hoisted_1$1p, [
            createBaseVNode("label", _hoisted_2$1f, [
              createBaseVNode("b", null, toDisplayString(_i18n("nedge.page_repeater_config.modal_repeater_config.repeater_type")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$15, [
              createVNode(script$2H, {
                selected_option: selected_repeater_type.value,
                "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_repeater_type).value = $event)),
                onSelect_option: _cache[1] || (_cache[1] = $event => (change_repeater_type())),
                options: repeater_type_array
              }, null, 8 /* PROPS */, ["selected_option"])
            ])
          ]),
          createCommentVNode(" IP "),
          (selected_repeater_type.value.value == 'custom')
            ? (openBlock(), createElementBlock("div", _hoisted_4$12, [
                createBaseVNode("div", _hoisted_5$R, [
                  createBaseVNode("label", _hoisted_6$F, [
                    createBaseVNode("b", null, toDisplayString(_i18n("nedge.page_repeater_config.ip")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_7$A, [
                    withDirectives(createBaseVNode("input", {
                      "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((ip).value = $event)),
                      onFocusout: check_empty_host,
                      class: "form-control col-7",
                      type: "text",
                      placeholder: unref(host_placeholder),
                      required: ""
                    }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_8$x), [
                      [vModelText, ip.value]
                    ])
                  ])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Interface "),
          createBaseVNode("div", _hoisted_9$t, [
            createBaseVNode("label", _hoisted_10$q, [
              createBaseVNode("b", null, toDisplayString(_i18n("nedge.page_repeater_config.interfaces")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_11$n, [
              createVNode(script$2H, {
                ref_key: "interfaces_search",
                ref: interfaces_search,
                selected_options: selected_interfaces.value,
                "onUpdate:selected_options": _cache[3] || (_cache[3] = $event => ((selected_interfaces).value = $event)),
                options: interface_array.value,
                multiple: true,
                onSelect_option: update_interfaces_selected,
                onUnselect_option: remove_interfaces_selected,
                onChange_selected_options: all_criteria
              }, null, 8 /* PROPS */, ["selected_options", "options"])
            ])
          ]),
          createBaseVNode("div", _hoisted_12$k, [
            createBaseVNode("label", _hoisted_13$i, [
              createBaseVNode("b", null, toDisplayString(_i18n("nedge.page_repeater_config.restricted_interfaces")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_14$g, [
              createVNode(script$2H, {
                ref_key: "restricted_interfaces_search",
                ref: restricted_interfaces_search,
                selected_options: selected_restricted_interfaces.value,
                "onUpdate:selected_options": _cache[4] || (_cache[4] = $event => ((selected_restricted_interfaces).value = $event)),
                options: interface_array.value,
                multiple: true,
                onSelect_option: update_restricted_interfaces_selected,
                onUnselect_option: remove_restricted_interfaces_selected,
                onChange_selected_options: all_criteria
              }, null, 8 /* PROPS */, ["selected_options", "options"])
            ]),
            createBaseVNode("small", _hoisted_15$f, toDisplayString(_i18n("nedge.page_repeater_config.restricted_interfaces_note")), 1 /* TEXT */)
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            disabled: disable_add.value,
            onClick: apply,
            class: "btn btn-primary"
          }, toDisplayString(button_text.value), 9 /* TEXT, PROPS */, _hoisted_16$f)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$l = "\ninput[data-v-5b3dc248]:invalid {\n  border-color: #ff0000;\n}\n";
    styleInject(css_248z$l);

    script$1y.__scopeId = "data-v-5b3dc248";
    script$1y.__file = "http_src/vue/modal-nedge-add-repeater-config.vue";

    const _hoisted_1$1o = { class: "mb-2" };
    const _hoisted_2$1e = { id: "aggregated_live_flows" };

    const timeout_delete = 1 * 500;


    var script$1x = {
      __name: 'page-nedge-repeaters-config',
      props: {
        url: String,
        ifid: Number,
        csrf: String
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const table_config = ref({});
    const table_rules = ref(null);
    const modal_add_repeater_config = ref(null);

    onBeforeMount(async () => {
        set_datatable_config();
    });

    function edit_repeater(repeater) {    
        const edit_url = `${http_prefix}/lua/pro/rest/v2/edit/nedge/forwarder.lua`;
        set_rule(repeater, edit_url);
    }

    function add_repeater(repeater) {
        const add_url = `${http_prefix}/lua/pro/rest/v2/add/nedge/forwarder.lua`;
        set_rule(repeater, add_url);
    }

    function set_rule(rule, url) {
        let headers = {
            'Content-Type': 'application/json'
        };
        let body = JSON.stringify({ ...rule, csrf: props.csrf});
        
        ntopng_utility$1.http_request(url, { method: "post", headers, body});
        refresh_table();    
    }

    const format_interfaces = function(data, rowData) {
        if (!data) data = ""; 
        return data.split(",").join(", ");
    };

    function set_datatable_config() {
        const datatableButton = [];
        
        let params = { 
    	ifid: ntopng_url_manager$1.get_url_entry("ifid") || props.ifid,	
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        
        datatableButton.push({
    	text: '<i class="fas fa-sync"></i>',
    	className: 'btn-link',
    	action: function (e, dt, node, config) {
    	    refresh_table();
                // table_rules.value.reload();
    	}
        }, {
    	text: '<i class="fas fa-plus"></i>',
    	className: 'btn-link',
    	action: function () {
    	    modal_add_repeater_config.value.show(null);
    	}
        });
        
        let defaultDatatableConfig = {
    	table_buttons: datatableButton,
    	data_url: `${props.url}?${url_params}`,
    	enable_search: false,
        };
        
        let columns = [
    	 { 
    	    columnName: _i18n("nedge.page_repeater_config.type"), targets: 0, name: 'type', data: 'type', className: 'text-nowrap text-left', responsivePriority: 1, render: function (data, _, rowData)  {
                    if (data == "custom") {
                        return _i18n("nedge.page_repeater_config.modal_repeater_config.custom");
                    } else {
                        return _i18n("nedge.page_repeater_config.modal_repeater_config.mdns");
                    }
                }
    	},
            { 
    	    columnName: _i18n("nedge.page_repeater_config.ip"), targets: 0, name: 'ip', data: 'ip', className: 'text-nowrap text-left', responsivePriority: 1
    	},
            { 
    	    columnName: _i18n("nedge.page_repeater_config.port"), targets: 0, name: 'port', data: 'port', className: 'text-nowrap text-left', responsivePriority: 1
    	},
            {
    	    columnName: _i18n("nedge.page_repeater_config.interfaces"), targets: 0, name: 'interfaces', data: 'details', className: 'text-nowrap text-left', responsivePriority: 1, render: function (data,_,rowData)  {
    		return format_interfaces(data)}
    	},
            {
    	    columnName: _i18n("nedge.page_repeater_config.restricted_interfaces"), targets: 0, name: 'restricted_interfaces', data: 'restricted_details', className: 'text-nowrap text-left', responsivePriority: 1, render: function (data,_,rowData)  {
    		return format_interfaces(data)}
    	},
        ];
        let wrap_columns_config = columns.map((c) => c);
        wrap_columns_config.push({ columnName: _i18n("actions"), width: '5%', name: 'actions', className: 'text-center', orderable: false, responsivePriority: 0, render: function (_, type, rowData) { return add_action_column(rowData) } });
        
        defaultDatatableConfig.columns_config = wrap_columns_config;
        table_config.value = defaultDatatableConfig;
    }

    const add_action_column = function (rowData) {
        let delete_handler = {
    	handlerId: "delete_host",	  
    	onClick: () => {
    	    delete_rule(rowData);
    	},
        };
        
        let edit_handler = {
    	handlerId: "edit_rule",
    	onClick: () => {
    	    modal_add_repeater_config.value.show(rowData);
    	},
        };
        
        return DataTableUtils.createActionButtons([
    	{ class: `pointer`, handler: edit_handler, handlerId: "edit_rule", icon: 'fa-edit', title: i18n('edit') },
    	{ class: `pointer`, handler: delete_handler, handlerId: "delete_rule", icon: 'fa-trash', title: i18n('delete') },
        ]);
    };

    function delete_rule(repeater) {
        const add_url = `${http_prefix}/lua/pro/rest/v2/delete/nedge/forwarder.lua`;
        let headers = {
            'Content-Type': 'application/json'
        };
        let body = JSON.stringify({ repeater_id: repeater.repeater_id, csrf: props.csrf});
        
        ntopng_utility$1.http_request(add_url, { method: "post", headers, body});
        refresh_table();    
    }

    function refresh_table() {
        setTimeout(() => {
    	ntopng_url_manager$1.reload_url();
     	// table_rules.value.reload();
        }, timeout_delete);
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$1o, [
          createBaseVNode("h2", null, toDisplayString(_i18n("nedge.repeaters_config_title")), 1 /* TEXT */),
          _cache[0] || (_cache[0] = createBaseVNode("br", null, null, -1 /* CACHED */))
        ]),
        createBaseVNode("div", _hoisted_2$1e, [
          createVNode(script$2e, {
            ref_key: "table_rules",
            ref: table_rules,
            table_buttons: table_config.value.table_buttons,
            columns_config: table_config.value.columns_config,
            data_url: table_config.value.data_url,
            filter_buttons: table_config.value.table_filters,
            enable_search: table_config.value.enable_search,
            table_config: table_config.value.table_config
          }, null, 8 /* PROPS */, ["table_buttons", "columns_config", "data_url", "filter_buttons", "enable_search", "table_config"])
        ]),
        createVNode(script$1y, {
          ref_key: "modal_add_repeater_config",
          ref: modal_add_repeater_config,
          onAdd: add_repeater,
          onEdit: edit_repeater
        }, null, 512 /* NEED_PATCH */)
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1x.__file = "http_src/vue/page-nedge-repeaters-config.vue";

    const _hoisted_1$1n = { class: "m-3" };
    const _hoisted_2$1d = { class: "m-4 card card-shadow" };
    const _hoisted_3$14 = { class: "card-body" };
    const _hoisted_4$11 = { class: "table-responsive" };
    const _hoisted_5$Q = { class: "table table-striped table-bordered" };
    const _hoisted_6$E = { class: "table_length" };
    const _hoisted_7$z = { class: "d-flex align-items-center" };
    const _hoisted_8$w = { class: "col-8" };
    const _hoisted_9$s = { class: "col-4" };
    const _hoisted_10$p = { class: "d-flex justify-content-end me-1" };


    var script$1w = {
      __name: 'page-flowdevice-config',
      props: {
      context: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const custom_name = ref(null);
    const prev_name = ref('');
    const disabled = ref(true);
    const props = __props;

    const get_flowdev_alias_url = `${http_prefix}/lua/pro/rest/v2/get/flowdevice/alias.lua?flowdev_ip=${get_ip_from_url()}&ifid=${props.context.ifid}`;
    const update_flowdev_alias_url = `${http_prefix}/lua/pro/rest/v2/set/flowdevice/alias.lua`;

    onMounted(async () => {
      getFlowDevAlias();
    });

    function get_ip_from_url() {
      return ntopng_url_manager.get_url_entry('ip')
    }

    async function getFlowDevAlias() {
      const rsp = await ntopng_utility.http_request(`${get_flowdev_alias_url}`, { method: 'get' });
      custom_name.value.value = rsp || props.ip;
      prev_name.value = custom_name.value.value;
    }

    const updateFlowDevAlias = async function () {
      const params = {
        csrf: props.context.csrf,
        ip: ntopng_url_manager.get_url_entry('ip'),
        alias: custom_name.value.value,
        ifid: props.context.ifid
      };
      let headers = {
        'Content-Type': 'application/json'
      };
      await ntopng_utility.http_request(update_flowdev_alias_url, { method: 'post', headers: headers, body: JSON.stringify(params) });
      disabled.value = true;
    };

    const checkDisabled = function () {
      if (prev_name.value == custom_name.value.value) {
        disabled.value = true;
      } else {
        disabled.value = false;
      }
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1n, [
        createBaseVNode("h5", null, toDisplayString(_i18n('modify_flowdev_alias')), 1 /* TEXT */),
        _cache[1] || (_cache[1] = createBaseVNode("hr", null, null, -1 /* CACHED */)),
        createBaseVNode("div", _hoisted_2$1d, [
          createBaseVNode("div", _hoisted_3$14, [
            createBaseVNode("div", _hoisted_4$11, [
              createBaseVNode("table", _hoisted_5$Q, [
                createBaseVNode("tbody", _hoisted_6$E, [
                  createBaseVNode("tr", null, [
                    createBaseVNode("td", null, [
                      createBaseVNode("div", _hoisted_7$z, [
                        createBaseVNode("div", _hoisted_8$w, [
                          createBaseVNode("b", null, toDisplayString(_i18n('flowdev_alias')), 1 /* TEXT */),
                          _cache[0] || (_cache[0] = createBaseVNode("br", null, null, -1 /* CACHED */))
                        ]),
                        createBaseVNode("div", _hoisted_9$s, [
                          createBaseVNode("input", {
                            type: "text",
                            ref_key: "custom_name",
                            ref: custom_name,
                            class: "form-control border",
                            onInput: checkDisabled
                          }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
                        ])
                      ])
                    ])
                  ])
                ])
              ])
            ]),
            createBaseVNode("div", _hoisted_10$p, [
              createBaseVNode("button", {
                class: normalizeClass(["btn btn-primary", [disabled.value ? 'disabled' : '']]),
                onClick: updateFlowDevAlias,
                id: "save"
              }, toDisplayString(_i18n("save_settings")), 3 /* TEXT, CLASS */)
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1w.__file = "http_src/vue/page-flowdevice-config.vue";

    const _hoisted_1$1m = { class: "table table-striped table-bordered col-sm-12" };
    const _hoisted_2$1c = { class: "col-3" };
    const _hoisted_3$13 = { class: "col-9" };
    const _hoisted_4$10 = { class: "d-flex ps-0" };
    const _hoisted_5$P = { class: "col-3" };
    const _hoisted_6$D = { class: "col-9" };
    const _hoisted_7$y = { class: "d-flex ps-0" };
    const _hoisted_8$v = { class: "d-flex align-items-center me-2" };
    const _hoisted_9$r = { class: "col-3" };
    const _hoisted_10$o = { class: "col-9" };
    const _hoisted_11$m = { class: "d-flex ps-0" };
    const _hoisted_12$j = { class: "d-flex align-items-center me-2" };

    const BIT_VALUE = 1000000000;

    var script$1v = {
      __name: 'page-flowdevice-interface-config',
      props: {
      ifid: String,
      csrf: String,
      device_ip: String,
      port_index: String,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const custom_name = ref(null);
    const prev_name = ref('');
    const uplink_speed = ref(null);
    const prev_uplink_speed = ref('');
    const downlink_speed = ref(null);
    const prev_downlink_speed = ref('');
    const disabled = ref(true);
    const props = __props;

    const get_interface_config_url = `${http_prefix}/lua/pro/rest/v2/get/flowdevice/interface/config.lua?device_ip=${props.device_ip}&port_index=${props.port_index}&ifid=${props.ifid}`;
    const update_interface_config_url = `${http_prefix}/lua/pro/rest/v2/set/flowdevice/interface/config.lua`;

    onMounted(async () => {
      getFlowDeviceInterfaceConfig();
    });

    async function getFlowDeviceInterfaceConfig() {
      const rsp = await ntopng_utility.http_request(`${get_interface_config_url}`, { method: 'get' });
      custom_name.value.value = rsp.alias;
      prev_name.value = custom_name.value.value;
      uplink_speed.value.value = rsp.uplink_speed / BIT_VALUE;
      prev_uplink_speed.value = uplink_speed.value.value;
      downlink_speed.value.value = rsp.downlink_speed / BIT_VALUE;
      prev_downlink_speed.value = downlink_speed.value.value;
    }

    const updateInterfaceConfig = async function () {
      const params = {
        ifid: props.ifid,
        csrf: props.csrf,
        device_ip: props.device_ip,
        port_index: props.port_index,
        alias: custom_name.value.value,
        uplink_speed: Number(uplink_speed.value.value) * BIT_VALUE,
        downlink_speed: Number(downlink_speed.value.value) * BIT_VALUE,
      };
      let headers = {
        'Content-Type': 'application/json'
      };
      await ntopng_utility.http_request(update_interface_config_url, { method: 'post', headers, body: JSON.stringify(params) });
      getFlowDeviceInterfaceConfig();
      disabled.value = true;
    };

    const checkDisabled = function () {
      if (prev_name.value == custom_name.value.value
        && prev_uplink_speed.value == uplink_speed.value.value
        && prev_downlink_speed.value == downlink_speed.value.value) {
        disabled.value = true;
      } else {
        disabled.value = false;
      }
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("table", _hoisted_1$1m, [
          createBaseVNode("tbody", null, [
            createBaseVNode("tr", null, [
              createBaseVNode("th", _hoisted_2$1c, toDisplayString(_i18n("interface_alias")), 1 /* TEXT */),
              createBaseVNode("td", _hoisted_3$13, [
                createBaseVNode("div", _hoisted_4$10, [
                  createBaseVNode("input", {
                    type: "text",
                    ref_key: "custom_name",
                    ref: custom_name,
                    class: "form-control",
                    style: {"width":"16rem"},
                    onInput: checkDisabled
                  }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
                ])
              ])
            ]),
            createBaseVNode("tr", null, [
              createBaseVNode("th", _hoisted_5$P, toDisplayString(_i18n("interface_uplink_speed")), 1 /* TEXT */),
              createBaseVNode("td", _hoisted_6$D, [
                createBaseVNode("div", _hoisted_7$y, [
                  createBaseVNode("label", _hoisted_8$v, toDisplayString(_i18n("gbit")), 1 /* TEXT */),
                  createBaseVNode("input", {
                    ref_key: "uplink_speed",
                    ref: uplink_speed,
                    class: "form-control text-end",
                    type: "number",
                    min: "0",
                    step: "0.01",
                    style: {"width":"8rem"},
                    onInput: checkDisabled
                  }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
                ])
              ])
            ]),
            createBaseVNode("tr", null, [
              createBaseVNode("th", _hoisted_9$r, toDisplayString(_i18n("interface_downlink_speed")), 1 /* TEXT */),
              createBaseVNode("td", _hoisted_10$o, [
                createBaseVNode("div", _hoisted_11$m, [
                  createBaseVNode("label", _hoisted_12$j, toDisplayString(_i18n("gbit")), 1 /* TEXT */),
                  createBaseVNode("input", {
                    ref_key: "downlink_speed",
                    ref: downlink_speed,
                    class: "form-control text-end",
                    type: "number",
                    min: "0",
                    step: "0.001",
                    style: {"width":"8rem"},
                    onInput: checkDisabled
                  }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
                ])
              ])
            ])
          ])
        ]),
        createBaseVNode("button", {
          class: normalizeClass(["btn btn-primary d-flex ms-auto", [disabled.value ? 'disabled' : '']]),
          onClick: updateInterfaceConfig,
          id: "save"
        }, toDisplayString(_i18n("save_settings")), 3 /* TEXT, CLASS */)
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1v.__file = "http_src/vue/page-flowdevice-interface-config.vue";

    var script$1u = {
      __name: 'modal-delete-scan-confirm',
      props: {
        body: String,
        title: String,
    },
      emits: ['delete','delete_all'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;

    const showed = () => {};
    const body = ref('');
    const title = ref('');
    const delete_type = ref('');
    const show_note_list = ref(true);
    const note_list = [
      i18n('note_scan_host')
    ];

    const show = (type, value) => {
      show_note_list.value = false;
      delete_type.value = type;

        if(type == "delete_all") {
          title.value = i18n("delete_all_entries");
          body.value = value;
        } else if(type == "delete_single_row") {
          title.value = i18n("delete_vs_host_title");
          body.value = value;
        } else if(type == "scan_all_rows") {
          title.value = i18n("scan_all_hosts_title");
          body.value = value;
        } else if(type == "scan_row") {
          show_note_list.value = true;
          title.value = i18n("scan_host_title");
          body.value = value;
        } else if (type == "delete_single_report") {
          title.value = i18n("hosts_stats.page_scan_hosts.reports_page.delete_title");
          body.value = i18n("hosts_stats.page_scan_hosts.reports_page.delete_description");
        }
        
        modal_id.value.show();
    };

    const delete_ = () => {
        if (delete_type.value == "delete_all") {
          emit('delete_all');
        } else if ( delete_type.value == "delete_single_row" || delete_type.value == "delete_single_report") {
          emit('delete');
        } else if (delete_type.value == "scan_all_rows") {
          emit('scan_all_rows');
        } else if (delete_type.value == "scan_row") {
          emit('scan_row');
        }
        close();
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[0] || (_cache[0] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(body.value) + " ", 1 /* TEXT */),
          (show_note_list.value)
            ? (openBlock(), createBlock(script$2u, {
                key: 0,
                note_list: note_list
              }))
            : createCommentVNode("v-if", true)
        ]),
        footer: withCtx(() => [
          (delete_type.value == 'delete_all' || delete_type.value == 'delete_single_row' || delete_type.value == 'delete_single_report')
            ? (openBlock(), createElementBlock("button", {
                key: 0,
                type: "button",
                onClick: delete_,
                class: "btn btn-danger"
              }, toDisplayString(_i18n('delete')), 1 /* TEXT */))
            : (openBlock(), createElementBlock("button", {
                key: 1,
                type: "button",
                onClick: delete_,
                class: "btn btn-primary"
              }, toDisplayString(_i18n('hosts_stats.page_scan_hosts.schedule_scan')), 1 /* TEXT */))
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1u.__file = "http_src/vue/modal-delete-scan-confirm.vue";

    const _hoisted_1$1l = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$1b = { class: "col-form-label col-sm-4" };
    const _hoisted_3$12 = { class: "col-8" };


    var script$1t = {
      __name: 'modal-update-periodicity-scan',
      props: {
            title: String,
        },
      emits: ['update',],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const automatic_scan_frequencies_list = ref([
      { id: "disabled", label:i18n('hosts_stats.page_scan_hosts.disabled')},
      { id: "1day", label:i18n('hosts_stats.page_scan_hosts.every_night')},
      { id: "1week", label:i18n('hosts_stats.page_scan_hosts.every_week')},
    ]);

    let selected_scan_frequency = ref(automatic_scan_frequencies_list.value[0]);
        
    const showed = () => {};

    //  const title = ref(i18n("hosts_stats.page_scan_hosts.delete_host_title"))

    const show = (row) => {
      modal_id.value.show();
    };

    const update_ = () => {
        emit('update', {scan_frequency: selected_scan_frequency.value.id});

        close();
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[1] || (_cache[1] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$1l, [
            createBaseVNode("label", _hoisted_2$1b, [
              createBaseVNode("b", null, toDisplayString(_i18n("hosts_stats.page_scan_hosts.automatic_scan")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$12, [
              createVNode(script$2H, {
                selected_option: unref(selected_scan_frequency),
                "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => (isRef(selected_scan_frequency) ? (selected_scan_frequency).value = $event : selected_scan_frequency = $event)),
                options: automatic_scan_frequencies_list.value
              }, null, 8 /* PROPS */, ["selected_option", "options"])
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: update_,
            class: "btn btn-secondary me-4"
          }, toDisplayString(_i18n('hosts_stats.page_scan_hosts.update_all')), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1t.__file = "http_src/vue/modal-update-periodicity-scan.vue";

    const _hoisted_1$1k = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$1a = { class: "col-form-label col-sm-2" };
    const _hoisted_3$11 = { class: "col-sm-4 pe-0" };
    const _hoisted_4$$ = ["disabled", "placeholder"];
    const _hoisted_5$O = { class: "col-2 ps-0" };
    const _hoisted_6$C = ["disabled", "placeholder"];
    const _hoisted_7$x = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_8$u = { class: "col-form-label col-sm-2" };
    const _hoisted_9$q = { class: "col-sm-10" };
    const _hoisted_10$n = ["placeholder"];
    const _hoisted_11$l = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_12$i = { class: "col-form-label col-sm-2" };
    const _hoisted_13$h = { class: "col-10" };
    const _hoisted_14$f = {
      key: 0,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_15$e = { class: "col-form-label col-sm-2" };
    const _hoisted_16$e = { class: "col-10" };
    const _hoisted_17$e = {
      key: 1,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_18$b = { class: "col-form-label col-sm-2" };
    const _hoisted_19$a = { class: "col-10" };
    const _hoisted_20$a = { class: "mt-4" };
    const _hoisted_21$a = {
      key: 0,
      class: "me-auto text-danger d-inline"
    };
    const _hoisted_22$a = ["disabled"];
    const _hoisted_23$a = ["disabled"];

    /* ****************************************************** */

    const CIDR_24$1 = 24;
    const CIDR_30 = 30;
    const CIDR_32$1 = 32;
    const CIDR_128$1 = 128;


    var script$1s = {
      __name: 'modal-add-vs-host',
      props: {
      context: Object,
    },
      emits: ["add", "edit"],
      setup(__props, { expose: __expose, emit: __emit }) {

    /* Imports */
    const _i18n = (t) => i18n(t);
    const emit = __emit;

    /* Consts */
    const title = ref(i18n("hosts_stats.page_scan_hosts.add_host"));
    const no_host_feedback = ref(i18n("hosts_stats.page_scan_hosts.host_not_resolved"));
    const host_placeholder = i18n("hosts_stats.page_scan_hosts.host_placeholder");
    const cidr_placeholder = i18n("hosts_stats.page_scan_hosts.cidr_placeholder");
    const ports_placeholder = i18n("hosts_stats.page_scan_hosts.ports_placeholder");
    const note_list = [
      _i18n("hosts_stats.page_scan_hosts.notes.note_1"),
      _i18n("hosts_stats.page_scan_hosts.notes.note_2"),
      _i18n("hosts_stats.page_scan_hosts.notes.note_3"),
      _i18n("hosts_stats.page_scan_hosts.notes.note_3_1"),
    ];
    const enterprise_note_list = [
      _i18n("hosts_stats.page_scan_hosts.notes.note_1"),
      _i18n("hosts_stats.page_scan_hosts.notes.note_2"),
      _i18n("hosts_stats.page_scan_hosts.notes.note_3"),
      _i18n("hosts_stats.page_scan_hosts.notes.note_3_1"),
      _i18n("hosts_stats.page_scan_hosts.notes.note_4"),
    ];

    const modal_id = ref(null);
    const selected_scan_type = ref({});
    const selected_discovered_scan_types = ref([]); // array for hosts discovered scan types
    const hide_ports_placeholder = ref("");
    const row_to_edit_id = ref("");
    const activate_add_spinner = ref(false);
    const is_edit_page = ref(false);
    const scan_type_list = ref([]);
    const discovered_scan_type_list = ref([]);
    const ifid = ref(null);
    const host = ref(null);
    const ports = ref(null);
    const show_port_feedback = ref(false);
    const is_enterprise_l = ref(null);
    const isPortCorrect = ref(true);
    const isCIDRValid = ref(false);
    const isNetscanValid = ref(true); // bool to be sure that on netscan at least one discovered hosts scan type is selected
    const isHostValid = ref(false);
    const is_ipv4_netscan = ref(false);
    const scan_frequencies_list = ref([
      { id: "disabled", label: i18n("hosts_stats.page_scan_hosts.disabled") },
      { id: "1day", label: i18n("hosts_stats.page_scan_hosts.every_night") },
      { id: "1week", label: i18n("hosts_stats.page_scan_hosts.every_week") },
    ]);

    const selected_cidr = ref(null);
    const selected_scan_frequency = ref(scan_frequencies_list.value[0]);
    const is_data_not_ok = ref(false);
    const refresh_select_search = ref(false);
    /* ****************************************************** */

    /*
     * Reset fields in modal form
     */
    const reset_modal_form = function () {
      host.value = "";
      ports.value = "";
      selected_cidr.value = "";
      isPortCorrect.value = true;
      isCIDRValid.value = false;
      isHostValid.value = false;
      activate_add_spinner.value = false;
      show_port_feedback.value = false;
      selected_scan_type.value = scan_type_list.value[0];
      selected_discovered_scan_types.value = [];
      row_to_edit_id.value = null;
      is_edit_page.value = false;
      is_data_not_ok.value = false;
      is_ipv4_netscan.value = false;
      isNetscanValid.value = !is_ipv4_netscan.value;
    };

    /* ****************************************************** */

    /*
     * Set row to edit
     */
    const set_row_to_edit = (row) => {
      is_edit_page.value = true;

      /* Set host values */
      host.value = row.host;
      ports.value = row.ports;
      isHostValid.value = true;
      isPortCorrect.value = true;
      row_to_edit_id.value = row.id;

      /* Set the correct values if available */
      /* Scan Type */
      selected_scan_type.value = scan_type_list.value.find(
        (item) => item.id == row.scan_type
      );

      /* Sub Scans Types */
      if (row.discovered_host_scan_type != null) {
        const discovered_scan_type_ids_array = row.discovered_host_scan_type.split(",");
        let tmp_selected_scan_types = [];
        let tmp_found_scan_type;
        discovered_scan_type_ids_array.forEach((setted_scan_type) => {
          tmp_found_scan_type = discovered_scan_type_list.value.find((item) => item.id == setted_scan_type);
          tmp_selected_scan_types.push(tmp_found_scan_type);
        });
        selected_discovered_scan_types.value = tmp_selected_scan_types;
      }

      /* CIDR */
      if (check_is_network_address(host.value) || row.cidr == CIDR_128$1) {
        selected_cidr.value = row.cidr;
      } else {
        selected_cidr.value = CIDR_32$1;
      }

      isCIDRValid.value = true;

      /* Scan Frequency */
      if (is_enterprise_l) {
        selected_scan_frequency.value = scan_frequencies_list.value.find(
          (item) => item.id == row.scan_frequency
        );
      }
    };

    /* ****************************************************** */

    /* This method is called whenever the modal is opened */
    const show = (row, _host) => {
      /* First of all reset all the data */
      reset_modal_form();
      title.value = i18n("hosts_stats.page_scan_hosts.add_host");
      if (!dataUtils.isEmptyOrNull(row)) {
        /* In case row is not null then an edit is requested */
        title.value = i18n("hosts_stats.page_scan_hosts.edit_host_title");
        set_row_to_edit(row);
      }

      if (!dataUtils.isEmptyOrNull(_host)) {
        host.value = _host;
        isHostValid.value = true;
      }

      modal_id.value.show();
    };

    /* ****************************************************** */

    /* Function called when a new selected discovered scan type
       is added
    */
    const update_selected_discovered_scan_types = (items) => {
      selected_discovered_scan_types.value = items;
      isNetscanValid.value = selected_discovered_scan_types.value.length > 0;
    };

    /* Function called when is removed a selected discovered scan type
    */
    const remove_selected_discovered_scan_types = (item_to_delete) => {
      selected_discovered_scan_types.value = selected_discovered_scan_types.value.filter((item) => item.id != item_to_delete.id);
      isNetscanValid.value = selected_discovered_scan_types.value.length > 0; 
    };

    /* ****************************************************** */

    /* Function to set is_ipv4_netscan in order to disable cidr selectio
       only /24 (for now)
    */
    const set_is_ipv4_netscan = () => {
      if (selected_scan_type.value.id == 'ipv4_netscan') {
        // /24 
        selected_cidr.value = CIDR_24$1;
        // is_ipv4_netscan -> enable the discovered_hosts_scan_types multiselection
        is_ipv4_netscan.value = true;
        // isNetscanValid -> disabled apply or add button because is necessary at least one discovered_hosts_scan_type
        isNetscanValid.value = false;
      } else {
        // is_ipv4_netscan -> disable the discovered_hosts_scan_types multiselection
        is_ipv4_netscan.value = false;
        // isNetscanValid -> enable the add or apply button because is not ipv4_netscan case
        isNetscanValid.value = true;
      }

    };

    /* ****************************************************** */
    const check_is_network_address = (address) => {
      const addr_parts = address.split(".");
      if (addr_parts.length > 3) {
        return addr_parts[3] == 0;
      }
    };

    /* ****************************************************** */

    /* Regex to check if the host is correct or not */
    const check_host_regex = () => {
      const is_ipv4 = regexValidation.validateIPv4(host.value);
      const is_ipv6 = regexValidation.validateIPv6(host.value);
      const is_host_name = regexValidation.validateHostName(host.value);
      if (selected_scan_type.value.id == 'ipv4_netscan') {
        
        if (is_ipv4) {
          // the IP must be an IPv4
          /* IPv4 */
          isHostValid.value = true;
          // the selected_discovered_scan_types must be an array with lenght more than 0
          isNetscanValid.value = selected_discovered_scan_types.value && selected_discovered_scan_types.value.length > 0;
        }

        isNetscanValid.value = true; // not ipv4_netscan case so isNetscanValid is true
      } else {

        /* When it isn't the ipv4_netscan case the cidr selection is enabled */

        if (is_ipv4) {
          /* IPv4 */
          if (!check_is_network_address(host.value)) {
            /* In case the CIDR is wrong */
            selected_cidr.value = CIDR_32$1;
          } else {
            selected_cidr.value = CIDR_24$1;
          }
          isHostValid.value = true;
          isCIDRValid.value = true;
        } else if (is_ipv6) {
          /* IPv6 */
          isHostValid.value = true;
          isCIDRValid.value = true;
          /* In case the CIDR is wrong */
          selected_cidr.value = CIDR_128$1;
          /* IPv6 */

        } else if (is_host_name) {
          /* Host Name */
          isHostValid.value = true;
          isCIDRValid.value = true;
          /* In case the CIDR is wrong */
          selected_cidr.value = CIDR_32$1;
          
        } else {
          isHostValid.value = false;
        }
      }
    };

    const check_cidr = () => {
      if (( selected_cidr.value >= CIDR_24$1 && selected_cidr.value <= CIDR_30) || 
            selected_cidr.value == CIDR_32$1 || selected_cidr.value == CIDR_128$1) {
        isCIDRValid.value = true;
        return true;
      } 
      isCIDRValid.value = false;
      return false;
    };

    /* ****************************************************** */

    /* Regex to check if ports list is correct or not */
    const check_ports = () => {
      if (
        !regexValidation.validatePortRange(ports.value) &&
        !regexValidation.validateCommaSeparatedPortList(ports.value) &&
        !dataUtils.isEmptyOrNull(ports.value)
      ) {
        isPortCorrect.value = false;
      } else {
        /* Empty port is alright! */
        isPortCorrect.value = true;
      }
    };

    /* ****************************************************** */

    /* Resolve hostname */
    async function resolve_host_name(host) {
      const resolve_host_name_url = `${http_prefix}/lua/rest/v2/get/host/resolve_host_name.lua`;
      const url = NtopUtils$1.buildURL(resolve_host_name_url, {
        host: host,
      });

      return await ntopng_utility.http_request(url);
    }

    /* ****************************************************** */

    /* Function called when the edit button is clicked */
    const edit_ = () => {
      add_(true);
    };

    /* ****************************************************** */

    /* Function called when the modal is closed */
    const close = () => {
      refresh_select_search.value = false;
      modal_id.value.close();
    };

    /* ****************************************************** */

    /* Function to add host to scan */
    const add_ = async (is_edit) => {
      const host_ports = ports.value;
      const host_scan_type = selected_scan_type.value.id;
      const emit_event = (is_edit === true) ? "edit" : "add";
      const row_id = (is_edit === true) ? row_to_edit_id.value : null;
      let new_host = host.value;
      let new_host_name_resolved = true;

      /* Activate the spinner to give the user a feedback */
      activate_add_spinner.value = true;

      /* Check if it's an IP or not, if not it means it's an hostname */
      if (!regexValidation.validateIP(host.value)) {
        /* During the validation disable the add button */
        isHostValid.value = false;
        new_host = await resolve_host_name(host.value);
        if (new_host === "no_success") {
          /* The resolution failed! */
          new_host_name_resolved = false;
          no_host_feedback.value = host.value + " " + i18n("hosts_stats.page_scan_hosts.host_not_resolved");
          is_data_not_ok.value = true;
          /* Hide the message after 3 seconds */
          setTimeout(() => {
            is_data_not_ok.value = false;
          }, 4000);
        }
        /* Validation ended, re-enable the button */
        isHostValid.value = true;
      }

      let tmp_second_scan_types = [];
      
      selected_discovered_scan_types.value.forEach((item) => {
        tmp_second_scan_types.push(item.id);
      });
      /* The discovered scan types are sent to the rest in comma separated string list */
      const tmp_second_scan_types_formatted = tmp_second_scan_types.join(",");

      /* If the resolution was ok or no resolution at all was done emit the event */
      activate_add_spinner.value = new_host_name_resolved;

      if (new_host_name_resolved) {
        /* Emit the event, only if the resolution 
        was ok or no resolution at all was needed */
        emit(emit_event, {
          host: new_host,
          scan_type: host_scan_type,
          scan_ports: host_ports,
          vs_cidr: selected_cidr.value,
          scan_frequency: is_enterprise_l ? selected_scan_frequency.value.id : null,
          scan_id: row_id,
          discovered_host_scan_type : tmp_second_scan_types_formatted
        });
      }

    };

    /* ****************************************************** */

    /* Load the available metrics */
    const metricsLoaded = async (_scan_type_list, _ifid, _is_enterprise_l) => {
      const scan_types = _scan_type_list.sort((a, b) =>
        a.label.localeCompare(b.label)
      );
      ifid.value = _ifid;
      scan_type_list.value = scan_types;
      discovered_scan_type_list.value = scan_types.filter((item) => (item.id != 'ipv4_netscan'));
      is_enterprise_l.value = _is_enterprise_l;
      selected_scan_type.value = scan_type_list.value[0];
    };

    /* ****************************************************** */
    /* ****************************************************** */

    /* Function called whenever the CIDR changes,
     * in case of a network the port is not needed
     */
    /* 

    <div class="form-group ms-2 me-2 mt-3 row">
      <div class="col-2"></div>
      <div class="col-10 d-flex align-items-center">
        <!--
          HIDDEN BUTTON FOR NOW
          <button
          type="button"
          @click="load_ports"
          :disabled="!isHostValid || disable_load_ports"
          class="btn btn-primary"
        >
          {{ _i18n("hosts_stats.page_scan_hosts.load_ports") }}
        </button>
        -->
        <dd v-if="show_port_feedback" class="ms-2 mb-0 text-danger">
          {{ port_feedback }}
        </dd>
        <a class="ntopng-truncate"></a>
      </div>
    </div>

     -- Hidden function for now, it's not needed --

    const port_feedback = i18n("hosts_stats.page_scan_hosts.no_ports_detected");
    const server_ports = `${http_prefix}/lua/rest/v2/get/host/open_ports.lua`;
    const disable_load_ports = ref(false);

    function disable_ports() {
      if (selected_cidr.value.id != cidr_24) {
        disable_load_ports.value = false;
      } else {
        disable_load_ports.value = true;
      }
    }

    async function load_ports() {
      activate_spinner.value = true;
      /* In case the host is not empty, hide the placeholder */
    /*  if (dataUtils.isEmptyOrNull(host.value)) {
        hide_ports_placeholder.value = true;
      } else {
        hide_ports_placeholder.value = false;
      }

      /* Request for the available ports */
    /*  const url = NtopUtils.buildURL(server_ports, {
        host: host.value,
        ifid: ifid.value,
        clisrv: "server",
      });

      const result = await ntopng_utility.http_request(url);

      /* Show the results or empty if no data was found */
    /*  if (!dataUtils.isEmptyOrNull(result)) {
        ports.value = result.map((x) => x.key).join(",");
        show_port_feedback.value = false;
      } else {
        show_port_feedback.value = true;
        ports.value = "";
        /* Remove the message after 5 seconds! */
    /*    setTimeout(() => {
          show_port_feedback.value = false;
        }, 5000);
      }
      activate_spinner.value = false;
    }
    */


    __expose({ show, close, metricsLoaded });

    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Target information, here an IP is put "),
          createBaseVNode("div", _hoisted_1$1k, [
            createBaseVNode("label", _hoisted_2$1a, [
              createBaseVNode("b", null, toDisplayString(_i18n("hosts_stats.page_scan_hosts.host_or_network")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$11, [
              withDirectives(createBaseVNode("input", {
                "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((host).value = $event)),
                onInput: check_host_regex,
                disabled: is_edit_page.value,
                class: "form-control",
                type: "text",
                placeholder: unref(host_placeholder),
                required: ""
              }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_4$$), [
                [vModelText, host.value]
              ])
            ]),
            _cache[7] || (_cache[7] = createBaseVNode("div", { class: "col-1 ps-5 pe-0 mt-1" }, [
              createBaseVNode("span", null, "/")
            ], -1 /* CACHED */)),
            createBaseVNode("div", _hoisted_5$O, [
              withDirectives(createBaseVNode("input", {
                "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((selected_cidr).value = $event)),
                onInput: check_cidr,
                disabled: is_edit_page.value,
                class: "form-control",
                type: "text",
                placeholder: unref(cidr_placeholder),
                required: ""
              }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_6$C), [
                [vModelText, selected_cidr.value]
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_7$x, [
            createBaseVNode("label", _hoisted_8$u, [
              createBaseVNode("b", null, toDisplayString(_i18n("hosts_stats.page_scan_hosts.ports")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_9$q, [
              withDirectives(createBaseVNode("input", {
                "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((ports).value = $event)),
                onFocusout: check_ports,
                class: normalizeClass(["form-control", hide_ports_placeholder.value === true ? 'ntopng-hide-placeholder' : ''
                ]),
                type: "text",
                placeholder: unref(ports_placeholder),
                required: ""
              }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_10$n), [
                [vModelText, ports.value]
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_11$l, [
            createBaseVNode("label", _hoisted_12$i, [
              createBaseVNode("b", null, toDisplayString(_i18n("hosts_stats.page_scan_hosts.scan_type")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_13$h, [
              createVNode(script$2H, {
                selected_option: selected_scan_type.value,
                "onUpdate:selected_option": _cache[3] || (_cache[3] = $event => ((selected_scan_type).value = $event)),
                options: scan_type_list.value,
                disabled: is_edit_page.value,
                onSelect_option: _cache[4] || (_cache[4] = $event => (set_is_ipv4_netscan()))
              }, null, 8 /* PROPS */, ["selected_option", "options", "disabled"])
            ])
          ]),
          (selected_scan_type.value.id == 'ipv4_netscan')
            ? (openBlock(), createElementBlock("div", _hoisted_14$f, [
                createBaseVNode("label", _hoisted_15$e, [
                  createBaseVNode("b", null, toDisplayString(_i18n("hosts_stats.page_scan_hosts.host_discovered_scan_type")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_16$e, [
                  createVNode(script$2H, {
                    selected_options: selected_discovered_scan_types.value,
                    "onUpdate:selected_options": _cache[5] || (_cache[5] = $event => ((selected_discovered_scan_types).value = $event)),
                    onChange_selected_options: update_selected_discovered_scan_types,
                    onUnselect_option: remove_selected_discovered_scan_types,
                    options: discovered_scan_type_list.value,
                    multiple: true
                  }, null, 8 /* PROPS */, ["selected_options", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          (is_enterprise_l.value == true)
            ? (openBlock(), createElementBlock("div", _hoisted_17$e, [
                createBaseVNode("label", _hoisted_18$b, [
                  createBaseVNode("b", null, toDisplayString(_i18n("hosts_stats.page_scan_hosts.periodicity")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_19$a, [
                  createVNode(script$2H, {
                    selected_option: selected_scan_frequency.value,
                    "onUpdate:selected_option": _cache[6] || (_cache[6] = $event => ((selected_scan_frequency).value = $event)),
                    options: scan_frequencies_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", _hoisted_20$a, [
            (is_enterprise_l.value == false)
              ? (openBlock(), createBlock(script$2u, {
                  key: 0,
                  note_list: note_list
                }))
              : (openBlock(), createBlock(script$2u, {
                  key: 1,
                  note_list: enterprise_note_list
                }))
          ])
        ]),
        footer: withCtx(() => [
          (is_data_not_ok.value)
            ? (openBlock(), createElementBlock("div", _hoisted_21$a, toDisplayString(no_host_feedback.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createVNode(script$2v, {
              show: activate_add_spinner.value,
              size: "1rem",
              class: "me-2"
            }, null, 8 /* PROPS */, ["show"]),
            (is_edit_page.value == false)
              ? (openBlock(), createElementBlock("button", {
                  key: 0,
                  type: "button",
                  onClick: add_,
                  class: "btn btn-primary",
                  disabled: !(isCIDRValid.value && isHostValid.value && isPortCorrect.value && isNetscanValid.value)
                }, toDisplayString(_i18n("add")), 9 /* TEXT, PROPS */, _hoisted_22$a))
              : (openBlock(), createElementBlock("button", {
                  key: 1,
                  type: "button",
                  onClick: edit_,
                  class: "btn btn-primary",
                  disabled: !(isCIDRValid.value && isHostValid.value && isPortCorrect.value && isNetscanValid.value)
                }, toDisplayString(_i18n("apply")), 9 /* TEXT, PROPS */, _hoisted_23$a))
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1s.__file = "http_src/vue/modal-add-vs-host.vue";

    const columns_formatter = (columns, scan_type_list, is_report, ifid) => {
      const visible_dict = {
            download: true,
            show_result: true
          };

      let map_columns = {
        "host": (host, row) => {
          return host_f(host,row);
        },
        "scan_type": (scan_type, row) => {
          return scan_type_f(scan_type, row, scan_type_list);
        },
        "last_scan": (last_scan, row) => {
          return last_scan_f(last_scan, row);
        },

        "duration": (last_scan, row) => {
          return duration_f(last_scan, row);
        },
        "scan_frequency" : (scan_frequency) => {
          return scan_frequency_f(scan_frequency);
        }, 
        "is_ok_last_scan": (is_ok_last_scan) => {
          return is_ok_last_scan_f(is_ok_last_scan);
          
        },
        "max_score_cve": (max_score_cve, row) => {
          return max_score_cve_f(max_score_cve, row);
        },
        "tcp_ports": (tcp_ports, row) => {
          return tcp_ports_f(tcp_ports, row);
          
        },
        "udp_ports": (udp_ports, row) => {
          return udp_ports_f(udp_ports, row);
        },
        "num_vulnerabilities_found": (num_vs, row) => {
          return num_vuln_found_f(num_vs,row);

        }
      };

      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];

        if (c.id == "actions") {
                
          c.button_def_array.forEach((b) => {
              
            b.f_map_class = (current_class, row) => { 
              current_class = current_class.filter((class_item) => class_item != "disabled");
              // FIX ME with UDP ports check
              if((row.is_ok_last_scan == 3 || row.is_ok_last_scan == null || (row.last_scan == null || ((row.last_scan != null && row.last_scan.time == null))) ) && visible_dict[b.id]) {
                current_class.push("disabled"); 
              }
              return current_class;
            };
          });
        }
      });
      
      return columns;
    };

    const num_vuln_found_f = (num_vuln_found, row) => {
      if (row.is_ok_last_scan == 1 && (row.last_scan != null && row.last_scan.time != null)) {
        return num_vuln_found;
      }
      return "";

    };
    const max_score_cve_f = (max_score_cve, row) => {
      let label = "";

      if (row.is_ok_last_scan == 1  && (row.last_scan != null && row.last_scan.time != null)) {
        const score = Number(max_score_cve);
        let font_color = "";
      
        if (max_score_cve != null) {
      
          if (score == 0) {
            font_color = "green";
          } else if(score < 3.9) {
            font_color = "grey";
          } else if(score < 7) {
            font_color = "yellow";
          } else  {
            font_color = "red";
          } 
      
          if (score != 0) {
            label = `<FONT COLOR=${font_color}>${max_score_cve}`;
          }
        }

      }
      


      return label;
    };


    const scan_type_f = (scan_type, from_report, row) => {
      if (from_report && row.is_down) {
        return '';
      }
      if (scan_type !== undefined) {
        let label = scan_type;
        const i18n_name = "hosts_stats.page_scan_hosts.scan_type_list."+scan_type;
        label = i18n(i18n_name);
        return label;
      }
    };

    const last_scan_f = (last_scan, row) => {
      if (row.is_ok_last_scan == 3) {
        return ``;
      }
      if (last_scan !== undefined && last_scan.time !== undefined) {
        return last_scan.time;
      } else if (last_scan !== undefined) {
        return last_scan;
      } else {
        return i18n("hosts_stats.page_scan_hosts.not_yet");
      }
    }; 

    const duration_f = (last_scan, row) => {
      if (row.is_ok_last_scan == 3 ) {
        return ``;
      }
      if (row.last_scan !== undefined && row.last_scan.duration !== undefined) {
        return row.last_scan.duration;
      } else {
        return i18n("hosts_stats.page_scan_hosts.not_yet");
      }
    };

    const scan_frequency_f = (scan_frequency) => {
      let label = "";
      if (scan_frequency == null || scan_frequency == "disabled") {
        return "";
      } else if (scan_frequency == "1day") {
        label =  i18n("hosts_stats.page_scan_hosts.daily");
      } else {
        label =  i18n("hosts_stats.page_scan_hosts.weekly");
      }
      return `<span class="badge bg-secondary" title="${label}">${label}</span>`;
    };

    const is_ok_last_scan_f = (is_ok_last_scan) => {
      let label = "";
      if (is_ok_last_scan == 2) {
        // scheduled
        label = i18n("hosts_stats.page_scan_hosts.scheduled");
        return `<span class="badge bg-dark" title="${label}">${label}</span>`;
      } else if (is_ok_last_scan == 4) {
        // not scanned
        label = i18n("hosts_stats.page_scan_hosts.scanning");
        return `<span class="badge bg-info" title="${label}">${label}</span>`; 
      } else if (is_ok_last_scan == 3 || is_ok_last_scan == null) {
        // not scanned
        label = i18n("hosts_stats.page_scan_hosts.not_scanned");
        return `<span class="badge bg-primary" title="${label}">${label}</span>`;
      } else if (is_ok_last_scan == 1) {
        // success
        label = i18n("hosts_stats.page_scan_hosts.success");
        return `<span class="badge bg-success" title="${label}">${label}</span>`;
      } else if (is_ok_last_scan == 0) {
        // error
        label = i18n("hosts_stats.page_scan_hosts.error");
        return `<span class="badge bg-danger" title="${label}">${label}</span>`;
      } else if (is_ok_last_scan == 5) {
        // warning -> failed
        label = i18n("hosts_stats.page_scan_hosts.failed");
        return `<span class="badge bg-warning" title="${label}">${label}</span>`;
      } 
    };

    const ports_list_string = (port_list) => {
      let ports_string = "";
      if (port_list != null) {
        port_list.forEach((item) => {
          if(ports_string == "") {
            ports_string = item;
          } else {
            ports_string += `, ${item}`;
          }
        });
      }

      return ports_string;
    };

    const get_num_open_ports_icon = (diff_case, unused_port_list, filtered_port_list) => {

      let label = null;
      switch(diff_case) {
        case 4: {
          let unused_port_list_string = ports_list_string(unused_port_list);
          label = ` <span class="badge bg-secondary"><i class="fa-solid fa-ghost" title='${unused_port_list_string}'></i></span></div>`;
        }
          break;
        case 3: {
          let filtered_ports_list_string = ports_list_string(filtered_port_list);
          label = ` <span class="badge bg-secondary"><i class="fa-solid fa-filter" title='${filtered_ports_list_string}'></i></span>`;
        }
          break;
      }

      return label;
    };

    const udp_ports_f = (udp_ports, row) => {
      if (udp_ports == 0 && row.udp_ports == 0 && row.scan_type.contains("udp")) {
        udp_ports = row.num_open_ports;
      }
      let label = "";

      if (udp_ports == null || udp_ports <= 0) {
        return label;
      }

      if (row.is_ok_last_scan == 1 && (row.last_scan != null && row.last_scan.time != null)) {
      
        label = `${udp_ports}`;

        if (row.host_in_mem) {

          const num_ports_icon = get_num_open_ports_icon(row.udp_ports_case,row.udp_ports_unused, row.udp_filtered_ports);
          if(num_ports_icon != null) {
            label += num_ports_icon;
          }
        }
      }

      return label;
    };


    const tcp_ports_f = (tcp_ports, row) => {
      if (tcp_ports == 0 && row.tcp_ports == 0 && row.scan_type.contains("tcp")) {
        tcp_ports = row.num_open_ports;
      }
      let label = "";

      if (tcp_ports == null || tcp_ports <= 0) {
        return label;
      }

      if (row.is_ok_last_scan == 1 && (row.last_scan != null && row.last_scan.time != null)) {

      
        label = `${tcp_ports}`;

        if (row.host_in_mem) {

          const num_ports_icon = get_num_open_ports_icon(row.tcp_ports_case,row.tcp_ports_unused, row.tcp_ports_filtered);
          if(num_ports_icon != null) {
            label += num_ports_icon;
          }
        }
      }

      return label;
    };

    const tcp_port_f = (port, row) => {
      let rsp = port;
      if (row.port_label != null && row.port_label != port) {
        rsp += ` (${row.port_label})`;
      }
      return rsp;
    };
    const find_badge = (port, row, ports_unused, ports_filtered) => {
      let result = '';
      if (ports_unused != null) {
        ports_unused.forEach((item) => {
          if(port == Number(item) ) {
            result = "unused";
          }
        });
      }

      if(result != '') {
        return result;
      }

      if (ports_filtered != null) {
        ports_filtered.forEach((item) => {
          if(port == Number(item)) {
            result = "filtered";
          }
        });
      }

      return result;
    };

    const get_icon_component = (item, row, ports_unused, ports_fitered) => {
      let port = item.split(" ")[0].split("/")[0];
      let port_badge = find_badge(Number(port), row, ports_unused, ports_fitered);
      let icon_comp = null;
      switch (port_badge) {
        case 'unused': 
            icon_comp = ` &nbsp;<span class="badge bg-secondary" title='${i18n('hosts_stats.page_scan_hosts.unused_port')}'><i class="fa-solid fa-ghost"></i></span>`;
          break;
        case 'filtered':
            icon_comp = ` &nbsp;<span class="badge bg-primary" title='${i18n('hosts_stats.page_scan_hosts.filtered_port')}'><i class="fa-solid fa-filter"></i></span>`;
          break;
      }

      return icon_comp;
    };

    const tcp_udp_ports_list_f = (tcp_ports_list,udp_ports_list, row) => {
      let ports_map = new Map();

      if (row.is_ok_last_scan == 1 && (row.last_scan != null && row.last_scan.time != null) && tcp_ports_list != null ) {
        const ports = tcp_ports_list.split(",");
        let label = "";
        let port_id = "";
        ports.forEach((item) => {
          if(item != null && item != '') {

            label = item;
            port_id = item;
            if (row.host_in_mem) {
              const icon_comp = get_icon_component(item, row, row.tcp_ports_unused, row.tcp_ports_filtered);
              if(icon_comp != null) {
                label += icon_comp;
              }
            }
            
            label = `<li>${label}</li>`;

            ports_map.set(item,  {port_label :label, port_id: Number(port_id.split("/")[0])});
          }
        });

        if (row.tcp_ports_filtered != null) {
          row.tcp_ports_filtered.forEach((item) => {

            item += `/tcp`;
            label = item;
            port_id = item;
            if (row.host_in_mem) {
              label += ` <span class="badge bg-primary" title='${i18n('hosts_stats.page_scan_hosts.filtered_port')}'><i class="fa-solid fa-filter"></i></span>`;
            }
            label = `<li>${label}</li>`;
            ports_map.set(item,  {port_label :label, port_id: Number(port_id.split("/")[0])});


          });
        }
        
      } 


      if (row.is_ok_last_scan == 1 && (row.last_scan != null && row.last_scan.time != null) && udp_ports_list != null) {
        const ports = udp_ports_list.split(",");

        let label = "";
        let port_id = "";
        ports.forEach((item) => {
          if(item != null && item != '') {

            label = item;
            port_id = item;
            if (row.host_in_mem) {
              const icon_comp = get_icon_component(item, row, row.udp_ports_unused, row.udp_ports_filtered);
              if(icon_comp != null) {
                label += icon_comp;
              }        
            }
            label = `<li>${label}</li>`;

            ports_map.set(item, {port_label :label, port_id: Number(port_id.split("/")[0])});
          }
        });

        if (row.udp_filtered_ports != null) {
          row.udp_ports_filtered.forEach((item) => {

            item += `/udp`;
            label = item;
            port_id = item;
            if (row.host_in_mem) {
              label += ` <span class="badge bg-primary" title='${i18n('hosts_stats.page_scan_hosts.filtered_port')}'><i class="fa-solid fa-filter"></i></span>`;
            }
            label = `<li>${item}</li>`;
            ports_map.set(item, {port_label: label, port_id : Number(port_id.split("/")[0])});
          });
        }
      }

      let content_label = "";
      ports_map = new Map([...ports_map.entries()].sort((a,b) => a[1].port_id-b[1].port_id));

      ports_map.forEach((values, keys) => {
        content_label += `${values.port_label}`;
      });


      return content_label;
    };

    const discoverd_hosts_list_f = (hosts_string) => {
      const hosts_list = hosts_string.split(",");
      let label = "";
      hosts_list.forEach((item) => {
        if (item != "")
          label += `<li>${item}</li>`;
      });
      return label;
    };
    const hosts_f = (hosts, row) => {

      const hosts_list = hosts.split(", ");
      let label = "";
      let hosts_map = new Map();
      hosts_list.forEach((item) => {
        let host_info = item.split("|");

        hosts_map.set(
          host_info.length > 5 && host_info[5] != null && host_info[5] != "" ? host_info[5] : host_info[0], 
          {
            scan_type: host_info[1],
            ip: host_info[0],
            date: host_info[2].replace(" ","_"),
            is_ipv4: host_info[3] == 'true',
            epoch: host_info[4]
          });
      });

      hosts_map = new Map([...hosts_map.entries()].sort());


      hosts_map.forEach((values, keys) => {
        let url = build_host_to_scan_report_url(values.ip, values.scan_type, values.date, values.epoch);

        if (values.is_ipv4) {
          label += `<li> <a href="${url}">${keys}</a></li>` ;
        } else {
          label += `<li> <a href="${url}">${keys} <span class="badge bg-secondary">${i18n('ipv6')}</span></a></li>` ;
        }
      });
      return label;
    };

    const build_host_to_scan_report_url = (host, scan_type, date, epoch) => {
      const active_monitoring_url = `${http_prefix}/lua/vulnerability_scan.lua`;

      let params = {
        host: host,
        scan_type: scan_type,
        scan_return_result: true,
        page: "show_result",
        scan_date: date,
        epoch: epoch

      };
      let url_params = ntopng_url_manager.obj_to_url_params(params);

      return `${active_monitoring_url}?${url_params}`;
    };

    const host_f = (host, row, ifid) => {
      let label = host;

      let host_not_reachable = row.is_ok_last_scan == 5 && row.is_down != null && row.is_down == true;
      if ((row.is_ok_last_scan == 1 || host_not_reachable) && (row.last_scan != null && row.last_scan.time != null)) {
        let url = build_host_to_scan_report_url(host, row.scan_type, row.last_scan.time.replace(" ","_"), row.last_scan.epoch);
        if (row.cidr !== undefined) {
          // add cidr if present
          const cidr = row.cidr;
          host = host + "/" + cidr;
        }
        label = `<a href="${url}">${host}</a>`;
        if (host_not_reachable) {
          label = `<a href="${url}">${host} <i class=\"fas fa-exclamation-triangle\" style='color: #B94A48;'></i> </a>`;
        }
      }
      return label;
    };

    const cves_f = (cves, row) => {
      let label = "";
      let index = 0;
      if (row.is_ok_last_scan == 1 && (row.last_scan != null && row.last_scan.time != null) && cves != null) {

        let cves_map = new Map();

        // map to sort cves on score
        cves.forEach((item) => {
          let cve_details = item.split("|");
          let actual_score = 0;
          if (cve_details.length> 1) {
            actual_score = Number(cve_details[1]);
          }
      
          cves_map.set(
            cve_details[0], 
            actual_score);
        });
      
        cves_map = new Map([...cves_map.entries()].sort((a,b) => b[1] - a[1]));

        // return first 100
        cves_map.forEach((score, key) => {
          if (index < 100) {

              let badge_type = "";
              if (score == 0) {
                badge_type = "bg-success";
              } else if(score < 3.9) {
                badge_type = "bg-secondary";
              } else if(score < 7) {
                badge_type = "bg-warning";
              } else {
                badge_type = "bg-danger";
              }
              
              const url = ntopng_utility.get_cve_details_url(key, row.scan_type);
              label += `<li  title='${i18n("hosts_stats.page_scan_hosts.report.cves_title")}'>
                        <a href="${url}"><span class="badge ${badge_type}">${key} </span></a> 
                        (${score})
                    </li>`;

            
            index++;
          } else {
            return label;
          }
        });

      }


      return label;
    };

    const _hoisted_1$1j = { class: "row" };
    const _hoisted_2$19 = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$10 = {
      key: 0,
      class: "alert alert-info alert-dismissable"
    };
    const _hoisted_4$_ = { class: "card card-shadow" };
    const _hoisted_5$N = { class: "card-body" };
    const _hoisted_6$B = {
      key: 0,
      class: "alert alert-info alert-dismissable"
    };
    const _hoisted_7$w = {
      key: 1,
      class: "alert alert-success alert-dismissable"
    };
    const _hoisted_8$t = {
      key: 2,
      class: "alert alert-danger alert-dismissable"
    };
    const _hoisted_9$p = {
      key: 3,
      class: "alert alert-warning alert-dismissable"
    };
    const _hoisted_10$m = { id: "hosts_to_scan" };
    const _hoisted_11$k = ["title"];
    const _hoisted_12$h = { class: "card-footer mt-3" };
    const _hoisted_13$g = { class: "card-footer" };

    /* ******************************************************************** */

    /* Consts */

    var script$1r = {
      __name: 'page-vulnerability-scan',
      props: {
      context: Object,
    },
      setup(__props) {

    /* Imports */
    const _i18n = (t) => i18n(t);

    const note = i18n(
      "hosts_stats.page_scan_hosts.notes.generic_notes_1"
    ).replaceAll("${http_prefix}", `${http_prefix}`);
    const note_list = [note];

    const insert_text = ref(i18n("scan_host_inserted"));
    const already_insert_text = ref(i18n("scan_host_already_inserted"));
    const not_found_hosts_text = ref(i18n("scan_net_not_found_hosts"));
    const in_progress_scan_text = ref("");
    const enable_clickhouse_text = ref(i18n("hosts_stats.page_scan_hosts.enable_clickhouse"));
    const title_delete = i18n("hosts_stats.page_scan_hosts.delete_host_title");
    const body_delete = i18n("hosts_stats.page_scan_hosts.delete_host_description");
    const title_update_periodicity_scan = i18n(
      "hosts_stats.page_scan_hosts.update_periodicity_title"
    );

    const add_host_url = `${http_prefix}/lua/rest/v2/add/host/to_scan.lua`;
    const edit_host_url = `${http_prefix}/lua/rest/v2/edit/host/update_va_scan_period.lua`;
    const remove_host_url = `${http_prefix}/lua/rest/v2/delete/host/delete_host_to_scan.lua`;
    const scan_host_url = `${http_prefix}/lua/rest/v2/exec/host/schedule_vulnerability_scan.lua`;
    const scan_type_list_url = `${http_prefix}/lua/rest/v2/get/host/vulnerability_scan_type_list.lua`;
    const active_monitoring_url = `${http_prefix}/lua/vulnerability_scan.lua`;
    const scan_result_url = `${http_prefix}/lua/rest/v2/get/host/vulnerability_scan_result.lua`;
    const check_status_url = `${http_prefix}/lua/rest/v2/get/host/vulnerability_scan_status.lua`;

    const table_id = ref("hosts_to_scan");
    const table_hosts_to_scan = ref();
    const modal_delete_confirm = ref();
    const modal_add = ref();
    const modal_update_perioditicy_scan = ref();
    const total_rows = ref(0);
    const in_progress_number = ref(null);
    const row_to_delete = ref({});
    const row_to_scan = ref({});
    const autorefresh = ref(false);
    const modal_opened = ref(false);
    const clickhouse_disabled = ref(false);
    const insert_with_success = ref(false);
    const already_inserted = ref(false);
    const not_found_hosts = ref(false);

    const props = __props;
    const rest_params = {
      csrf: props.context.csrf,
    };
    const context = ref({
      csrf: props.context.csrf,
      ifid: props.context.ifid,
      is_enterprise_l: props.context.is_enterprise_l,
    });

    let scan_type_list = [];
    let get_scan_type_list_v = null;

    /* ******************************************************************** */

    onBeforeMount(() => {
      get_scan_type_list_v = Promise.all([
        get_scan_type_list(),
        check_in_progress_status(),
      ]);
    });

    /* ******************************************************************** */

    onMounted(async () => {
      await get_scan_type_list_v;
      await modal_add.value.metricsLoaded(
        scan_type_list,
        props.context.ifid,
        props.context.is_enterprise_l
      );
      if (props.context.host != null) {
        modal_add.value.show(null, props.context.host);
      }

      clickhouse_disabled.value = !(props.context.is_clickhouse_enabled);

      /* Check the status periodically every 10 seconds */
      setInterval(check_autorefresh, 10000);
    });

    /* ******************************************************************** */

    /* Function to add a new host to scan */
    function add_host() {
      if (props.context.host != null && props.context.host != "")
        modal_add.value.show(null, props.context.host);
      else modal_add.value.show();
    }

    /* ******************************************************************** */

    /* Function to refresh table */
    function refresh_table(disable_loading) {
      /* It's important to set autorefresh to false, in this way when refreshed 
         all the entries are going to be checked and if all of them are not scanning it stays false
       */
      if (disable_loading != null)
        table_hosts_to_scan.value.refresh_table(disable_loading);
      else table_hosts_to_scan.value.refresh_table(true);
    }

    /* ******************************************************************** */

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager$1.get_url_object();
      return extra_params;
    };

    /* ******************************************************************** */

    /* Function to handle all buttons */
    function on_table_custom_event(event) {
      let events_managed = {
        click_button_edit_host: click_button_edit_host,
        click_button_delete: click_button_delete,
        click_button_scan: click_button_scan,
        click_button_download: click_button_download,
        click_button_show_result: click_button_show_result,
      };
      if (events_managed[event.event_id] == null) {
        return;
      }
      events_managed[event.event_id](event);
    }

    /* ******************************************************************** */

    /* Default sorting, by default default by IP in Asc order */
    function compare_by_host_ip(r0, r1) {
      return sortingFunctions$1.sortByIP(
        r0["host"],
        r1["host"],
        1 /* by default asc */
      );
    }

    /* ******************************************************************** */

    /* This function simply return the data of the exact column and row requested */
    function column_data(col, row) {
      let data = row[col.data.data_field];
      if (col.id == "duration") {
        data =
          row["last_scan"] === undefined
            ? -1
            : row["last_scan"]["duration_epoch"];
      } else if (col.id == "last_scan") {
        data =
          row["last_scan"] === undefined
            ? 0
            : row["last_scan"]["epoch"];
      } else if (col.id == "is_ok_last_scan") {
        data = get_scan_status_value(data, row);
      } else if (col.id == "scan_frequency") {
        data = get_scan_frequency(data);
      } else if (col.id == "max_score_cve" || col.id == "num_vulnerabilities_found" || col.id == "tcp_ports" || col.id == "udp_ports")
        // to correctly sort also during scan.
        // because during scan the old value is present but not visible
        // status must not success and not failed
        if (row["is_ok_last_scan"] != 1 && row["is_ok_last_scan"] != 5) {
          data = 0;
        }
      return data;
    }

    /* ******************************************************************** */

    /* Function used to sort the columns of the table */
    function columns_sorting(col, r0, r1) {
      if (col != null) {
        let r0_col = column_data(col, r0);
        let r1_col = column_data(col, r1);

        /* In case the values are the same, sort by IP */
        if (r0_col == r1_col) {
          return compare_by_host_ip(r0, r1);
        }
        if (col.id == "host") {
          return sortingFunctions$1.sortByIP(r0_col, r1_col, col.sort);
        } else if (col.id == "host_name") {
          return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
        } else if (col.id == "num_vulnerabilities_found") {
          const lower_value = 0;
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);

        } else if (col.id == "tcp_ports" || col.id == "udp_ports") {
          const lower_value = 0;
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);

        } else if (col.id == "duration") {
          const lower_value = -1;
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
        } else if (col.id == "last_scan") {
          const lower_value = 0;
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
        } else if (col.id == "is_ok_last_scan") {
          return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
        } else if (col.id == "max_score_cve") {
          const lower_value = 0;
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);

        } else if (col.id == "scan_frequency") {
          return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
        } else {
          return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
        }
      }

      return compare_by_host_ip(r0, r1);
    }

    /* ******************************************************************** */

    function get_scan_frequency(scan_frequency) {
      if (scan_frequency == "1day") {
        return i18n("hosts_stats.page_scan_hosts.daily");
      } else if (scan_frequency == "1week") {
        return i18n("hosts_stats.page_scan_hosts.weekly");
      } else {
        return "";
      }
    }

    /* ******************************************************************** */

    function get_scan_status_value(is_ok_last_scan, r) {
      let status = "";
      if (is_ok_last_scan == 2) {
        status = i18n("hosts_stats.page_scan_hosts.scheduled");
      } else if (is_ok_last_scan == 4) {
        status = i18n("hosts_stats.page_scan_hosts.scanning");
      } else if (is_ok_last_scan == 3 || is_ok_last_scan == null) {
        status = i18n("hosts_stats.page_scan_hosts.not_scanned");
      } else if (is_ok_last_scan == 1) {
        status = i18n("hosts_stats.page_scan_hosts.success");
      } else if (is_ok_last_scan == 5) {
        status = i18n("hosts_stats.page_scan_hosts.failed");
      } else {
        status = i18n("hosts_stats.page_scan_hosts.error");
      }
      return status + r.id;
    }

    /* ******************************************************************** */

    /* Function to handle delete button */
    function click_button_delete(event) {
      insert_with_success.value = false;
      already_inserted.value = false;
      not_found_hosts.value = false;
      refresh_feedback_messages();
      row_to_delete.value = event.row;
      modal_delete_confirm.value.show("delete_single_row", i18n("delete_vs_host"));
    }

    /* ******************************************************************** */

    /* Function to handle scan button */
    function click_button_scan(event) {
      insert_with_success.value = false;
      already_inserted.value = false;
      not_found_hosts.value = false;
      refresh_feedback_messages();
      row_to_scan.value = event.row;
      const scan_host_msg = `${i18n("scan_host")}`;
      modal_delete_confirm.value.show("scan_row", scan_host_msg);
    }

    /* ******************************************************************** */

    /* Function to handle edit button */
    function click_button_edit_host(event) {
      modal_add.value.show(event.row);
    }

    /* ******************************************************************** */

    /* Function to delete all entries */
    function delete_all_entries() {
      insert_with_success.value = false;
      already_inserted.value = false;
      not_found_hosts.value = false;
      refresh_feedback_messages();
      modal_delete_confirm.value.show("delete_all", i18n("delete_all_vs_hosts"));
    }

    /* ******************************************************************** */

    /* Function to edit host to scan */
    async function edit(params) {
      params.is_edit = true;
      await add_host_rest(params);
    }

    /* ******************************************************************** */

    /* Every 10 second check to disable feedbacks */
    function set_already_insert_or_insert_with_success() {
      if (insert_with_success.value === true) {
        insert_with_success.value = false;
      }

      if (already_inserted.value === true) {
        already_inserted.value = false;
      }

      if (not_found_hosts.value == true) {
        not_found_hosts.value = false;
      }
    }

    /* ******************************************************************** */

    /* Every 10 second check to disable autorefresh */
    function check_autorefresh() {
      check_in_progress_status();
    }

    /* ******************************************************************** */

    /* Get the number of rows of the table */
    function on_table_loaded() {
      total_rows.value = table_hosts_to_scan.value.get_rows_num();
    }

    /* ******************************************************************** */

    /* Function to map columns data */
    const map_table_def_columns = (columns) => {
      return columns_formatter(columns, scan_type_list, false, props.context.ifid);
    };

    /* ************************** REST Functions ************************** */

    /* Function to add a new host during edit */
    const add_host_rest = async function (params) {
      const url = NtopUtils.buildURL(add_host_url, {
        ...params,
      });

      const result = await ntopng_utility$1.http_post_request(url, rest_params);
      modal_add.value.close();
      if (result.rsp === 1) {
        params.vs_cidr != null
          ? (insert_text.value = i18n("scan_host_updated").replace(
            "%{host}",
            `${params.host}/${params.vs_cidr}`
          ))
          : (insert_text.value = i18n("scan_host_updated").replace(
            "%{host}",
            `${params.host}`
          ));

        insert_with_success.value = true;
        already_inserted.value = false;
        not_found_hosts.value = false;
        setTimeout(set_already_insert_or_insert_with_success, 10000);
        refresh_table(false);
      } else if (result.rsp === 2) {
        params.vs_cidr != null
          ? (already_insert_text.value = i18n("scan_host_already_inserted").replace(
            "%{host}",
            `${params.host}/${params.vs_cidr}`
          ))
          : (already_insert_text.value = i18n("scan_host_already_inserted").replace(
            "%{host}",
            `${params.host}`
          ));

        let scan_type_label = "";

        scan_type_list.forEach((item) => {
          if (item.id == params.scan_type) {
            scan_type_label = item.label;
          }
        });

        already_insert_text.value = i18n("scan_host_already_inserted").replace(
          "%{scan_type}",
          `${scan_type_label}`
        );
        already_inserted.value = true;
        insert_with_success.value = false;
        not_found_hosts.value = false;
        setTimeout(set_already_insert_or_insert_with_success, 10000);
      } else if (result.rsp === 3) {
        params.vs_cidr != null
          ? (not_found_hosts_text.value = i18n("scan_net_not_found_hosts").replace(
            "%{host}",
            `${params.host}/${params.vs_cidr}`
          ))
          : (not_found_hosts_text.value = i18n("scan_net_not_found_hosts").replace(
            "%{host}",
            `${params.host}`
          ));

        already_inserted.value = false;
        insert_with_success.value = false;
        not_found_hosts.value = true;
        setTimeout(set_already_insert_or_insert_with_success, 10000);
      }

      if (params.is_edit !== true) {
        refresh_table(false);
      }
    };

    /* ******************************************************************** */

    /* This function adds a feedback message on the page */
    const refresh_feedback_messages = function (in_progress) {
      /* In case a in_progress is a number, customize the feedback message */
      if (in_progress != null && in_progress != 0) {
        in_progress_scan_text.value = i18n("scan_in_progress").replace(
          "total",
          `${in_progress}`
        );
      }
    };

    /* ******************************************************************** */

    const update_all_scan_frequencies = async function (params) {
      const url = NtopUtils.buildURL(edit_host_url, {
        ...params,
      });

      await ntopng_utility$1.http_post_request(url, rest_params);

      insert_with_success.value = false;
      already_inserted.value = false;
      not_found_hosts.value = false;
      refresh_feedback_messages();
      refresh_table(false);
    };

    /* ******************************************************************** */

    /* Function to retrieve scan types list */
    const get_scan_type_list = async function () {
      const url = NtopUtils.buildURL(scan_type_list_url, {
        ...rest_params,
      });

      const result = await ntopng_utility$1.http_request(url);
      scan_type_list = result.rsp;
    };

    /* ******************************************************************** */

    /* Function to check if there is a scan in progress */
    const check_in_progress_status = async function () {
      const url = NtopUtils.buildURL(check_status_url, {
        ...rest_params,
      });

      const result = await ntopng_utility$1.http_request(url);
      insert_with_success.value = false;
      already_inserted.value = false;
      not_found_hosts.value = false;
      refresh_feedback_messages(result.rsp.total_in_progress);

      /* Get the number of scans currently in progress */
      /* In case the number changed, refresh the table */
      if (in_progress_number.value == null) {
        /* First time checking the number of scans, don't refresh the table */
        in_progress_number.value = result.rsp.total_in_progress;
      }

      const scans_ended =
        result.rsp.total_in_progress == 0 && in_progress_number.value > 0;
      in_progress_number.value = result.rsp.total_in_progress;
      autorefresh.value =
        in_progress_number.value > 0 && modal_opened.value === false;

      if (autorefresh.value === true) {
        /* Refresh the data, periodic update */
        setTimeout(function () {
          refresh_table(true);
        }, 2000);
      } else if (scans_ended) {
        /* Refresh the data, all scans ended */
        setTimeout(function () {
          refresh_table(true);
        }, 5000);
      }
    };

    /* ******************************************************************** */

    /* Function to confirm to start all scan */
    const confirm_scan_all_entries = function () {
      modal_delete_confirm.value.show("scan_all_rows", i18n("scan_all_hosts"));
      refresh_table(false);
    };

    /* ******************************************************************** */

    /* Function to update all scan  frequencies*/
    const update_all_periodicity = function () {
      modal_update_perioditicy_scan.value.show();
    };

    /* ******************************************************************** */

    /* Function to exec the vulnerability scan of a single host */
    const scan_row = async function () {
      const row = row_to_scan.value;
      await scan_row_rest(row.host, row.scan_type, row.ports, row.id, row.cidr);
      refresh_table(true /* Disable loading, annoying when enabling a scan */);
    };

    /* ******************************************************************** */

    const scan_row_rest = async function (host, scan_type, ports, id, cidr) {
      const url = NtopUtils.buildURL(scan_host_url, {
        host: host,
        scan_type: scan_type,
        scan_single_host: true,
        scan_ports: ports,
        scan_id: id,
        vs_cidr: cidr
      });
      await ntopng_utility$1.http_post_request(url, rest_params);
    };

    /* ******************************************************************** */

    /* Function to exec a vulnerability scan to all hosts set */
    async function scan_all_entries() {
      const url = NtopUtils.buildURL(scan_host_url, {
        scan_single_host: false,
      });
      await ntopng_utility$1.http_post_request(url, rest_params);
      refresh_table(false);
    }

    /* ******************************************************************** */

    /* Function to delete host to scan */
    const delete_row = async function () {
      const row = row_to_delete.value;
      const url = NtopUtils.buildURL(remove_host_url, {
        host: row.host,
        scan_type: row.scan_type,
        delete_all_scan_hosts: false,
        scan_id: row.id,
      });

      await ntopng_utility$1.http_post_request(url, rest_params);
      refresh_table(false);
    };

    /* ******************************************************************** */

    /* This function deletes all the rows */
    const delete_all_rows = async function () {
      const url = NtopUtils.buildURL(remove_host_url, {
        delete_all_scan_hosts: true,
      });

      await ntopng_utility$1.http_post_request(url, rest_params);
      refresh_table(false);
    };

    /* ******************************************************************** */

    /* Function to download last vulnerability scan result */
    function click_button_download(event) {
      const params = {
        host: event.row.host,
        scan_type: event.row.scan_type,
        epoch: event.row.last_scan.epoch
      };
      const url_params = ntopng_url_manager$1.obj_to_url_params(params);

      const url = `${scan_result_url}?${url_params}`;
      ntopng_utility$1.download_URI(url);
    }

    /* ******************************************************************** */

    /* Function to show last vulnerability scan result */
    function click_button_show_result(event) {
      const host = event.row.host;
      const date = event.row.last_scan.time.replace(" ", "_");

      const params = {
        host: host,
        scan_type: event.row.scan_type,
        scan_return_result: true,
        page: "show_result",
        epoch: event.row.last_scan.epoch,
        scan_date: date,
      };
      const url_params = ntopng_url_manager$1.obj_to_url_params(params);

      const url = `${active_monitoring_url}?${url_params}`;
      ntopng_url_manager$1.go_to_url(url);
    }

    /* ******************************************************************** */

    function update_modal_status(value) {
      /* update the modal_opened var used for disable/enable 
         autorefresh when modal is open/closed */
      modal_opened.value = value;
    }

    /* ******************************************************************** */

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$1j, [
          createBaseVNode("div", _hoisted_2$19, [
            (clickhouse_disabled.value)
              ? (openBlock(), createElementBlock("div", _hoisted_3$10, [
                  createBaseVNode("span", null, toDisplayString(enable_clickhouse_text.value), 1 /* TEXT */)
                ]))
              : createCommentVNode("v-if", true),
            createBaseVNode("div", _hoisted_4$_, [
              createBaseVNode("div", _hoisted_5$N, [
                (autorefresh.value)
                  ? (openBlock(), createElementBlock("div", _hoisted_6$B, [
                      _cache[3] || (_cache[3] = createBaseVNode("span", { class: "spinner-border spinner-border-sm text-info me-1" }, null, -1 /* CACHED */)),
                      createBaseVNode("span", null, toDisplayString(in_progress_scan_text.value), 1 /* TEXT */)
                    ]))
                  : createCommentVNode("v-if", true),
                (insert_with_success.value)
                  ? (openBlock(), createElementBlock("div", _hoisted_7$w, [
                      _cache[4] || (_cache[4] = createBaseVNode("span", { class: "text-success me-1" }, null, -1 /* CACHED */)),
                      createBaseVNode("span", null, toDisplayString(insert_text.value), 1 /* TEXT */)
                    ]))
                  : createCommentVNode("v-if", true),
                (already_inserted.value)
                  ? (openBlock(), createElementBlock("div", _hoisted_8$t, [
                      _cache[5] || (_cache[5] = createBaseVNode("span", { class: "text-danger me-1" }, null, -1 /* CACHED */)),
                      createBaseVNode("span", null, toDisplayString(already_insert_text.value), 1 /* TEXT */)
                    ]))
                  : createCommentVNode("v-if", true),
                (not_found_hosts.value)
                  ? (openBlock(), createElementBlock("div", _hoisted_9$p, [
                      _cache[6] || (_cache[6] = createBaseVNode("span", { class: "text-warning me-1" }, null, -1 /* CACHED */)),
                      createBaseVNode("span", null, toDisplayString(not_found_hosts_text.value), 1 /* TEXT */)
                    ]))
                  : createCommentVNode("v-if", true),
                createBaseVNode("div", _hoisted_10$m, [
                  createVNode(script$1u, {
                    ref_key: "modal_delete_confirm",
                    ref: modal_delete_confirm,
                    title: unref(title_delete),
                    body: unref(body_delete),
                    onDelete: delete_row,
                    onDelete_all: delete_all_rows,
                    onScan_row: scan_row,
                    onScan_all_rows: scan_all_entries
                  }, null, 8 /* PROPS */, ["title", "body"]),
                  createVNode(script$1t, {
                    ref_key: "modal_update_perioditicy_scan",
                    ref: modal_update_perioditicy_scan,
                    title: unref(title_update_periodicity_scan),
                    onUpdate: update_all_scan_frequencies
                  }, null, 8 /* PROPS */, ["title"]),
                  createVNode(script$2w, {
                    ref_key: "table_hosts_to_scan",
                    ref: table_hosts_to_scan,
                    table_id: table_id.value,
                    showLoading: true,
                    csrf: context.value.csrf,
                    f_map_columns: map_table_def_columns,
                    get_extra_params_obj: get_extra_params_obj,
                    f_sort_rows: columns_sorting,
                    onCustom_event: on_table_custom_event,
                    onRows_loaded: on_table_loaded
                  }, {
                    custom_buttons: withCtx(() => [
                      createBaseVNode("button", {
                        class: "btn btn-link",
                        type: "button",
                        ref: "add_host",
                        onClick: add_host
                      }, [
                        createBaseVNode("i", {
                          class: "fas fa-plus",
                          "data-bs-toggle": "tooltip",
                          "data-bs-placement": "top",
                          title: _i18n('add_vs_host')
                        }, null, 8 /* PROPS */, _hoisted_11$k)
                      ], 512 /* NEED_PATCH */)
                    ]),
                    _: 1 /* STABLE */
                  }, 8 /* PROPS */, ["table_id", "csrf"])
                ])
              ]),
              createBaseVNode("div", _hoisted_12$h, [
                createBaseVNode("button", {
                  type: "button",
                  ref: "delete_all",
                  onClick: delete_all_entries,
                  class: normalizeClass(["btn btn-danger me-1", { disabled: total_rows.value == 0 }])
                }, [
                  _cache[7] || (_cache[7] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                  createTextVNode(" " + toDisplayString(_i18n("delete_all_entries")), 1 /* TEXT */)
                ], 2 /* CLASS */),
                createBaseVNode("button", {
                  type: "button",
                  ref: "scan_all",
                  onClick: confirm_scan_all_entries,
                  class: normalizeClass(["btn btn-primary me-1", { disabled: total_rows.value == 0 }])
                }, [
                  _cache[8] || (_cache[8] = createBaseVNode("i", { class: "fas fa-clock-rotate-left" }, null, -1 /* CACHED */)),
                  createTextVNode(" " + toDisplayString(_i18n("hosts_stats.page_scan_hosts.schedule_all_scan")), 1 /* TEXT */)
                ], 2 /* CLASS */),
                (props.context.is_enterprise_l)
                  ? (openBlock(), createElementBlock("button", {
                      key: 0,
                      type: "button",
                      ref: "update_all",
                      onClick: update_all_periodicity,
                      class: normalizeClass(["btn btn-secondary me-1", { disabled: total_rows.value == 0 }])
                    }, toDisplayString(_i18n("hosts_stats.page_scan_hosts.update_periodicity_title")), 3 /* TEXT, CLASS */))
                  : createCommentVNode("v-if", true)
              ]),
              createBaseVNode("div", _hoisted_13$g, [
                createVNode(script$2u, { note_list: note_list })
              ])
            ])
          ])
        ]),
        createVNode(script$1s, {
          ref_key: "modal_add",
          ref: modal_add,
          context: context.value,
          onAdd: add_host_rest,
          onEdit: edit,
          onCloseModal: _cache[0] || (_cache[0] = $event => (update_modal_status(false))),
          onOpenModal: _cache[1] || (_cache[1] = $event => (update_modal_status(true))),
          onHidden: _cache[2] || (_cache[2] = $event => (update_modal_status(false)))
        }, null, 8 /* PROPS */, ["context"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1r.__file = "http_src/vue/page-vulnerability-scan.vue";

    const _hoisted_1$1i = { class: "row" };
    const _hoisted_2$18 = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$$ = { class: "card card-shadow" };
    const _hoisted_4$Z = ["innerHTML"];
    const _hoisted_5$M = ["innerHTML"];


    var script$1q = {
      __name: 'page-host-vs-result',
      props: {
      context: Object,
    },
      setup(__props) {

    const scan_result_url = `${http_prefix}/lua/rest/v2/get/host/vulnerability_scan_result.lua`;

    ref(null);
    const loading = ref(true);


    const props = __props; 
    const message = ref('');
    const message_html = ref('');
    const title_html = ref('');

    const title = ref('');
    ref([]);


    async function get_result(host, scan_type, date, epoch) {
      
      loading.value = true;
      let params = {
        host: host,
        scan_type: scan_type,
        scan_return_result: true,
        epoch: epoch

      };
      let url_params = ntopng_url_manager.obj_to_url_params(params);
      let url = `${scan_result_url}?${url_params}`;
      let result = await ntopng_utility.http_request(url);
      message.value = result.rsp;
      message_html.value = `<pre>${message.value}</pre>`;

      if (scan_type == 'ipv4_netscan') {
        host += "/24";
      }
      const host_href = props.context.is_in_mem === 'true' || props.context.is_in_mem == true ? `${host} <a href="${http_prefix}/lua/host_details.lua?host=${host}"><i class = "fas fa-laptop"></i></a>`: host;
      
      
      title.value = i18n("hosts_stats.page_scan_hosts.vs_result").replace("%{host}", host_href);
      if (date != null)
        date = date.replaceAll("_"," ");

      
      title.value = title.value.replace("%{date}",date);
      title_html.value = title.value;

      loading.value = false;
    }



    /* ******************************************************************** */ 

    onBeforeMount(async () => {
      await get_result(props.context.host, props.context.scan_type, props.context.date, props.context.epoch);
    });



    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1i, [
        createBaseVNode("div", _hoisted_2$18, [
          createBaseVNode("div", _hoisted_3$$, [
            createBaseVNode("h3", {
              class: "d-inline-block pt-3 ps-3",
              innerHTML: title_html.value
            }, null, 8 /* PROPS */, _hoisted_4$Z),
            createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
            createBaseVNode("div", {
              class: normalizeClass(["card-body", [loading.value ? 'ntopng-gray-out' : '']]),
              innerHTML: message_html.value
            }, null, 10 /* CLASS, PROPS */, _hoisted_5$M)
          ])
        ])
      ]))
    }
    }

    };

    script$1q.__file = "http_src/vue/page-host-vs-result.vue";

    const _hoisted_1$1h = { class: "row" };
    const _hoisted_2$17 = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$_ = { class: "card card-shadow" };
    const _hoisted_4$Y = { class: "card-body" };
    const _hoisted_5$L = { id: "open_ports" };

    /* ******************************************************************** */

    /* Consts */

    var script$1p = {
      __name: 'page-open-ports',
      props: {
      context: Object,
    },
      setup(__props) {



    const active_monitoring_url = `${http_prefix}/lua/vulnerability_scan.lua`;


    const table_id = ref('open_ports');
    const map_config = (config) => {
      return config;
    };

    const table_open_ports = ref();

    const props = __props;

    ({
      csrf: props.context.csrf
    });

    const context = ref({
      csrf: props.context.csrf,
      ifid: props.context.ifid,
      is_enterprise_l: props.context.is_enterprise_l
    });

    /* ******************************************************************** */

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager$1.get_url_object();
      return extra_params;
    };

    /* ******************************************************************** */

    /* Function to handle all buttons */
    function on_table_custom_event(event) {

      let events_managed = {
        "click_button_show_hosts": click_button_show_hosts
      };
      if (events_managed[event.event_id] == null) {
        return;
      }
      events_managed[event.event_id](event);
    }


    function compare_by_port(r0, r1) {

      let col = {
        "data": {
          "title_i18n": "port",
          "data_field": "port",
          "sortable": true,
          "class": [
            "text-nowrap",
            "text-end"
          ]
        }
      };
      let r0_col = r0[col.data.data_field];
      let r1_col = r1[col.data.data_field];

      r0_col = format_num_ports_for_sort(r0_col);
      r1_col = format_num_ports_for_sort(r1_col);
      return r0_col - r1_col;
    }

    function columns_sorting(col, r0, r1) {

      if (col != null) {
        let r0_col = r0[col.data.data_field];
        let r1_col = r1[col.data.data_field];
        if (col.id == "port") {

          r0_col = format_num_ports_for_sort(r0_col);
          r1_col = format_num_ports_for_sort(r1_col);
          if (col.sort == 1) {
            return r0_col - r1_col;
          }
          return r1_col - r0_col;
        } else if (col.id == "count_host") {
          r0_col = format_cve_num(r0_col);
          r1_col = format_cve_num(r1_col);

          if (r0_col == r1_col) {
            return compare_by_port(r0, r1);
          }
          if (col.sort == 1) {
            return r0_col - r1_col;
          }
          return r1_col - r0_col;
        }
        else if (col.id == "cves") {
          r0_col = format_cve_num(r0_col);
          r1_col = format_cve_num(r1_col);

          if (r0_col == r1_col) {
            return compare_by_port(r0, r1);
          }
          if (col.sort == 1) {
            return r0_col - r1_col;
          }
          return r1_col - r0_col;
        }
        else if (col.id == "hosts") {
          /* It's an array */

          if (r0_col == r1_col) {
            return compare_by_port(r0, r1);
          }
          if (col.sort == 1) {
            return r0_col.localeCompare(r1_col);
          }
          return r1_col.localeCompare(r0_col);
        } else {

          if (r0_col != null) ; else {
            r0_col = "";
          }

          if (r1_col != null) ; else {
            r1_col = "";
          }
          if (r0_col == r1_col) {
            return compare_by_port(r0, r1);
          }
          if (col.sort == 1) {
            return r0_col.localeCompare(r1_col);
          }
          return r1_col.localeCompare(r0_col);
        }

      } else {
        return compare_by_port(r0, r1);
      }

    }

    function format_cve_num(num) {
      let value = 0;
      if (dataUtils.isEmptyOrNull(num) || isNaN(value)) {
        value = 0;
      } else {
        num = num.split(',').join("");
        value = parseInt(num);
      }

      return value;
    }

    function format_num_ports_for_sort(num) {
      if (dataUtils.isEmptyOrNull(num))
        num = 0;

      num = parseInt(num);  return num;
    }

    /* ******************************************************************** */
    /* Function to map columns data */
    const map_table_def_columns = (columns) => {
      let map_columns = {
        "hosts": (hosts, row) => {
          let label = ``;
          const hosts_splited = hosts.split(", ");
          const length = hosts_splited.length;
          let i = 0;
          while (i < 5 && i < length) {
            const host_splitted = hosts_splited[i].split("|");
            const host = host_splitted[0];
            const scan_type = host_splitted[1];
            const date = host_splitted[2].replace(" ", "_");
            const is_ipv4 = host_splitted[3] === 'true';
            const epoch = host_splitted[4];

            let host_name = '';
            if (host_splitted.length > 5) {
              host_name = host_splitted[5];
            }

            let params = {
              host: host,
              scan_type: scan_type,
              scan_return_result: true,
              page: "show_result",
              epoch: epoch,
              scan_date: date

            };
            let url_params = ntopng_url_manager$1.obj_to_url_params(params);

            let url = `${active_monitoring_url}?${url_params}`;

            const host_label = host_name != '' ? (is_ipv4 == false ? `${host_name}  <span class="badge bg-secondary">${i18n('ipv6')}</span>` : host_name) : host;

            if (label == ``)
              label += `<a href="${url}">${host_label}</a>`;
            else
              label += `, <a href="${url}">${host_label}</a>`;

            i++;
          }

          if (length > 5) {
            label += `...`;
          }

          return label;

        }
      };


      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];
      });

      return columns;
    };

    /* ************************** REST Functions ************************** */
    /* Function to show all hosts during edit */
    async function click_button_show_hosts(event) {
      let port = event.row.port_number;

      let params = {
        port: port,
      };

      let url_params = ntopng_url_manager$1.obj_to_url_params(params);

      let url = `${active_monitoring_url}?${url_params}`;
      ntopng_url_manager$1.go_to_url(url);
    }


    /* ******************************************************************** */


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1h, [
        createBaseVNode("div", _hoisted_2$17, [
          createBaseVNode("div", _hoisted_3$_, [
            createBaseVNode("div", _hoisted_4$Y, [
              createBaseVNode("div", _hoisted_5$L, [
                createVNode(script$2w, {
                  ref_key: "table_open_ports",
                  ref: table_open_ports,
                  table_id: table_id.value,
                  csrf: context.value.csrf,
                  f_map_columns: map_table_def_columns,
                  get_extra_params_obj: get_extra_params_obj,
                  f_sort_rows: columns_sorting,
                  f_map_config: map_config,
                  onCustom_event: on_table_custom_event
                }, null, 8 /* PROPS */, ["table_id", "csrf"])
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1p.__file = "http_src/vue/page-open-ports.vue";

    const _hoisted_1$1g = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$16 = { class: "col-form-label col-sm-2" };
    const _hoisted_3$Z = { class: "col-sm-10" };
    const _hoisted_4$X = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_5$K = { class: "col-form-label col-sm-2" };
    const _hoisted_6$A = { class: "col-sm-10" };
    const _hoisted_7$v = { class: "mt-4" };
    const _hoisted_8$s = ["disabled"];


    /* ****************************************************** */


    var script$1o = {
      __name: 'modal-edit-vs-report',
      props: {
      context: Object,
    },
      emits: ["add", "edit"],
      setup(__props, { expose: __expose, emit: __emit }) {

    /* Imports */
    const _i18n = (t) => i18n(t);
    const emit = __emit;

    /* Consts */
    const title = ref(i18n("hosts_stats.page_scan_hosts.reports_page.edit_report"));

    const modal_id = ref(null);
    const report_date = ref(null);
    const report_name = ref(null);
    const row_to_edit = ref(null);

    const is_report_name_correct = ref(false);

    const note_list = [
      _i18n("hosts_stats.page_scan_hosts.reports_page.notes.note_1"),
    ];
    /* ****************************************************** */

    /*
     * Reset fields in modal form
     */
    const reset_modal_form = function () {
      report_date.value = "";
      report_name.value = "";
      row_to_edit.value = null;
      is_report_name_correct.value = false; 
      
    };

    /* ****************************************************** */

    /*
     * Set row to edit
     */
    const set_row_to_edit = (row) => {

      row_to_edit.value = row;
      /* Set host values */
      report_date.value = row.report_date;
      report_name.value = row.name;

    };

    /* ****************************************************** */

    /* This method is called whenever the modal is opened */
    const show = (row) => {
      /* First of all reset all the data */
      reset_modal_form();
      set_row_to_edit(row);

      modal_id.value.show();
    };

    /* ****************************************************** */


    /* ****************************************************** */

    /* Function called when the edit button is clicked */
    const edit_ = () => {
      const tmp_report_date = row_to_edit.value.epoch;
      const tmp_name = report_name.value.replaceAll(" ", "_");

      emit("edit", {
          report_title: tmp_name,
          epoch_end: tmp_report_date
        });
      
        modal_id.value.close();
    };

    /* ****************************************************** */

    /* Function called when the modal is closed */
    const close = () => {
      modal_id.value.close();
    };

    /* ****************************************************** */



    /* ****************************************************** */

    const check_title = () => {
      // Handle empty input
      if (!report_name.value || report_name.value.trim() === "") {
        is_report_name_correct.value = false;
        return;
      }

      const name = report_name.value.trim();
      
      // Only allow alphanumeric characters, spaces, hyphens, and underscores
      const safeCharacterPattern = /^[a-zA-Z0-9\s\-_]+$/;
      
      const sqlInjectionPatterns = [
        /['"`]/,           // Single quotes, double quotes, backticks
        /[;]/,             // Semicolon
        /--/,              // SQL comments
        /\/\*/,            // Multi-line comment start
        /\*\//,            // Multi-line comment end
        /\b(union|select|insert|update|delete|drop|create|alter|exec|execute)\b/i, // SQL keywords
        /[<>]/,            // HTML/XML tags (XSS prevention)
        /[\\]/,            // Backslashes
        /\x00/,            // Null bytes
      ];
      
      const containsOnlySafeChars = safeCharacterPattern.test(name);
      const containsSqlInjection = sqlInjectionPatterns.some(pattern => pattern.test(name));
      
      // length check
      const isReasonableLength = name.length > 0 && name.length <= 100;
      
      is_report_name_correct.value = containsOnlySafeChars && !containsSqlInjection && isReasonableLength;
    };

    __expose({ show, close });

    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Target information, here an IP is put "),
          createBaseVNode("div", _hoisted_1$1g, [
            createBaseVNode("label", _hoisted_2$16, [
              createBaseVNode("b", null, toDisplayString(_i18n("hosts_stats.page_scan_hosts.reports_page.date")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$Z, [
              withDirectives(createBaseVNode("input", {
                "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((report_date).value = $event)),
                disabled: true,
                class: "form-control",
                type: "text"
              }, null, 512 /* NEED_PATCH */), [
                [vModelText, report_date.value]
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_4$X, [
            createBaseVNode("label", _hoisted_5$K, [
              createBaseVNode("b", null, toDisplayString(_i18n("hosts_stats.page_scan_hosts.reports_page.name")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_6$A, [
              withDirectives(createBaseVNode("input", {
                "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((report_name).value = $event)),
                class: "form-control",
                type: "text",
                onInput: check_title,
                required: ""
              }, null, 544 /* NEED_HYDRATION, NEED_PATCH */), [
                [vModelText, report_name.value]
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_7$v, [
            createVNode(script$2u, { note_list: note_list })
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", null, [
            createBaseVNode("button", {
              type: "button",
              onClick: edit_,
              disabled: !(is_report_name_correct.value),
              class: "btn btn-primary"
            }, toDisplayString(_i18n("apply")), 9 /* TEXT, PROPS */, _hoisted_8$s)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1o.__file = "http_src/vue/modal-edit-vs-report.vue";

    const _hoisted_1$1f = { class: "row" };
    const _hoisted_2$15 = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$Y = { class: "card card-shadow" };
    const _hoisted_4$W = { class: "card-body" };
    const _hoisted_5$J = { id: "vs_reports" };
    /* ******************************************************************** */

    /* Consts */

    var script$1n = {
      __name: 'page-vulnerability-scan-report',
      props: {
      context: Object,
    },
      setup(__props) {

    /* Imports */
    const _i18n = (t) => i18n(t);

    i18n(
      "hosts_stats.page_scan_hosts.notes.generic_notes_1"
    ).replaceAll("${http_prefix}", `${http_prefix}`);

    const title_delete = _i18n("hosts_stats.page_scan_hosts.report_page.delete_title");
    const body_delete = _i18n("hosts_stats.page_scan_hosts.report_page.delete_description");


    const open_report_url = `${http_prefix}/lua/pro/enterprise/vulnerability_scan_report.lua`;
    const remove_report_url = `${http_prefix}/lua/pro/rest/v2/delete/vs/report.lua`;
    const edit_report_url = `${http_prefix}/lua/pro/rest/v2/edit/vs/report.lua`;

    const table_id = ref("vs_reports");
    const table_vs_reports = ref();
    const modal_edit = ref();
    const modal_delete_confirm = ref();
    const total_rows = ref(0);
    const row_to_delete = ref({});

    const props = __props;

    const rest_params = {
      csrf: props.context.csrf,
    };

    const context = ref({
      csrf: props.context.csrf,
      ifid: props.context.ifid,
    });

    /* ******************************************************************** */

    /* Function to refresh table */
    function refresh_table() {
      table_vs_reports.value.refresh_table();
    }

    /* ******************************************************************** */

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager$1.get_url_object();
      return extra_params;
    };

    /* ******************************************************************** */

    /* Function to handle all buttons */
    function on_table_custom_event(event) {
      let events_managed = {
        click_button_edit: click_button_edit,
        click_button_delete: click_button_delete,
      };
      if (events_managed[event.event_id] == null) {
        return;
      }
      events_managed[event.event_id](event);
    }

    /* ******************************************************************** */

    /* This function simply return the data of the exact column and row requested */
    function column_data(col, row) {
      let data = row[col.data.data_field];

      if (col.data.data_field == 'report_date') {
        data = row['epoch'];
      }

      if (col.data.data_field == 'name') {
        data = format_report_name(row['name'], row['epoch']);
      }

      return data;
    }

    /* ******************************************************************** */

    /* Default sorting, by date in DESC order */
    function compare_by_report_date(r0, r1) {
      return sortingFunctions$1.sortByNumber(
        r0["epoch"],
        r1["epoch"],
        0 /* by default asc */
      );
    }

    /* Specific function for report name because could be "Report of date" (default) or a custom name */
    function format_report_name(report_name, epoch) {
      
      let formatted_report_name = report_name;
      
      if (report_name.startsWith("Report of")) {
        formatted_report_name = 'report_'+epoch;   
      }

      return formatted_report_name;

    }

    /* ******************************************************************** */

    /* Function used to sort the columns of the table */
    function columns_sorting(col, r0, r1) {
      if (col != null) {
        let r0_col = column_data(col, r0);
        let r1_col = column_data(col, r1);

        /* In case the values are the same, sort by IP */
        if (r0_col == r1_col) {
          return compare_by_report_date(r0, r1);
        }
        
        if (col.id == "report_date") {
          return sortingFunctions$1.sortByNumber(r0_col, r1_col, col.sort);
        } else if (col.id == "tcp_ports" || col.id == "udp_ports") {
          return sortingFunctions$1.sortByNumber(r0_col, r1_col, col.sort);
        } else if (col.id == "cves") {
          return sortingFunctions$1.sortByNumber(r0_col, r1_col, col.sort);
        } else if (col.id == "name") {
          return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
        } else if (col.id == "num_hosts") {
          return sortingFunctions$1.sortByNumber(r0_col, r1_col, col.sort);
        } else {
          return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
        }
      }

      return compare_by_report_date(r0, r1);
    }


    /* ******************************************************************** */

    /* Function to handle delete button */
    function click_button_delete(event) {
      row_to_delete.value = event.row;
      modal_delete_confirm.value.show("delete_single_report");
    }



    /* ******************************************************************** */

    /* Function to handle edit button */
    function click_button_edit(event) {
      modal_edit.value.show(event.row);
    }

    /* ******************************************************************** */

    /* Function to edit report */
    async function edit(params) {
      await edit_report_rest(params);
    }

    const edit_report_rest = async function (params) {
      const url = NtopUtils.buildURL(edit_report_url, {
        ...params,
      });

      await ntopng_utility$1.http_post_request(url, rest_params);

      refresh_table();
    };

    /* ******************************************************************** */

    /* Get the number of rows of the table */
    function on_table_loaded() {
      total_rows.value = table_vs_reports.value.get_rows_num();
    }

    /* ******************************************************************** */

    /* Function to map columns data */
    const map_table_def_columns = (columns) => {


      let map_columns = {
        "report_date": (report_date, row) => {
            const url = NtopUtils.buildURL(open_report_url, {
            printable: false,
            epoch_end: row.epoch,
            epoch_begin: row.epoch
          });
        
        return `<a href="${url}">${report_date}</a>`;
        },
      };

      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];

        
      });
      
      return columns;
    };

    /* ************************** REST Functions ************************** */


    /* ******************************************************************** */

    /* Function to delete report */
    const delete_row = async function () {
      const row = row_to_delete.value;
      const url = NtopUtils.buildURL(remove_report_url, {
        epoch: row.epoch
      });
      await ntopng_utility$1.http_post_request(url, rest_params);
      refresh_table();
    };

    /* ******************************************************************** */

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$1f, [
          createBaseVNode("div", _hoisted_2$15, [
            createBaseVNode("div", _hoisted_3$Y, [
              createBaseVNode("div", _hoisted_4$W, [
                createBaseVNode("div", _hoisted_5$J, [
                  createVNode(script$1u, {
                    ref_key: "modal_delete_confirm",
                    ref: modal_delete_confirm,
                    title: unref(title_delete),
                    body: unref(body_delete),
                    onDelete: delete_row
                  }, null, 8 /* PROPS */, ["title", "body"]),
                  createVNode(script$2w, {
                    ref_key: "table_vs_reports",
                    ref: table_vs_reports,
                    table_id: table_id.value,
                    csrf: context.value.csrf,
                    f_map_columns: map_table_def_columns,
                    get_extra_params_obj: get_extra_params_obj,
                    f_sort_rows: columns_sorting,
                    onCustom_event: on_table_custom_event,
                    onRows_loaded: on_table_loaded
                  }, null, 8 /* PROPS */, ["table_id", "csrf"])
                ])
              ])
            ])
          ])
        ]),
        createVNode(script$1o, {
          ref_key: "modal_edit",
          ref: modal_edit,
          context: context.value,
          onEdit: edit
        }, null, 8 /* PROPS */, ["context"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1n.__file = "http_src/vue/page-vulnerability-scan-report.vue";

    const _hoisted_1$1e = { class: "card h-100 overflow-hidden" };
    const _hoisted_2$14 = { class: "m-2 mt-0" };
    const _hoisted_3$X = { class: "m-2 mb-3" };
    const _hoisted_4$V = { class: "card-footer" };

    /* ************************************** */

    const max_interfaces = 20;
    const serie_name = "Congestion";
    const system_interface_id$1 = -1;

    var script$1m = {
      __name: 'page-snmp-usage',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    /* ************************************** */
    const time_preset_list = [
        { value: "10_min", label: i18n('show_alerts.presets.10_min'), currently_active: false },
        { value: "30_min", label: i18n('show_alerts.presets.30_min'), currently_active: false },
        { value: "hour", label: i18n('show_alerts.presets.hour'), currently_active: false },
        { value: "2_hours", label: i18n('show_alerts.presets.2_hours'), currently_active: false },
        { value: "6_hours", label: i18n('show_alerts.presets.6_hours'), currently_active: true },
        { value: "12_hours", label: i18n('show_alerts.presets.12_hours'), currently_active: false },
        { value: "day", label: i18n('show_alerts.presets.day'), currently_active: false },
        { value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
        { value: "month", label: i18n('show_alerts.presets.month'), currently_active: false },
        { value: "year", label: i18n('show_alerts.presets.year'), currently_active: false },
        { value: "custom", label: i18n('show_alerts.presets.custom'), currently_active: false, disabled: true, },
    ];

    const table_snmp_usage = ref(null);
    const date_time_picker = ref(null);
    const table_id = ref('snmp_usage');
    const chart_id = ref('snmp_usage_chart');
    const csrf = props.context.csrf;
    const chart = ref(null);
    const chart_type = ref(ntopChartApex.typeChart.TS_LINE);
    const base_url = `${http_prefix}/lua/pro/rest/v2/get/snmp/metric/usage_chart.lua`;
    let id_date_time_picker = "date_time_picker";

    const note_list = [
        i18n('snmp.chart_congestion_rate_note'),
        i18n('snmp.chart_congestion_link'),
        i18n('snmp.chart_congestion_configuration'),
        i18n('snmp.chart_congestion_rate_color'),
    ];

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        const formatter = formatterUtils.getFormatter("percentage");
        let map_columns = {
            "ip": (value, row) => {
                const url = `${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?ip=${value}`;
                return `<a href=${url}>${value}</a>`
            },
            "interface": (value, row) => {
                const epoch_begin = ntopng_url_manager.get_url_entry("epoch_begin");
                const epoch_end = ntopng_url_manager.get_url_entry("epoch_end");
                const url = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.ip}&snmp_port_idx=${row.ifid}&page=historical&ifid=-1&epoch_end=${epoch_end}&epoch_begin=${epoch_begin}&timeseries_groups_mode=1_chart_x_metric&timeseries_groups=snmp_interface;-1%2B${row.ip}%2B${row.ifid};snmp_if:usage;uplink=true:false:false:false|downlink=true:false:false:false`;
                return `<a href=${url}>${value}</a>`
            },
            "type": (type, row) => {
                if (type == 'uplink') {
                    return `${i18n('out_usage')} <i class="fa-solid fa-circle-arrow-up" style="color: #C6D9FD"></i>`
                } else {
                    return `${i18n('in_usage')} <i class="fa-solid fa-circle-arrow-down" style="color: #90EE90"></i>`
                }
            },
            "speed": (value, row) => {
                const formatted_speed = formatterUtils.getFormatter("speed")(value);
                return `${formatted_speed} <a href='${create_config_url_link(row)}'><i class="fas fa-cog"></i></a>`
            },
            "min": (value, row) => {
                return formatter(value);
            },
            "max": (value, row) => {
                return formatter(value);
            },
            "average": (value, row) => {
                return formatter(value);
            },
            "last_value": (value, row) => {
                return formatter(value);
            },
            "congestion_rate": (value, row) => {
                return formatter(value);
            }
        };
        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    timeseries_chart: props.context.timeseries_enabled,
                    device_configuration: true,
                    interface_configuration: true,
                };
                c.button_def_array.forEach((b) => {
                    if (!visible_dict[b.id]) {
                        b.class.push("disabled");
                    }
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            const r0_col = r0[col.data.data_field];
            const r1_col = r1[col.data.data_field];

            /* In case the values are the same, sort by IP */
            if (r0_col == r1_col) {
                return sortingFunctions$1.sortByName(r0.device, r1.device, col ? col.sort : null);
            }
            if (col.id == "device_name") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else if (col.id == "ip") {
                return sortingFunctions$1.sortByIP(r0_col, r1_col, col.sort);
            } else if (col.id == "interface") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else if (col.id == "type") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else if (col.id == "speed") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "min") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "max") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "average") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "congestion_rate") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "last_value") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            }
        }

        return sortingFunctions$1.sortByName(r0.device, r1.device, col ? col.sort : null);
    }

    /* ************************************** */

    async function epoch_change() {
        if (table_snmp_usage.value) {
            table_snmp_usage.value.refresh_table(false);
        }

        if (chart.value) {
            const options = await get_chart_options();
            chart.value.update_chart_series(options?.data);
        }
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function create_config_url_link(row) {
        return `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.ip}&snmp_port_idx=${row.ifid}&page=config`
    }

    /* ************************************** */

    function click_button_timeseries(event) {
        const row = event.row;
        const epoch_begin = ntopng_url_manager.get_url_entry("epoch_begin");
        const epoch_end = ntopng_url_manager.get_url_entry("epoch_end");
        window.location.href = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.ip}&snmp_port_idx=${row.ifid}&page=historical&ifid=-1&epoch_end=${epoch_end}&epoch_begin=${epoch_begin}&timeseries_groups_mode=1_chart_x_metric&timeseries_groups=snmp_interface;-1%2B${row.ip}%2B${row.ifid};snmp_if:usage;uplink=true:false:false:false|downlink=true:false:false:false`;
    }

    /* ************************************** */

    function click_button_device_configuration(event) {
        const row = event.row;
        window.location.href = `${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?host=${row.ip}&page=config`;
    }

    /* ************************************** */

    function click_button_interface_configuration(event) {
        const row = event.row;
        window.location.href = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.ip}&snmp_port_idx=${row.ifid}&page=config`;
    }

    /* ************************************** */

    function fillEmptyValues(config) {
        if (config.data && config.data.length > 0) {
            for(let i = config.data.length; i < max_interfaces; i++) {
                config.data[i] = [i, null];
            }
        }
        return config;
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_timeseries": click_button_timeseries,
            "click_button_device_configuration": click_button_device_configuration,
            "click_button_interface_configuration": click_button_interface_configuration
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* *************************************************** */

    /* This function run the REST API with the data */
    async function get_chart_options() {
        let result = null;
        const post_params = {
            csrf: csrf,
            ifid: system_interface_id$1,
            epoch_begin: ntopng_url_manager.get_url_entry("epoch_begin"),
            epoch_end: ntopng_url_manager.get_url_entry("epoch_end"),
            host: ntopng_url_manager.get_url_entry("host"),
        };

        result = await ntopng_utility.http_post_request(base_url, post_params);
        /* Format the result in the format needed by Dygraph */
        let config = timeseriesUtils.formatSimpleSerie(result, serie_name, "bar", ["percentage"], [0, 100]);

        /* Custom options for this chart */
        config.title = '<div style="font-size:18px;">' + i18n('snmp.top_congested_devices') + '</div>';
        config.titleHeight = 48;
        config.axes.y.axisLabelWidth = 50;
        config.xAxisHeight = 6;
        config.axes.x.axisLabelWidth = 120;
        config.axes.x.pixelsPerLabel = 20;
        config.xRangePad = 40;

        localStorage.setItem(`${serie_name}_x_axis_label`, JSON.stringify(result.labels));
        localStorage.setItem(`${serie_name}_metadata`, JSON.stringify(result.metadata));
        config.axes.x.axisLabelFormatter = function (value, granularity, opts, dygraph) {
            return ''
        };

        config.axes.x.valueFormatter = function (value, granularity, opts, dygraph) {
            /* Sometimes happens that X values are approximated in DyGraph, e.g. 5 becomes 5.000001
             * In this case no label is found even if it's present, su round the value before checking the label
             */
            if (value != null) {
                const rounded_value = Number(value.toFixed(4));
                const labels_json = localStorage.getItem(`${serie_name}_x_axis_label`);
                const labels_array = JSON.parse(labels_json);
                const label = labels_array[rounded_value - 1];
                if (label)
                    return `<span style="white-space: pre-wrap">${label}</span>`

                return ''
            }
        };

        config.clickCallback = function (e, x, points) {
            // table_snmp_usage.value.search_value(x);
            const rounded_value = Number(x.toFixed(4));
            const metadata_json = localStorage.getItem(`${serie_name}_metadata`);
            const metadata_array = JSON.parse(metadata_json);
            const metadata = metadata_array[rounded_value - 1];
            if (metadata) {
                click_button_timeseries({ row: metadata });
            }
        };

        config = fillEmptyValues(config);

        return config;
    }

    /* ************************************** */

    onMounted(async () => {
        await Promise.all([
            ntopng_sync.on_ready(id_date_time_picker),
        ]);
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1e, [
        createVNode(script$2G, {
          style: {"margin-top":"0.5rem"},
          class: "ms-1",
          id: unref(id_date_time_picker),
          enable_refresh: false,
          ref_key: "date_time_picker",
          ref: date_time_picker,
          onEpoch_change: epoch_change,
          custom_time_interval_list: time_preset_list
        }, null, 8 /* PROPS */, ["id"]),
        createBaseVNode("div", _hoisted_2$14, [
          createVNode(script$2i, {
            ref_key: "chart",
            ref: chart,
            id: chart_id.value,
            chart_type: chart_type.value,
            base_url_request: base_url,
            get_custom_chart_options: get_chart_options,
            register_on_status_change: false
          }, null, 8 /* PROPS */, ["id", "chart_type"])
        ]),
        createBaseVNode("div", _hoisted_3$X, [
          createVNode(script$2w, {
            ref_key: "table_snmp_usage",
            ref: table_snmp_usage,
            table_id: table_id.value,
            csrf: unref(csrf),
            showLoading: true,
            f_map_columns: map_table_def_columns,
            get_extra_params_obj: get_extra_params_obj,
            f_sort_rows: columns_sorting,
            onCustom_event: on_table_custom_event
          }, null, 8 /* PROPS */, ["table_id", "csrf"])
        ]),
        createBaseVNode("div", _hoisted_4$V, [
          createVNode(script$2u, { note_list: note_list })
        ])
      ]))
    }
    }

    };

    var css_248z$k = "\n.dygraph-axis-label.dygraph-axis-label-x[data-v-da0f3f1a] {\n    font-size: 12px;\n    transform: rotate(-90deg) translate(-20px, 0);\n}\n";
    styleInject(css_248z$k);

    script$1m.__scopeId = "data-v-da0f3f1a";
    script$1m.__file = "http_src/vue/page-snmp-usage.vue";

    const _hoisted_1$1d = { class: "m-2 mb-3" };
    const _hoisted_2$13 = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_3$W = { class: "no-wrap d-flex align-items-center my-auto me-2 filters-label" };
    const _hoisted_4$U = { class: "d-flex justify-content-center align-items-center" };

    /* ************************************** */

    const dhcp_host_icon$2 = '<i class="fa-solid fa-bolt" title="DHCP Host"></i>';

    var script$1l = {
      __name: 'page-hosts-list',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const props = __props;

    /* ************************************** */

    const loading = ref(false);
    let table_id = props.context?.has_vlans ? ref('hosts_list_with_vlans') : ref('hosts_list');
    if (props.context.isNedge) {
        table_id = props.context?.has_vlans ? ref('nedge_hosts_list_with_vlans') : ref('nedge_hosts_list');
    }
    const table_hosts_list = ref(null);
    const csrf = props.context.csrf;
    const filter_table_array = ref([]);
    const filters = ref([]);
    const child_safe_icon = "<font color='#5cb85c'><i class='fas fa-lg fa-child' aria-hidden='true' title='" + i18n("host_pools.children_safe") + "'></i></font>";
    const system_host_icon = "<i class='fas fa-flag' title='" + i18n("system_host") + "'></i>";
    const hidden_from_top_icon = "<i class='fas fa-eye-slash' title='" + i18n("hidden_from_top_talkers") + "'></i>";
    const blacklisted_icon = "<i class='fas fa-ban fa-sm' title='" + i18n("hosts_stats.blacklisted") + "'></i>";
    const crawler_bot_scanner_host_icon = "<i class='fas fa-spider fa-sm' title='" + i18n("hosts_stats.crawler_bot_scanner") + "'></i>";
    const multicast_icon = "<abbr title='" + i18n("multicast") + "'><span class='badge bg-primary'>" + i18n("short_multicast") + "</span></abbr>";
    const localhost_icon = "<abbr title='" + i18n("details.label_local_host") + "'><span class='badge bg-success'>" + i18n("details.label_short_local_host") + "</span></abbr>";
    const remotehost_icon = "<abbr title='" + i18n("details.label_remote") + "'><span class='badge bg-secondary'>" + i18n("details.label_short_remote") + "</span></abbr>";
    const blackhole_icon = "<abbr title='" + i18n("details.label_blackhole") + "'><span class='badge bg-info'>" + i18n("details.label_short_blackhole") + "</span></abbr>";
    const blocking_quota_icon = "<i class='fas fa-hourglass' title='" + i18n("hosts_stats.blocking_traffic_policy_popup_msg") + "'></i>";
    const thpt_trend_icons = {
        1: "<i class='fas fa-arrow-up'></i>",
        2: "<i class='fas fa-arrow-down'></i>",
        3: "<i class='fas fa-minus'></i>",
    };

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "ip_address": (value, row) => {
                const host = row.host;
                let ip_address = host.ip;
                let icons = '';
                const url = `${http_prefix}/lua/host_details.lua?host=${host.ip}&vlan=${host.vlan.id}&mac=${host.mac.address}`;

                if (!dataUtils.isEmptyOrNull(host.system_host)) {
                    icons = `${icons} ${system_host_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.os)) {
                    const os_icon = osUtils$1.getOS(host.os);
                    icons = `${icons} ${os_icon.icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.device_type)) {
                    icons = `${icons} ${osUtils$1.getAssetIcon(host.device_type) || ''}`;
                }
                if (!dataUtils.isEmptyOrNull(host.hidden_from_top)) {
                    icons = `${icons} ${hidden_from_top_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.child_safe)) {
                    icons = `${icons} ${child_safe_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.dhcp_host)) {
                    icons = `${icons} ${dhcp_host_icon$2}`;
                }
                if (!dataUtils.isEmptyOrNull(host.blocking_traffic_policy)) {
                    icons = `${icons} ${blocking_quota_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.country)) {
                    icons = `${icons} <a href='${http_prefix}/lua/hosts_stats.lua?country=${host.country}'><img src='${http_prefix}/dist/images/blank.gif' class='flag flag-${host.country.toLowerCase()}'></a>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_blacklisted)) {
                    icons = `${icons} ${blacklisted_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.crawler_bot_scanner_host)) {
                    icons = `${icons} ${crawler_bot_scanner_host_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_multicast)) {
                    icons = `${icons} ${multicast_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.localhost)) {
                    icons = `${icons} ${localhost_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.remotehost)) {
                    icons = `${icons} ${remotehost_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_blackhole)) {
                    icons = `${icons} ${blackhole_icon}`;
                }

                // Strike throough in nEdge
                if (row.isBlocked) {
                    ip_address = `<span style="text-decoration: line-through">${ip_address}</span>`;
                }

                return `<a href=${url}>${ip_address}</a> ${icons}`
            },
            "num_flows": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("full_number")(value)
                }
                return ''
            },
            "alerts": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("full_number")(value) + " <i class='fas fa-exclamation-triangle' style='color: #B94A48;'></i>"
                }
                return ''
            },
            "location": (value, row) => {
                if (!dataUtils.isEmptyOrNull(value)) {
                    if (value == "unknown") {
                        return value.charAt(0).toUpperCase() + value.slice(1)
                    } else {
                        return value.toUpperCase()
                    }
                }
                return ''
            },
            "vlan": (value, row) => {
                const vlan = row.host.vlan;
                if (!dataUtils.isEmptyOrNull(vlan.name)) {
                    return vlan.name
                }
                if (!dataUtils.isEmptyOrNull(vlan.id)) {
                    return vlan.id
                }
                return ''
            },
            "num_cves": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("full_number")(value)
                }
                return ''
            },
            "tcp_unresp_as_server": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("full_number")(value)
                }
                return ''
            },
            "hostname": (hostname, row) => {
                let name = hostname.name;
                if (!dataUtils.isEmptyOrNull(hostname.alt_name)) {
                    name = hostname.alt_name;
                    if (hostname.alt_name != hostname.name && !dataUtils.isEmptyOrNull(hostname.name)) {
                        name = `${name} [${hostname.name}]`;
                    }
                }

                return name
            },
            "first_seen": (value, row) => {
                if (value > 0) {
                    return NtopUtils$1.secondsToTime((Math.round(new Date().getTime() / 1000)) - value)
                }
                return ''
            },
            "score": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("full_number")(value)
                }
                return ''
            },
            "traffic_breakdown": (value, row) => {
                const sent_pctg = row.bytes.sent * 100 / row.bytes.total;
                const rcvd_pctg = row.bytes.rcvd * 100 / row.bytes.total;
                return NtopUtils$1.createBreakdown(sent_pctg, rcvd_pctg, i18n('sent'), i18n('rcvd'))
            },
            "throughput": (value, row) => {
                let return_value = '';
                if (value.type === 'bps' && !dataUtils.isEmptyOrNull(value.bps)) {
                    return_value = formatterUtils.getFormatter("bps")(value.bps);
                } else if (value.type === 'pps' && !dataUtils.isEmptyOrNull(value.pps)) {
                    return_value = formatterUtils.getFormatter("pps")(value.pps);
                }
                if (!dataUtils.isEmptyOrNull(return_value) && !dataUtils.isEmptyOrNull(value.trend)) {
                    return_value = `${return_value} ${thpt_trend_icons[value.trend]}`;
                }
                return return_value
            },
            "bytes": (value, row) => {
                if (!dataUtils.isEmptyOrNull(value.total)) {
                    return formatterUtils.getFormatter("bytes")(value.total)
                }
                return ''
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {

                        // handle block button
                        if (b.id === "block_host") {
                            if (props.context.isNedge) {
                                current_class[0] = row.isBlocked ? "btn-danger" : "btn-secondary";
                            } else {
                                // ntopng
                                current_class[0] = "invisible";
                            }
                        }

                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function set_filters_list(res) {
        if (!res) {
            filter_table_array.value = filters.value.filter((t) => {
                if (t.show_with_key) {
                    const key = ntopng_url_manager.get_url_entry(t.show_with_key);
                    if (key !== t.show_with_value) {
                        return false
                    }
                }
                return true
            });
        } else {
            filters.value = res.map((t) => {
                const key_in_url = ntopng_url_manager.get_url_entry(t.name);
                if (key_in_url === null) {
                    ntopng_url_manager.set_key_to_url(t.name, ``);
                }
                return {
                    id: t.name,
                    label: t.label,
                    title: t.tooltip,
                    options: t.value,
                    show_with_key: t.show_with_key,
                    show_with_value: t.show_with_value,
                };
            });
            set_filters_list();
            return;
        }
        set_filter_array_label();
    }

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            /* Setting the basic label */
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            /* Getting the currently selected filter */
            const url_entry = ntopng_url_manager.get_url_entry(el.id);
            el.options.forEach((option) => {
                if (option.value.toString() === url_entry) {
                    el.current_option = option;
                }
            });
        });
    }

    /* ************************************** */

    function add_table_filter(opt) {
        ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
        refresh_table_without_loading();
        load_table_filters_array();
    }

    /* ************************************** */

    async function load_table_filters_array() {
        loading.value = true;

        const extra_params = get_extra_params_obj();
        const url_params = ntopng_url_manager.obj_to_url_params(extra_params);
        const url = `${http_prefix}/lua/rest/v2/get/host/host_filters.lua?${url_params}`;
        const res = await ntopng_utility.http_request(url);

        set_filters_list(res);

        loading.value = false;
    }

    /* ************************************** */

    function reset_filters() {
        filter_table_array.value.forEach((el, index) => {
            /* Getting the currently selected filter */
            ntopng_url_manager.set_key_to_url(el.id, ``);
        });
        load_table_filters_array();
        refresh_table();
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) { }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function create_config_url_link(row) {
        return `${http_prefix}/lua/host_details.lua?host=${row.host.ip}&vlan=${row.host.vlan.id}&page=flows`
    }

    /* ************************************** */

    function click_button_live_flows(event) {
        const row = event.row;
        window.open(create_config_url_link(row));
    }

    async function click_button_drop_host_traffic(event) {

        const host_ip = event.row.host.ip;
        const url = `${http_prefix}/lua/pro/nedge/toggle_block_host.lua?host=${host_ip}`;

        try {
            const res = await ntopng_utility.http_request(url);
            let status = res.status;

            if (status == "BLOCKED") {
                event.row.isBlocked = true;
            }
            else if (status == "UNBLOCKED") {
                event.row.isBlocked = false;
            }

        } catch (err) {
            console.error("HTTP request failed:", err);
        }
    }


    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_live_flows": click_button_live_flows,
            "click_button_drop_host_traffic": click_button_drop_host_traffic,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    function refresh_table_without_loading() {
        table_hosts_list.value.refresh_table(false);
    }

    /* ************************************** */

    function refresh_table() {
        table_hosts_list.value.refresh_table(true);
    }

    /* ************************************** */

    onBeforeMount(() => {
        load_table_filters_array();
    });

    /* ************************************** */

    onMounted(async () => {
        setInterval(refresh_table, 10000);
        set_filter_array_label();
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1d, [
        createVNode(script$2w, {
          ref_key: "table_hosts_list",
          ref: table_hosts_list,
          table_id: unref(table_id),
          csrf: unref(csrf),
          showLoading: true,
          f_map_columns: map_table_def_columns,
          get_extra_params_obj: get_extra_params_obj,
          f_sort_rows: columns_sorting,
          onCustom_event: on_table_custom_event
        }, {
          custom_header: withCtx(() => [
            (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
              return (openBlock(), createElementBlock("div", _hoisted_2$13, [
                createBaseVNode("span", _hoisted_3$W, [
                  createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                ]),
                createVNode(script$2H, {
                  selected_option: item['current_option'],
                  "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                  theme: "bootstrap-5",
                  dropdown_size: "small",
                  disabled: loading.value,
                  options: item['options'],
                  onSelect_option: add_table_filter
                }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "disabled", "options"])
              ]))
            }), 256 /* UNKEYED_FRAGMENT */)),
            createBaseVNode("div", _hoisted_4$U, [
              createBaseVNode("div", {
                class: "btn btn-sm btn-primary mt-2 me-3",
                type: "button",
                onClick: reset_filters
              }, toDisplayString(_i18n('reset')), 1 /* TEXT */),
              createVNode(script$2v, {
                show: loading.value,
                size: "1rem",
                class: "me-1"
              }, null, 8 /* PROPS */, ["show"])
            ])
          ]),
          _: 1 /* STABLE */
        }, 8 /* PROPS */, ["table_id", "csrf"])
      ]))
    }
    }

    };

    script$1l.__file = "http_src/vue/page-hosts-list.vue";

    /*
      (C) 2013-24 - ntop.org
     */

    const breed_icon = {
        Safe: "<i class='fas fa-thumbs-up'></i>",
        Acceptable: "<i class='fas fa-thumbs-up'></i>",
        Fun: "<i class='fas fa-smile'></i>",
        Unsafe: "<i class='fas fa-thumbs-down'></i>",
        Dangerous: "<i class='fas fa-exclamation-triangle'></i>"
    };

    const confidence_icons = [
        { id: -1, icon_class: "badge bg-warning" }, /* Unknown */
        { id: 0, icon_class: "badge bg-warning" }, /* Guessed */
        { id: 1, icon_class: "badge bg-success" }, /* DPI */
    ];

    const encrypted_icon = "<i class='fas fa-lock'></i>";

    /* *********************************** */

    const formatBreedIcon = function(breed, is_encrypted) {
        let icon = '';
        if(breed_icon[breed]) {
            icon = breed_icon[breed];
        }
        return is_encrypted ? `${icon} ${encrypted_icon}` : icon
    };

    /* *********************************** */

    const formatConfidence = function(confidence, confidence_id) {
        let confidence_string = '';
        confidence_icons.forEach((el) => {
            if(Number(el.id) === Number(confidence_id)) {
                confidence_string = `<span class="${el.icon_class}" title="${confidence}">${confidence}</span>`;
            }
        });
        return confidence_string
    };

    /* *********************************** */

    const protocolUtils = function () {
        return {
            formatBreedIcon,
            formatConfidence
        };
    }();

    /*
      (C) 2013-24 - ntop.org
     */

    const alert_severities = {
        none: {
            severity_id: 0,
            label: "bg-info",
            color: "#a8e4ef",
            icon: "",
            i18n_title: "alerts_dashboard.none",
            syslog_severity: 10,
        },
        debug: {
            severity_id: 1,
            label: "bg-info",
            icon: "fas fa-bug text-info",
            color: "#a8e4ef",
            i18n_title: "alerts_dashboard.debug",
            syslog_severity: 7,
            emoji: "\xE2\x84\xB9"
        },
        info: {
            severity_id: 2,
            label: "bg-info",
            icon: "fas fa-info-circle text-info",
            color: "#c1f0c1",
            i18n_title: "alerts_dashboard.info",
            syslog_severity: 6,
            used_by_alerts: true,
            emoji: "\xE2\x84\xB9"
        },
        notice: {
            severity_id: 3,
            label: "bg-info",
            icon: "fas fa-info-circle text-info",
            color: "#5cd65c",
            i18n_title: "alerts_dashboard.notice",
            syslog_severity: 5,
            used_by_alerts: true,
            emoji: "\xE2\x84\xB9"
        },
        warning: {
            severity_id: 4,
            label: "bg-warning",
            icon: "fas fa-exclamation-triangle text-warning",
            color: "#ffc007",
            i18n_title: "alerts_dashboard.warning",
            syslog_severity: 4,
            used_by_alerts: true,
            emoji: "\xE2\x9A\xA0"
        },
        error: {
            severity_id: 5,
            label: "bg-danger",
            icon: "fas fa-exclamation-triangle text-danger",
            color: "#ff3231",
            i18n_title: "alerts_dashboard.error",
            syslog_severity: 3,
            used_by_alerts: true,
            emoji: "\xE2\x9D\x97"
        },
        critical: {
            severity_id: 6,
            label: "bg-danger",
            icon: "fas fa-exclamation-triangle text-danger",
            color: "#fb6962",
            i18n_title: "alerts_dashboard.critical",
            syslog_severity: 2,
            emoji: "\xE2\x9D\x97"
        },
        emergency: {
            severity_id: 8,
            label: "bg-danger text-danger",
            icon: "fas fa-bomb text-danger",
            color: "#fb6962",
            i18n_title: "alerts_dashboard.emergency",
            syslog_severity: 0,
            emoji: "\xF0\x9F\x9A\xA9"
        }
    };

    /* *********************************** */

    const getSeverityIcon = function (severity_id) {
        for (const [_, value] of Object.entries(alert_severities)) {
            if (Number(severity_id) == Number(value.severity_id)) {
                return value.icon
            }
        }
    };

    /* *********************************** */

    const getSeverityColor = function (severity_id) {
        for (const [_, value] of Object.entries(alert_severities)) {
            if (Number(severity_id) == Number(value.severity_id)) {
                return value.color
            }
        }
    };

    /* *********************************** */

    const mapScoreToSeverity = function (score) {
        if (score < 1)
            return alert_severities.none.severity_id;
        else if (score < 10)
            return alert_severities.info.severity_id;
        else if (score < 50)
            return alert_severities.notice.severity_id;
        else if (score < 100)
            return alert_severities.warning.severity_id;
        else if (score < 150)
            return alert_severities.error.severity_id;
        else if (score < 200)
            return alert_severities.critical.severity_id;
        else
            return alert_severities.emergency.severity_id;
    };

    /* *********************************** */

    const alertSeverities = function () {
        return {
            getSeverityIcon,
            mapScoreToSeverity,
            getSeverityColor
        };
    }();

    const _hoisted_1$1c = { class: "m-2 mb-3" };
    const _hoisted_2$12 = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_3$V = { class: "no-wrap d-flex align-items-center filters-label" };
    const _hoisted_4$T = { class: "d-flex justify-content-center align-items-center" };

    /* ************************************** */

    const refresh_rate = 10000;
    const flow_exporter_icon = "<i class='fas fa-file-export'></i>";
    const host_details_icon = "<i class='fas fa-laptop'></i>";
    const dhcp_host_icon$1 = '<i class="fa-solid fa-bolt" data-bs-toggle="tooltip" data-bs-placement="bottom" title="DHCP Host"></i>';

    var script$1k = {
      __name: 'page-flows-list',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const props = __props;

    /* ************************************** */

    // conditionally render tables
    const table_id = computed$1(() => {
        if (props.context?.is_enterprise_l) {
            if (props.context?.ASNModeEnabled) {
                return 'flows_list_with_exporters_enterprise_l_ixp_mode'
            } else if (props.context?.has_exporters) {
                return 'flows_list_with_exporters_enterprise_l'
            } else {
                return 'flows_list_enterprise_l'
            }
        } else {
            if (props.context?.has_exporters) {
                return 'flows_list_with_exporters'
            } else {
                return 'flows_list'
            }
        }
    });

    const table_flows_list = ref(null);
    const csrf = props.context.csrf;
    //const chart = ref(null);
    const filter_table_array = ref([]);
    const filters = ref([]);
    const host_details_url = `${http_prefix}/lua/host_details.lua`;
    const flow_exporter_url = `${http_prefix}/lua/pro/enterprise/exporters.lua`;
    const snmp_iface_url = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua`;
    const asn_hosts_url = `${http_prefix}/lua/hosts_stats.lua?asn=`;

    const child_safe_icon = "<font color='#5cb85c'><i class='fas fa-lg fa-child' aria-hidden='true' data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("host_pools.children_safe") + "'></i></font>";
    const system_host_icon = "<i class='fas fa-flag' data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("system_host") + "'></i>";
    const hidden_from_top_icon = "<i class='fas fa-eye-slash' data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("hidden_from_top_talkers") + "'></i>";
    const blacklisted_icon = "<i class='fas fa-ban fa-sm' data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("hosts_stats.blacklisted") + "'></i>";
    const crawler_bot_scanner_host_icon = "<i class='fas fa-spider fa-sm' data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("hosts_stats.crawler_bot_scanner") + "'></i>";
    const multicast_icon = "<abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("multicast") + "'><span class='badge bg-primary'>" + i18n("short_multicast") + "</span></abbr>";
    const localhost_icon = "<abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_local_host") + "'><span class='badge bg-success'>" + i18n("details.label_short_local_host") + "</span></abbr>";
    const remotehost_icon = "<abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_remote") + "'><span class='badge bg-secondary'>" + i18n("details.label_short_remote") + "</span></abbr>";
    const blackhole_icon = "<abbr data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("details.label_blackhole") + "'><span class='badge bg-info'>" + i18n("details.label_short_blackhole") + "</span></abbr>";
    const blocking_quota_icon = "<i class='fas fa-hourglass' data-bs-toggle='tooltip' data-bs-placement='bottom' title='" + i18n("hosts_stats.blocking_traffic_policy_popup_msg") + "'></i>";
    const thpt_trend_icons = {
        1: "<i class='fas fa-arrow-up'></i>",
        2: "<i class='fas fa-arrow-down'></i>",
        3: "<i class='fas fa-minus'></i>",
    };
    const loading = ref(false);
    const interval_id = ref(null);

    /* ************************************** */

    const format_host = function (value) {
        let icons = '';
        let port_name = ` : ${value.port}`;
        let process = '';
        let container = '';
        const url = `${host_details_url}?host=${value.ip}&vlan=${value.vlan || ''}`;

        if (!dataUtils.isEmptyOrNull(value.system_host)) {
            icons = `${icons} ${system_host_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.os)) {
            const os_icon = osUtils.getOS(value.os);
            icons = `${icons} ${os_icon.icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.device_type)) {
            icons = `${icons} ${osUtils.getAssetIcon(value.device_type) || ''}`;
        }
        if (!dataUtils.isEmptyOrNull(value.hidden_from_top)) {
            icons = `${icons} ${hidden_from_top_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.child_safe)) {
            icons = `${icons} ${child_safe_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.dhcp_host)) {
            icons = `${icons} ${dhcp_host_icon$1}`;
        }
        if (!dataUtils.isEmptyOrNull(value.blocking_traffic_policy)) {
            icons = `${icons} ${blocking_quota_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.country)) {
            icons = `${icons} <a href='${http_prefix}/lua/hosts_stats.lua?country=${value.country}'><img src='${http_prefix}/dist/images/blank.gif' class='flag flag-${value.country.toLowerCase()}'></a>`;
        }
        if (!dataUtils.isEmptyOrNull(value.is_blacklisted)) {
            icons = `${icons} ${blacklisted_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.crawler_bot_scanner_host)) {
            icons = `${icons} ${crawler_bot_scanner_host_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.is_multicast)) {
            icons = `${icons} ${multicast_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.localhost)) {
            icons = `${icons} ${localhost_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.remotehost)) {
            icons = `${icons} ${remotehost_icon}`;
        }
        if (!dataUtils.isEmptyOrNull(value.is_blackhole)) {
            icons = `${icons} ${blackhole_icon}`;
        }
        if (value.port !== 0) {
            const show_port_number = true;
            const p = format_port(value.port, value.service_port, show_port_number);
            const title = p[0];
            const hover = p[1];
            port_name = ` : <a href="#" class="tableFilter" tag-filter="port" data-bs-toggle='tooltip' data-bs-placement='bottom' title='${hover}' tag-value="${value.port}">${title}</a>`;
        } else {
            port_name = '';
        }
        if (!dataUtils.isEmptyOrNull(value.process.name)) {
            process = ` <a href="${http_prefix}/lua/process_details.lua?host=${value.ip}&vlan=${value.vlan}&pid_name=${value.process.pid_name}&pid=${value.process.pid}"><i class='fas fa-terminal'></i> ${value.process.process_name}</a>`;
        }
        if (!dataUtils.isEmptyOrNull(value.container.id) && (value.container.id !== 0) && (value.container.id !== '')) {
            container = ` <a href="${http_prefix}/lua/flows_stats.lua?container=${value.container.id}"><i class='fas fa-ship'></i> ${value.container.name}</a>`;
        }
        if (props.context.is_viewed) {
            return `<a href="#" class="tableFilter" tag-filter="flowhosts_type" data-bs-toggle='tooltip' data-bs-placement='bottom' title='${value.ip}' tag-value="${value.ip}@${value.vlan || 0}">${value.name}</a> ${icons}${port_name}${process}${container}`
        } else {
            return `<a href="#" class="tableFilter" tag-filter="flowhosts_type" data-bs-toggle='tooltip' data-bs-placement='bottom' title='${value.ip}' tag-value="${value.ip}@${value.vlan || 0}">${value.name}</a> ${icons} <a href=${url}>${host_details_icon}</a>${port_name}${process}${container}`
        }
    };

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "flow": (value, row) => {
                const client = format_host(row.client);
                const server = format_host(row.server);

                let flow_label = `${client} <i class="fas fa-exchange-alt fa-lg" aria-hidden="true"></i> ${server}`;
                // strikethrough if verdict is to drop
                flow_label = ((row.verdict != undefined) && (!row.verdict)) ? ` <strike>${flow_label}</strike>` : `${flow_label}`;

                return flow_label
            },
            "protocol": (value, row) => {
                value = row.application;
                const name = ((row.verdict != undefined) && (!row.verdict)) ? ` <strike>${value.name}</strike>` : `${value.name}`;
                const l7_proto_id = (dataUtils.isEmptyOrNull(value.master_id) || value.master_id === value.app_id) ? value.app_id : `${value.master_id}.${value.app_id}`;
                const application = `<a href="#" class="tableFilter" tag-filter="application" tag-value="${l7_proto_id}">${name} ${protocolUtils.formatBreedIcon(value.breed, value.encrypted)}</a> ${protocolUtils.formatConfidence(value.confidence, value.confidence_id)}`;
                value = row.l4_proto;
                let proto = "";
                if (value && value.name) {
                    proto = ((row.verdict != undefined) && (!row.verdict)) ? ` <strike>${value.name}</strike>` : `${value.name}`;
                }
                proto = `<a href="#" class="tableFilter" tag-filter="l4proto" tag-value="${value.id}">${proto}</a>`;
                if (row.l4_proto.name == "UDP" || row.l4_proto.name == "TCP") {
                    return `${proto}:${application}`
                }
                else {
                    return `${proto}`

                }
            },
            "proto": (value, row) => {
                if (value) {
                    const name = ((row.verdict != undefined) && (!row.verdict)) ? ` <strike>${value}</strike>` : `${value}`;
                    return name
                }
                return ""
            },
            "first_seen": (value, row) => {
                if (value > 0) {
                    return NtopUtils$1.secondsToTime((Math.round(new Date().getTime() / 1000)) - value)
                }
                return ''
            },
            "last_seen": (value, row) => {
                if (value > 0) {
                    return NtopUtils$1.secondsToTime((Math.round(new Date().getTime() / 1000)) - value)
                }
                return ''
            },
            "srv_asn": (value, row) => {
                if (value.asn && value.name) {
                    let formatted_asn = formatterUtils.formatAsn(value.asn, value.name);
                    return `<a href="${asn_hosts_url}${value.asn}">${formatted_asn}</a>`
                }

                return ''
            },
            "cli_asn": (value, row) => {
                if (value.asn && value.name) {
                    let formatted_asn = formatterUtils.formatAsn(value.asn, value.name);
                    return `<a href="${asn_hosts_url}${value.asn}">${formatted_asn}</a>`
                }

                return ''
            },
            "transit_asn": (value, row) => {
                if (value.asn && value.name) {
                    let formatted_asn = formatterUtils.formatAsn(value.asn, value.name);
                    return `<a href="${asn_hosts_url}${value.asn}">${formatted_asn}</a>`
                }

                return ''
            },
            "score": (value, row) => {
                if (value > 0) {
                    let danger_icon = '';
                    let severity = alertSeverities.mapScoreToSeverity(value);
                    let score_color = alertSeverities.getSeverityColor(severity);
                    return `<span style="color:${score_color} !important">${formatterUtils.getFormatter("full_number")(value)}${danger_icon}</span>`
                }
                return ''
            },
            "traffic_breakdown": (value, row) => {
                const cli_bytes_pctg = row.bytes.cli_bytes * 100 / row.bytes.total;
                const srv_bytes_pctg = (row.bytes.total - row.bytes.cli_bytes) * 100 / row.bytes.total;
                return NtopUtils$1.createBreakdown(cli_bytes_pctg, srv_bytes_pctg, i18n('client'), i18n('server'))
            },
            "throughput": (value, row) => {
                let return_value = '';
                if (value.type === 'bps' && !dataUtils.isEmptyOrNull(value.bps)) {
                    return_value = formatterUtils.getFormatter("bps_no_scale")(value.bps);
                } else if (value.type === 'pps' && !dataUtils.isEmptyOrNull(value.pps)) {
                    return_value = formatterUtils.getFormatter("pps")(value.pps);
                }
                if (!dataUtils.isEmptyOrNull(return_value) && !dataUtils.isEmptyOrNull(value.trend)) {
                    return_value = `${return_value} ${thpt_trend_icons[value.trend]}`;
                }
                return return_value
            },
            "bytes": (value, row) => {
                if (!dataUtils.isEmptyOrNull(value.total)) {
                    return formatterUtils.getFormatter("bytes")(value.total)
                }
                return ''
            },
            "info": (value, row) => {
                let info = '';
                if (!dataUtils.isEmptyOrNull(value)) {
                    info = infoUtils.addFlowInfoIcon(value);
                    if (row.periodic_flow) {
                        const periodic_map_url = `${http_prefix}/lua/pro/enterprise/network_maps.lua?map=periodicity_map&page=table`;
                        const address = row.client.mac ? row.client.mac : row.client.host;
                        info = `${value} <a href="${periodic_map_url}&host=${address}&l7proto=${row.application.name}"><span class="badge bg-warning text-dark">${i18n("periodic_flow")}</span></a>`;
                    }
                    if (row.application.http_method) {
                        let span_mode = "warning";
                        let color_class = "badge bg-danger";
                        let status_code = '';
                        if (row.application.http_method == "GET") {
                            span_mode = "success";
                        }
                        if (row.application.return_code < 400) {
                            color_class = "badge bg-success";
                        }
                        if (row.application.return_code > 0) {
                            status_code = `<span class="${color_class}">${row.application.rsp_status_code}</span>`;
                        }
                        info = `<span class="badge bg-${span_mode}">${row.application.http_method}</span> ${status_code} ${info}`;
                    }
                }
                return info
            },
            "flow_exporter": (value) => {
                if (!dataUtils.isEmptyOrNull(value)) {
                    let ifid = '';
                    let ifid_name = '';
                    let tag_filter2 = '';
                    if (value.seen_on_interface) {
                        ifid = `&ifid=${value.seen_on_interface.id}`;
                        ifid_name = ` [${value.seen_on_interface.name}]`;
                        tag_filter2 = ` tag-filter2="interface_filter" tag-value2="${value.seen_on_interface.id}" `;
                    }

                    return `<a href="${flow_exporter_url}?probe_uuid=${value.device.probe_uuid}${ifid}">${flow_exporter_icon}</a> <a href="#" class="tableFilter" data-bs-toggle='tooltip' data-bs-placement='bottom' title='${value.device.label}' tag-filter="deviceIP" tag-value="${value.device.ip}" ${tag_filter2}>${value.device.name}${ifid_name}</a>`
                }
                return ''
            },
            "qoe": (value) => {
                /* The value is between 0 and 100 */
                if (value && value > 0 && value <= 100) {
                    return dataUtils$1.QoEQualityIcon(value)
                }
                return '';
            },
            "in_index": (value, row) => {
                if (!dataUtils.isEmptyOrNull(row.flow_exporter)) {
                    let ifid = '';
                    let tag_filter3 = '';
                    let name = row.flow_exporter.in_port.name;
                    if (name !== row.flow_exporter.in_port.index) {
                        name = `${name} [${row.flow_exporter.in_port.index}]`;
                    }
                    if (row.flow_exporter.seen_on_interface) {
                        ifid = `&ifid=${row.flow_exporter.seen_on_interface.id}`;
                        tag_filter3 = ` tag-filter3="interface_filter" tag-value3="${row.flow_exporter.seen_on_interface.id}" `;
                    }

                    return `<a href="${snmp_iface_url}?host=${row.flow_exporter.device.ip}&snmp_port_idx=${row.flow_exporter.in_port.index}"${ifid}>${flow_exporter_icon}</a> <a href="#" class="tableFilter" tag-filter="inIfIdx" tag-filter2="deviceIP" tag-value="${row.flow_exporter.in_port.index}" tag-value2="${row.flow_exporter.device.ip}"${tag_filter3}>${name}</a>`
                }
                return ''
            },
            "out_index": (value, row) => {
                if (!dataUtils.isEmptyOrNull(row.flow_exporter)) {
                    let ifid = '';
                    let tag_filter3 = '';
                    let name = row.flow_exporter.out_port.name;
                    if (name !== row.flow_exporter.out_port.index) {
                        name = `${name} [${row.flow_exporter.out_port.index}]`;
                    }
                    if (row.flow_exporter.seen_on_interface) {
                        ifid = `&ifid=${row.flow_exporter.seen_on_interface.id}`;
                        tag_filter3 = ` tag-filter3="interface_filter" tag-value3="${row.flow_exporter.seen_on_interface.id}" `;
                    }
                    return `<a href="${snmp_iface_url}?host=${row.flow_exporter.device.ip}&snmp_port_idx=${row.flow_exporter.out_port.index}"${ifid}>${flow_exporter_icon}</a> <a href="#" class="tableFilter" tag-filter="outIfIdx" tag-filter2="deviceIP" tag-value="${row.flow_exporter.out_port.index}" tag-value2="${row.flow_exporter.device.ip}"${tag_filter3}>${name}</a>`
                }
                return ''
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];

            if (c.id == "actions") {
                const visible_dict = {
                    block_host: props.context.isNedge,
                    historical_chart: props.context.is_clickhouse_enabled && !props.context.is_pcap,
                };
                c.button_def_array.forEach((b, pos) => {
                    if ((b.id === "block_host") && (!props.context.isNedge)) {
                        c.button_def_array.splice(pos, 1);
                    }
                    b.f_map_class = (current_class, row) => {
                        if (visible_dict[b.id] != null && visible_dict[b.id] == false) {
                            current_class.push("disabled");
                        } else if (row.last_seen - row.first_seen < 310 /* 5 minutes and 10 seconds */ &&
                            visible_dict[b.id] != null && visible_dict[b.id] === true && b.id === "historical_chart") {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    /* ************************************** */
    // format_port returns the display value as the first element and the hover as the second
    function format_port(port_number, service_name, show_number)
    {
        if (show_number)
            return [port_number, service_name]
        else return [service_name, port_number]
    }

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            /* Setting the basic label */
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            /* Getting currently selected filter */
            let url_entry = ntopng_url_manager.get_url_entry(el.id);

            el.options.forEach((option) => {
                if ('value' in option && option.value.toString() === url_entry) {
                    el.current_option = option;
                }
            });
        });
    }

    /* ************************************** */

    function add_filter_from_table_element(e) {
        const value = e.target.getAttribute("tag-value");
        const filter = e.target.getAttribute("tag-filter");
        const value2 = e.target.getAttribute("tag-value2");
        const filter2 = e.target.getAttribute("tag-filter2");
        const value3 = e.target.getAttribute("tag-value3");
        const filter3 = e.target.getAttribute("tag-filter3");
        add_table_filter({
            key: filter,
            value: value
        }, (filter2) ? {
            key: filter2,
            value: value2
        } : null, (filter3) ? {
            key: filter3,
            value: value3
        } : null);
    }

    /* ************************************** */

    function add_filters_to_rows() {
        const filters = document.querySelectorAll('.tableFilter');
        filters.forEach(filter => {
            filter.addEventListener('click', add_filter_from_table_element);
        });
    }

    /* ************************************** */

    function change_filter_labels() {
        add_filters_to_rows();
    }

    /* ************************************** */

    function add_table_filter(opt, opt2, opt3) {
        ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
        if (opt2) {
            ntopng_url_manager.set_key_to_url(opt2.key, `${opt2.value}`);
        }
        if (opt3) {
            ntopng_url_manager.set_key_to_url(opt3.key, `${opt3.value}`);
        }
        refresh_table();
        load_table_filters_array();
    }

    /* ************************************** */

    function set_filters_list(res) {
        if (!res) {
            filter_table_array.value = filters.value.filter((t) => {
                if (t.show_with_key) {
                    const key = ntopng_url_manager.get_url_entry(t.show_with_key);
                    if (key !== t.show_with_value) {
                        return false
                    }
                }
                return true
            });
        } else {
            filters.value = res.map((t) => {
                const key_in_url = ntopng_url_manager.get_url_entry(t.name);
                if (key_in_url === null) {
                    ntopng_url_manager.set_key_to_url(t.name, ``);
                }
                return {
                    id: t.name,
                    label: t.label,
                    title: t.tooltip,
                    options: t.value,
                    show_with_key: t.show_with_key,
                    show_with_value: t.show_with_value,
                };
            });
            set_filters_list();
            return;
        }
        set_filter_array_label();
    }

    /* ************************************** */

    async function load_table_filters_array() {
        /* Clear the interval 2 times just in case, being this function async, 
            it could happen some strange behavior */
        clearInterval(interval_id.value);
        loading.value = true;
        let extra_params = get_extra_params_obj();
        let url_params = ntopng_url_manager.obj_to_url_params(extra_params);

        const url = `${http_prefix}/lua/rest/v2/get/flow/flow_filters.lua?${url_params}`;
        let res = await ntopng_utility.http_request(url);

        set_filters_list(res);
        loading.value = false;
        clearInterval(interval_id.value);
        interval_id.value = setInterval(periodicRefresh, refresh_rate);
    }

    /* ************************************** */

    function reset_filters() {
        filter_table_array.value.forEach((el, index) => {
            /* Getting the currently selected filter */
            ntopng_url_manager.set_key_to_url(el.id, ``);
        });
        load_table_filters_array();
        refresh_table();
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "ip") {
                return sortingFunctions.sortByIP(r0.ip, r1.ip, col.sort);
            } else if (col.id == "name") {
                return sortingFunctions.sortByName(r0.name, r1.name, col.sort);
            } else if (col.id == "ntopng_interface") {
                return sortingFunctions.sortByIP(r0.ntopng_interface, r1.ntopng_interface, col.sort);
            } else if (col.id == "probe_ip") {
                return sortingFunctions.sortByIP(r0.probe_ip, r1.probe_ip, col.sort);
            } else if (col.id == "export_type") {
                return sortingFunctions.sortByName(r0.export_type, r1.export_type, col.sort);
            } else if (col.id == "exporter_interfaces") {
                return sortingFunctions.sortByNumber(r0.exporter_interfaces, r1.exporter_interfaces, col.sort);
            } else if (col.id == "time_last_used") {
                return sortingFunctions.sortByName(r0.name, r1.name, col.sort);
            } else if (col.id == "exported_flows") {
                return sortingFunctions.sortByNumber(r0.exported_flows, r1.exported_flows, col.sort);
            } else if (col.id == "dropped_flows") {
                return sortingFunctions.sortByNumber(r0.dropped_flows, r1.dropped_flows, col.sort);
            } else if (col.id == "interface_name") {
                return sortingFunctions.sortByName(r0.interface_name, r1.interface_name, col.sort);
            }
        }
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();

        return extra_params;
    };

    /* ************************************** */

    function create_config_url_link(row) {
        return `${http_prefix}/lua/flow_details.lua?flow_key=${row.key}&flow_hash_id=${row.hash_id}`
    }

    /* ************************************** */

    function click_button_live_flows(event) {
        const row = event.row;
        window.open(create_config_url_link(row));
    }

    /* ************************************** */

    function click_button_drop_host_traffic(event) {
        const row = event.row;
        const url_params = ntopng_url_manager.obj_to_url_params({
            flow_key: row.key,
            flow_hash_id: row.hash_id
        });

        const url = `${http_prefix}/lua/pro/rest/v2/get/flow/nedge/block_flow.lua?${url_params}`;
        ntopng_utility.http_request(url).then(data => {
            refresh_table();
        });
    }

    /* ************************************** */

    function click_button_historical_data(event) {
        const row = event.row;
        let cli_port = "";
        let srv_port = "";
        if (!dataUtils.isEmptyOrNull(row.client.port)) {
            cli_port = `cli_port=${row.client.port};eq`;
        }
        if (!dataUtils.isEmptyOrNull(row.server.port)) {
            srv_port = `srv_port=${row.server.port};eq`;
        }
        /* Use 30 seconds more and 30 less */
        window.open(`${http_prefix}/lua/pro/db_search.lua?ifid=${row.ifid}&epoch_begin=${row.first_seen - 30}&epoch_end=${row.last_seen + 30}&l7proto=${row.application.app_id};eq&cli_ip=${row.client.ip};eq&srv_ip=${row.server.ip};eq&${cli_port}&${srv_port}&aggregated=false&query_preset=&count=THROUGHPUT`);
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_live_flows": click_button_live_flows,
            "click_button_historical_data": click_button_historical_data,
            "click_button_drop_host_traffic": click_button_drop_host_traffic,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    function refresh_table() {
        table_flows_list.value.refresh_table(false);
    }

    /* ************************************** */

    function periodicRefresh() {
        table_flows_list.value.refresh_table(true);
    }

    /* ************************************** */

    onBeforeMount(() => {
        load_table_filters_array();
    });

    /* ************************************** */

    onMounted(() => {
        clearInterval(interval_id.value);
        interval_id.value = setInterval(periodicRefresh, refresh_rate);
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1c, [
        createVNode(script$2w, {
          ref_key: "table_flows_list",
          ref: table_flows_list,
          table_id: table_id.value,
          csrf: unref(csrf),
          f_map_columns: map_table_def_columns,
          showLoading: true,
          get_extra_params_obj: get_extra_params_obj,
          f_sort_rows: columns_sorting,
          onCustom_event: on_table_custom_event,
          onRows_loaded: change_filter_labels
        }, {
          custom_header: withCtx(() => [
            (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
              return (openBlock(), createElementBlock("div", _hoisted_2$12, [
                createBaseVNode("span", _hoisted_3$V, [
                  createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                ]),
                createVNode(script$2H, {
                  selected_option: item['current_option'],
                  "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                  theme: "bootstrap-5",
                  dropdown_size: "small",
                  disabled: loading.value,
                  options: item['options'],
                  onSelect_option: add_table_filter
                }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "disabled", "options"])
              ]))
            }), 256 /* UNKEYED_FRAGMENT */)),
            createBaseVNode("div", _hoisted_4$T, [
              createBaseVNode("div", {
                class: "btn btn-sm btn-primary mt-2 me-3",
                type: "button",
                onClick: reset_filters
              }, toDisplayString(_i18n('reset')), 1 /* TEXT */),
              createVNode(script$2v, {
                show: loading.value,
                size: "1rem",
                class: "me-1"
              }, null, 8 /* PROPS */, ["show"])
            ])
          ]),
          _: 1 /* STABLE */
        }, 8 /* PROPS */, ["table_id", "csrf"])
      ]))
    }
    }

    };

    script$1k.__file = "http_src/vue/page-flows-list.vue";

    const _hoisted_1$1b = { class: "m-2 mb-3" };
    const _hoisted_2$11 = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_3$U = { class: "no-wrap d-flex align-items-center my-auto me-2 filters-label" };
    const _hoisted_4$S = { class: "d-flex justify-content-center align-items-center" };

    /* ************************************** */

    const workstation_icon = '<i class="fas fa-desktop fa-lg devtype-icon" aria-hidden="true"></i>';
    const networking_icon = '<i class="fas fa-arrows-alt fa-lg devtype-icon" aria-hidden="true"></i>';
    const tv_icon = '<i class="fas fa-tv fa-lg devtype-icon" aria-hidden="true"></i>';
    const printer_icon = '<i class="fas fa-print fa-lg devtype-icon" aria-hidden="true"></i>';
    const iot_icon = '<i class="fas fa-thermometer fa-lg devtype-icon" aria-hidden="true"></i>';
    const multimedia_icon = '<i class="fas fa-music fa-lg devtype-icon" aria-hidden="true"></i>';
    const tablet_icon = '<i class="fas fa-tablet fa-lg devtype-icon" aria-hidden="true"></i>';
    const video_icon = '<i class="fas fa-video fa-lg devtype-icon" aria-hidden="true"></i>';
    const wifi_icon = '<i class="fas fa-wifi fa-lg devtype-icon" aria-hidden="true"></i>';
    const laptop_icon = '<i class="fas fa-laptop fa-lg devtype-icon" aria-hidden="true"></i>';
    const phone_icon = '<i class="fas fa-mobile fa-lg devtype-icon" aria-hidden="true"></i>';
    const nas_icon = '<i class="fas fa-database fa-lg devtype-icon" aria-hidden="true"></i>';

    /* ************************************** */


    var script$1j = {
      __name: 'page-macs-list',
      props: { context: Object },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const props = __props;

    /* ************************************** */
    const loading = ref(false);
    const mac_filters_key = ref(0);
    const table_id = ref(props.context.isnEdge ? "nedge_macs_list" : "macs_list");
    const table_macs_list = ref(null);
    const filter_table_array = ref([]);
    const csrf = props.context.csrf;

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "mac": (value, row) => {
                let mac_details_url = `${http_prefix}/lua/mac_details.lua?host=${value}`;

                return `<a href=${mac_details_url}>${value}</a>`
            },
            "manufacturer": (value, row) => {
                return value
            },
            "device_type": (value, row) => {
                let device_type_label = value.device_type_label;
                let icons = '';
                if (!dataUtils.isEmptyOrNull(value.workstation)) {
                    icons = `${icons} ${workstation_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.networking)) {
                    icons = `${icons} ${networking_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.tv)) {
                    icons = `${icons} ${tv_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.printer)) {
                    icons = `${icons} ${printer_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.iot)) {
                    icons = `${icons} ${iot_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.multimedia)) {
                    icons = `${icons} ${multimedia_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.tablet)) {
                    icons = `${icons} ${tablet_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.video)) {
                    icons = `${icons} ${video_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.wifi)) {
                    icons = `${icons} ${wifi_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.laptop)) {
                    icons = `${icons} ${laptop_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.phone)) {
                    icons = `${icons} ${phone_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(value.nas)) {
                    icons = `${icons} ${nas_icon}`;
                }

                return `${device_type_label} ${icons}`
            },
            "name": (value, row) => {
                if(value.has_name === false){
                    if(value.num_hosts > 0){
                        let url_first_device = `${http_prefix}/lua/host_details.lua?host=${value.host_label}`;
                        let url_mac = `${http_prefix}/lua/hosts_stats.lua?mac=${row.mac}`;
                        if(value.num_hosts == 1){
                            return `<a href=${url_first_device}>${value.host_label}</a>`
                        }
                        else if(value.num_hosts > 1) {
                            return `<a href=${url_first_device}>${value.host_label}</a> and <a href=${url_mac}>${value.num_hosts-1} more host(s)</a>`
                        }
                    }
                    else return ''
                }
                else return value.name
            },
            "hosts": (value, row) => {
                return formatterUtils.getFormatter("full_number")(value)
            },
            "location": (value, row) => {
                if (!dataUtils.isEmptyOrNull(value)) {
                    return value.toUpperCase()
                }
                return ''
            },
            "arp": (value, row) => {
                return formatterUtils.getFormatter("full_number")(value)
            },
            "seen_since": (value, row) => {
                if (value > 0) {
                    return NtopUtils.secondsToTime((Math.round(new Date().getTime() / 1000)) - value)
                }
                return ''
            },
            "breakdown": (value, row) => {
                let traffic = row.bytes_sent + row.bytes_rcvd;
                const sent_pctg = (row.bytes_sent / traffic) * 100;
                const rcvd_pctg = (row.bytes_rcvd / traffic) * 100;
                return NtopUtils.createBreakdown(sent_pctg, rcvd_pctg, _i18n('sent'), _i18n('rcvd'))
            },
            "throughput": (value, row) => {
                let return_value = '';
                if (row.throughput_type === 'bps' && !dataUtils.isEmptyOrNull(value)) {
                    return_value = formatterUtils.getFormatter("bps")(value);
                } else if (row.throughput_type === 'pps' && !dataUtils.isEmptyOrNull(value)) {
                    return_value = formatterUtils.getFormatter("pps")(value);
                }
                return return_value
            },
            "traffic": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value)
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });

        return columns;
    };

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            /* Setting the basic label */
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            /* Getting the currently selected filter */
            const url_entry = ntopng_url_manager.get_url_entry(el.id);
            el.options.forEach((option) => {
                if (option.value.toString() === url_entry) {
                    el.current_option = option;
                }
            });
        });
    }

    /* ************************************** */

    function change_filter_labels() {
        set_filter_array_label();
    }

    /* ************************************** */

    async function add_table_filter(opt) {
        ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
        set_filter_array_label();
        table_macs_list.value.refresh_table();
        filter_table_array.value = await load_table_filters_array();
    }

    /* ************************************** */

    async function load_table_filters_array() {
        loading.value = true;
        let extra_params = get_extra_params_obj();
        let url_params = ntopng_url_manager.obj_to_url_params(extra_params);
        const url = `${http_prefix}/lua/rest/v2/get/mac/mac_filters.lua?${url_params}`;
        let res = await ntopng_utility.http_request(url);
        mac_filters_key.value = mac_filters_key.value + 1;
        loading.value = false;

        return res.map((t) => {
            const key_in_url = ntopng_url_manager.get_url_entry(t.name);
            if (dataUtils.isEmptyOrNull(key_in_url)) {
                ntopng_url_manager.set_key_to_url(t.name, ``);
            }
            return {
                id: t.name,
                label: t.label,
                title: t.tooltip,
                options: t.value,
                hidden: (t.value.length == 1)
            };
        });
    }

    /* ************************************** */

    function reset_filters() {
        filter_table_array.value.forEach((el, index) => {
            /* Getting the currently selected filter */
            ntopng_url_manager.set_key_to_url(el.id, ``);
        });
        load_table_filters_array();
        refresh_table();
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
          if (col.id == "manufacturer") {
          return sortingFunctions$1.sortByName(r0.manufacturer, r1.manufacturer, col.sort);
        } else if (col.id == "mac") {
          return sortingFunctions$1.sortByMacAddress(r0.mac, r1.mac, col.sort);
        } else if (col.id == "location") {
          return sortingFunctions$1.sortByName(r0.location, r1.location, col.sort);
        } else if (col.id == "hosts") {
          return sortingFunctions$1.sortByNumber(r0.hosts, r1.hosts, col.sort);
        } else if (col.id == "arp") {
          return sortingFunctions$1.sortByNumber(r0.arp, r1.arp, col.sort);
        } else if (col.id == "seen_since") {
          return sortingFunctions$1.sortByNumber(r0.seen_since, r1.seen_since, col.sort);
        } else if (col.id == "throughput") {
          return sortingFunctions$1.sortByNumber(r0.throughput, r1.throughput, col.sort);
        } else if (col.id == "traffic") {
          return sortingFunctions$1.sortByNumber(r0.traffic, r1.traffic, col.sort);
        }}
     }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function refresh_table() {
        table_macs_list.value.refresh_table(false);
    }

    /* ************************************** */

    onMounted(async () => {
        filter_table_array.value = await load_table_filters_array();
        set_filter_array_label();
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$1b, [
        createVNode(script$2w, {
          ref_key: "table_macs_list",
          ref: table_macs_list,
          table_id: table_id.value,
          csrf: unref(csrf),
          showLoading: true,
          f_map_columns: map_table_def_columns,
          get_extra_params_obj: get_extra_params_obj,
          f_sort_rows: columns_sorting,
          onRows_loaded: change_filter_labels
        }, {
          custom_header: withCtx(() => [
            (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
              return (openBlock(), createElementBlock("div", _hoisted_2$11, [
                createBaseVNode("span", _hoisted_3$U, [
                  createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                ]),
                createVNode(script$2H, {
                  selected_option: item['current_option'],
                  "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                  theme: "bootstrap-5",
                  dropdown_size: "small",
                  disabled: loading.value,
                  options: item.options,
                  onSelect_option: add_table_filter
                }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "disabled", "options"])
              ]))
            }), 256 /* UNKEYED_FRAGMENT */)),
            createBaseVNode("div", _hoisted_4$S, [
              createBaseVNode("div", {
                class: "btn btn-sm btn-primary mt-2 me-3",
                onClick: reset_filters
              }, toDisplayString(_i18n('reset')), 1 /* TEXT */),
              createVNode(script$2v, {
                show: loading.value,
                size: "1rem",
                class: "me-1"
              }, null, 8 /* PROPS */, ["show"])
            ])
          ]),
          _: 1 /* STABLE */
        }, 8 /* PROPS */, ["table_id", "csrf"])
      ]))
    }
    }

    };

    script$1j.__file = "http_src/vue/page-macs-list.vue";

    const _hoisted_1$1a = { class: "m-2 mb-3" };
    const _hoisted_2$10 = ["title"];
    const _hoisted_3$T = ["onClick", "title"];
    const _hoisted_4$R = { class: "card-footer" };

    /* ************************************** */


    var script$1i = {
      __name: 'page-snmp-interfaces',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    /* ************************************** */

    /* The same exact page is used for both, the interfaces page for all SNMP devices
     * and the interfaces page for a specific device. Having different columns, simply switch between 
     * those two tables.
     */
    const table_id = props.context?.inside_a_device ? ref('snmp_device_interfaces') : ref('snmp_interfaces');
    const table_snmp_interfaces = ref(null);
    const csrf = props.context.csrf;
    const filter_table_array = ref([]);
    const filter_table_dropdown_array = ref([]);

    const note_list = [
        i18n("snmp.snmp_note_periodic_interfaces_polling"),
        i18n("snmp.snmp_note_thpt_calc"),
        i18n("snmp.snmp_note_avg_usage")
    ];
    const interface_status = {
        ["1"]: "<font color=green>" + i18n("snmp.status_up") + "</font>",
        ["101"]: "<font color=green>" + i18n("snmp.status_up_in_use") + "</font>",
        ["2"]: "<font color=red>" + i18n("snmp.status_down") + "</font>",
        ["3"]: i18n("snmp.testing"),
        ["4"]: i18n("snmp.status_unknown"),
        ["5"]: i18n("snmp.status_dormant"),
        ["6"]: i18n("status_notpresent"),
        ["7"]: "<font color=red>" + i18n("snmp.status_lowerlayerdown") + "</font>",
    };
    const duplex_status = {
        ["1"]: i18n("unknown"),
        ["2"]: "<font color=orange>" + i18n("flow_devices.half_duplex") + "</font>",
        ["3"]: "<font color=green>" + i18n("flow_devices.full_duplex") + "</font>"
    };

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        formatterUtils.getFormatter("percentage");
        let map_columns = {
            "device_name": (value, row) => {
                let url = `<a href='${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?ip=${row.device_ip}'>${value}</a>`;
                if (dataUtils.isEmptyOrNull(value)) {
                    url = row.device_ip;
                }
                return url
            },
            "interface_name": (value, row) => {
                value = value.replace(/</g, "&lt;").replace(/>/g, "&gt;");
                const url = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.device_ip}&snmp_port_idx=${row.interface_id}`;
                return `<a href=${url}>${value}</a>`
            },
            "vlan": (value, row) => {
                let vlan_name = '';
                if (row.vlan_name != '') {
                    vlan_name = '[' + row.vlan_name + ']';
                }
                return `${value} ${vlan_name}`
            },
            "admin_status": (value, row) => {
                return `${interface_status[value] || ''}`
            },
            "status": (value, row) => {
                return `${interface_status[value] || ''}`
            },
            "duplex_status": (value, row) => {
                return `${duplex_status[value] || ''}`
            },
            "num_macs": (value, row) => {
                if (value > 0) {
                    const url = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.device_ip}&snmp_port_idx=${row.interface_id}&page=layer_2`;
                    return `<a href=${url}>${value}</a>`
                }
                return ''
            },
            "in_bytes": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("bytes")(value)
                }
                return ''
            },
            "out_bytes": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("bytes")(value)
                }
                return ''
            },
            "in_errors": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("full_number")(value)
                }
                return ''
            },
            "out_errors": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("full_number")(value)
                }
                return ''
            },
            "in_discards": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("full_number")(value)
                }
                return ''
            },
            "throughput": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("bps")(value)
                }
                return ''
            },
            "uplink_speed": (value, row) => {
                const formatted_speed = formatterUtils.getFormatter("speed")(value);
                return `${formatted_speed} <a href='${create_config_url_link(row, true)}'><i class="fas fa-cog"></i></a>`
            },
            "downlink_speed": (value, row) => {
                const formatted_speed = formatterUtils.getFormatter("speed")(value);
                return `${formatted_speed} <a href='${create_config_url_link(row, true)}'><i class="fas fa-cog"></i></a>`
            },
            "last_in_usage": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("percentage")(value)
                }
                return ''
            },
            "last_out_usage": (value, row) => {
                if (value > 0) {
                    return formatterUtils.getFormatter("percentage")(value)
                }
                return ''
            },
            "last_change": (value, row) => {
                return row.last_change_string
            },
        };
        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    interface_configuration: true, /* Alwais available */
                };
                c.button_def_array.forEach((b) => {
                    if (!visible_dict[b.id]) {
                        b.class.push("disabled");
                    }
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            const url_entry = ntopng_url_manager.get_url_entry(el.id);
            if (url_entry != null) {
                el.options.forEach((option) => {
                    if (option.value.toString() === url_entry) {
                        el.label = `${el.basic_label}: ${option.label || option.value}`;
                    }
                });
            }
        });
    }

    /* ************************************** */

    function add_table_filter(opt, event) {
        event.stopPropagation();
        ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
        set_filter_array_label();
        table_snmp_interfaces.value.refresh_table();
    }

    /* ************************************** */

    const get_open_filter_table_dropdown = (filter, filter_index) => {
        return (_) => {
            load_table_filters(filter, filter_index);
        };
    };

    /* ************************************** */

    async function load_table_filters(filter, filter_index) {
        filter.show_spinner = true;
        await nextTick();
        filter.options = filter_table_array.value.find((t) => t.id == filter.id).options;
        await nextTick();
        let dropdown = filter_table_dropdown_array.value[filter_index];
        dropdown.load_menu();
        filter.show_spinner = false;
    }

    /* ************************************** */

    async function load_table_filters_array() {
        let extra_params = get_extra_params_obj();
        let url_params = ntopng_url_manager.obj_to_url_params(extra_params);
        const url = `${http_prefix}/lua/pro/rest/v2/get/snmp/metric/interfaces_filters.lua?${url_params}`;
        let res = await ntopng_utility.http_request(url);
        return res.map((t) => {
            return {
                id: t.name,
                label: t.label,
                title: t.tooltip,
                options: t.value,
                hidden: (t.value.length == 1)
            };
        });
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            const r0_col = r0[col.data.data_field];
            const r1_col = r1[col.data.data_field];

            /* In case the values are the same, sort by IP */
            if (r0_col == r1_col) {
                return sortingFunctions$1.sortByName(r0.device, r1.device, col ? col.sort : null);
            }
            if (col.id == "device_name") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else if (col.id == "ip") {
                return sortingFunctions$1.sortByIP(r0_col, r1_col, col.sort);
            } else if (col.id == "interface") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else if (col.id == "type") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else if (col.id == "port_type") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else if (col.id == "speed") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "min") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "max") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "average") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "congestion_rate") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "last_value") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            }
        }

        return sortingFunctions$1.sortByName(r0.device, r1.device, col ? col.sort : null);
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function create_config_url_link(row, add_interface) {
        if (add_interface) {
            return `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.device_ip}&snmp_port_idx=${row.interface_id}&page=config`
        } else {
            return `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.device_ip}&page=config`
        }
    }

    /* ************************************** */

    function click_button_timeseries(event) {
        const row = event.row;
        const epoch_begin = ntopng_url_manager.get_url_entry("epoch_begin");
        const epoch_end = ntopng_url_manager.get_url_entry("epoch_end");
        window.open(`${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=${row.ip}&snmp_port_idx=${row.ifid}&page=historical&ifid=-1&epoch_end=${epoch_end}&epoch_begin=${epoch_begin}&timeseries_groups_mode=1_chart_x_metric&timeseries_groups=snmp_interface;-1%2B${row.ip}%2B${row.ifid};snmp_if:usage;uplink=true:false:false:false|downlink=true:false:false:false`);
    }

    /* ************************************** */

    function click_button_configuration(event) {
        const row = event.row;
        window.open(create_config_url_link(row, true));
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_timeseries": click_button_timeseries,
            "click_button_configuration": click_button_configuration
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    onMounted(async () => {
        filter_table_array.value = await load_table_filters_array();
        set_filter_array_label();
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$1a, [
          createVNode(script$2w, {
            ref_key: "table_snmp_interfaces",
            ref: table_snmp_interfaces,
            table_id: unref(table_id),
            csrf: unref(csrf),
            showLoading: true,
            f_map_columns: map_table_def_columns,
            get_extra_params_obj: get_extra_params_obj,
            f_sort_rows: columns_sorting,
            onCustom_event: on_table_custom_event
          }, {
            custom_header: withCtx(() => [
              (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (t, t_index) => {
                return (openBlock(), createBlock(script$2B, {
                  f_on_open: get_open_filter_table_dropdown(t, t_index),
                  ref_for: true,
                  ref: el => { filter_table_dropdown_array.value[t_index] = el; },
                  hidden: t.hidden
                }, {
                  title: withCtx(() => [
                    createVNode(script$2v, {
                      show: t.show_spinner,
                      size: "1rem",
                      class: "me-1"
                    }, null, 8 /* PROPS */, ["show"]),
                    createBaseVNode("a", {
                      class: "ntopng-truncate",
                      title: t.title
                    }, toDisplayString(t.label), 9 /* TEXT, PROPS */, _hoisted_2$10)
                  ]),
                  menu: withCtx(() => [
                    (openBlock(true), createElementBlock(Fragment, null, renderList(t.options, (opt) => {
                      return (openBlock(), createElementBlock("a", {
                        style: {"cursor":"pointer","display":"block"},
                        onClick: $event => (add_table_filter(opt, $event)),
                        class: "ntopng-truncate tag-filter",
                        title: opt.value
                      }, [
                        (opt.count == null)
                          ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                              createTextVNode(toDisplayString(opt.label), 1 /* TEXT */)
                            ], 64 /* STABLE_FRAGMENT */))
                          : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                              createTextVNode(toDisplayString(opt.label + " (" + opt.count + ")"), 1 /* TEXT */)
                            ], 64 /* STABLE_FRAGMENT */))
                      ], 8 /* PROPS */, _hoisted_3$T))
                    }), 256 /* UNKEYED_FRAGMENT */))
                  ]),
                  _: 2 /* DYNAMIC */
                }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["f_on_open", "hidden"]))
              }), 256 /* UNKEYED_FRAGMENT */))
            ]),
            _: 1 /* STABLE */
          }, 8 /* PROPS */, ["table_id", "csrf"])
        ]),
        createBaseVNode("div", _hoisted_4$R, [
          createVNode(script$2u, { note_list: note_list })
        ])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1i.__file = "http_src/vue/page-snmp-interfaces.vue";

    const _hoisted_1$19 = { class: "m-2 mb-3" };
    const _hoisted_2$$ = { class: "card-footer" };

    /* ******************************************************************** */


    var script$1h = {
      __name: 'page-snmp-topology',
      props: {
      context: Object,
    },
      setup(__props) {

    /* Imports */
    const snmp_interface_href = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=%host&snmp_port_idx=%interface`;
    const snmp_device_href = `${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?ip=%ip`;
    const mac_href = `${http_prefix}/lua/mac_details.lua?ip=%ip`;

    /* Consts */
    const _i18n = (t) => i18n(t);

    const note_list = [
      _i18n("snmp.snmp_note_periodic_interfaces_polling"),
      _i18n("snmp.snmp_note_thpt_calc"),
      _i18n("snmp.snmp_lldp_cdp_descr")
    ];
    const snmp_status = {
      "1": "<font color=green>" + _i18n("snmp.status_up") + "</font>",
      "2": "<font color=red>" + _i18n("snmp.status_down") + "</font>",
      "3": _i18n("snmp.testing"),
      "4": _i18n("snmp.status_unknown"),
      "5": _i18n("snmp.status_dormant"),
      "6": _i18n("status_notpresent"),
      "7": "<font color=red>" + _i18n("snmp.status_lowerlayerdown") + "</font>",
      "101": "<font color=green>" + _i18n("snmp.status_up_in_use") + "</font>",
    };

    const lldp_span = `<span class="badge bg-info">${_i18n('snmp.lldp')}</span>`;
    const cdp_span = `<span class="badge bg-info">${_i18n('snmp.cdp')}</span>`;
    const trunk_span = `<span class="badge bg-info">${_i18n('trunk')}</span>`;

    const table_id = ref("snmp_topology");
    const table_topology = ref();
    const total_rows = ref(0);

    const props = __props;
    const context = ref({
      csrf: props.context.csrf,
      ifid: props.context.ifid,
    });

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager$1.get_url_object();
      return extra_params;
    };


    /* Function used to sort the columns of the table */
    function columns_sorting(col, r0, r1) {
      if (col != null) {
        if (col.id == "thpt") {
          return sortingFunctions$1.sortByNumber(r0.port.thpt, r1.port.thpt, col.sort);
        } else if (col.id == "vlan") {
          return sortingFunctions$1.sortByName(r0.port.vlan, r1.port.vlan, col.sort);
        } else if (col.id == "interface") {
          return sortingFunctions$1.sortByName(r0.port.interface_name, r1.port.interface_name, col.sort);
        } else if (col.id == "device") {
          return sortingFunctions$1.sortByName(r0.port.device_name, r1.port.device_name, col.sort);
        } else if (col.id == "port_status") {
          return sortingFunctions$1.sortByNumber(r0.port.port_status, r1.port.port_status, col.sort);
        } else if (col.id == "lldp_remote_sys_name") {
          return sortingFunctions$1.sortByName(r0.remote_port.sys_name, r1.remote_port.sys_name, col.sort);
        } else if (col.id == "lldp_remote_port_id") {
          let r0_val = r0.remote_port.port_id;
          let r1_val = r1.remote_port.port_id;
          if (typeof (r0.remote_port.port_id) == 'object')
            r0_val = r0.remote_port.port_id.mac;
          if (typeof (r1.remote_port.port_id) == 'object')
            r1_val = r1.remote_port.port_id.mac;
          return sortingFunctions$1.sortByName(r0_val, r1_val, col.sort);
        } else if (col.id == "lldp_remote_port_descr") {
          return sortingFunctions$1.sortByName(r0.remote_port.port_descr, r1.remote_port.port_descr, col.sort);
        } else if (col.id == "lldp_remote_sys_descr") {
          return sortingFunctions$1.sortByName(r0.remote_port.sys_descr, r1.remote_port.sys_descr, col.sort);
        }
      }

    }

    /* Get the number of rows of the table */
    function on_table_loaded() {
      total_rows.value = table_topology.value.get_rows_num();
    }

    /* ******************************************************************** */

    /* Function to map columns data */
    const map_table_def_columns = (columns) => {
      let map_columns = {
        "thpt": (_, row) => {
          if(!row.port.thpt)
            return ''
          return NtopUtils$1.bitsToSize(row.port.thpt);
        },
        "interface": (_, row) => {
          const local_port = row.port;
          return `<a href='${snmp_interface_href.replace('%host', local_port.device_ip).replace('%interface', local_port.interface)}'>${local_port.interface_name}</a>${local_port.is_lldp ? ' ' + lldp_span : ''}${local_port.is_cdp ? ' ' + cdp_span : ''}${local_port.is_trunk ? ' ' + trunk_span : ''}`;
        },
        "vlan": (_, row) => {
          return row.port.vlan || '';
        },
        "device": (_, row) => {
          const local_port = row.port;
          return `<a href='${snmp_device_href.replace('%ip', local_port.device_ip)}'>${local_port.device_name}</a>`;
        },
        "port_status": (_, row) => {
          return snmp_status[row.port.port_status];
        },
        "lldp_remote_sys_name": (_, row) => {
          return row.remote_port.sys_name;
        },
        "lldp_remote_port_id": (_, row) => {
          if (typeof (row.remote_port.port_id) == 'object') {
            if (row.remote_port.port_id.in_memory)
              return `<a href='${mac_href.replace('%mac', row.remote_port.port_id.mac)}'>${row.remote_port.port_id.mac}</a>`;
            else
              return row.remote_port.port_id.mac
          } else
            return row.remote_port.port_id
        },
        "lldp_remote_port_descr": (_, row) => {
          return row.remote_port.port_descr;
        },
        "lldp_remote_sys_descr": (_, row) => {
          return row.remote_port.sys_descr;
        },
      };
      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];
      });

      return columns;
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$19, [
        createVNode(script$2w, {
          ref_key: "table_topology",
          ref: table_topology,
          table_id: table_id.value,
          csrf: context.value.csrf,
          showLoading: true,
          f_map_columns: map_table_def_columns,
          get_extra_params_obj: get_extra_params_obj,
          f_sort_rows: columns_sorting,
          onRows_loaded: on_table_loaded
        }, null, 8 /* PROPS */, ["table_id", "csrf"]),
        createBaseVNode("div", _hoisted_2$$, [
          createVNode(script$2u, { note_list: note_list })
        ])
      ]))
    }
    }

    };

    script$1h.__file = "http_src/vue/page-snmp-topology.vue";

    const _hoisted_1$18 = { class: "row" };
    const _hoisted_2$_ = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$S = { class: "card card-shadow" };
    const _hoisted_4$Q = { class: "card-body" };
    const _hoisted_5$I = { class: "card-footer" };

    /* ******************************************************************** */

    /* Consts */

    var script$1g = {
      __name: 'page-snmp-topology-map',
      props: {
      context: Object,
    },
      setup(__props) {

    /* Imports */
    const _i18n = (t) => i18n(t);

    const no_services_message = _i18n('map_page.no_services');
    const note_list = [
      _i18n("snmp.snmp_lldp_cdp_descr"),
      _i18n("snmp.snmp_lldp_cdp_zoom_descr"),
      _i18n("snmp.snmp_lldp_cdp_node_color"),
    ];
    const map_id = ref('topology-map');
    const url_params = ntopng_url_manager$1.get_url_object();
    const topology_url = `${http_prefix}/lua/pro/rest/v2/get/snmp/topology_map.lua`;
    const snmp_device_url = `${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?ip=%ip`;

    const click_device = function(item) {
      ntopng_url_manager$1.go_to_url(snmp_device_url.replace('%ip', item.nodes[0]));
    };

    const event_listeners = {
      'doubleClick': click_device
    };

    const props = __props;

    const context = ref({
      csrf: props.context.csrf,
      ifid: props.context.ifid,
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$18, [
        createBaseVNode("div", _hoisted_2$_, [
          createBaseVNode("div", _hoisted_3$S, [
            createBaseVNode("div", _hoisted_4$Q, [
              createVNode(script$26, {
                ref: "service_map",
                empty_message: unref(no_services_message),
                event_listeners: event_listeners,
                page_csrf: context.value.csrf,
                url: topology_url,
                url_params: unref(url_params),
                map_id: map_id.value
              }, null, 8 /* PROPS */, ["empty_message", "page_csrf", "url_params", "map_id"])
            ]),
            createBaseVNode("div", _hoisted_5$I, [
              createVNode(script$2u, { note_list: note_list })
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$1g.__file = "http_src/vue/page-snmp-topology-map.vue";

    const _hoisted_1$17 = { class: "m-2 mb-3" };
    const _hoisted_2$Z = { class: "card-footer" };

    /* ******************************************************************** */

    /* Consts */

    var script$1f = {
      __name: 'page-snmp-similarity',
      props: {
      context: Object,
    },
      setup(__props) {

    /* Imports */
    const _i18n = (t) => i18n(t);

    const note_list = [
      _i18n("snmp.snmp_similarity_note"),
      _i18n("snmp.snmp_similarity_time_note")
    ];

    const timestamp = Math.floor(Date.now() / 1000);
    const device_href = `${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?host=%host`;
    const interface_href = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?host=%host&snmp_port_idx=%ifid&page=historical&epoch_begin=${timestamp - 86400}&epoch_end=${timestamp}`;
    const table_id = ref("snmp_similarity");
    const table_similarity = ref();
    const total_rows = ref(0);

    const props = __props;

    const context = ref({
      csrf: props.context.csrf,
      ifid: props.context.ifid,
    });



    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager$1.get_url_object();
      return extra_params;
    };


    /* This function simply return the data of the exact column and row requested */
    function column_data(col, row) {
      let data = row[col.data.data_field];
      if (col.data.data_field == "port_id") {
        return Number(data.split(">")[1].split("<")[0]);
      }
      return data;
    }


    /* Function used to sort the columns of the table */
    function columns_sorting(col, r0, r1) {
      if (col != null) {
        let r0_col = column_data(col, r0);
        let r1_col = column_data(col, r1);


        if (col.id == "average_traffic_a" || col.id == "average_traffic_b" || col.id == "similarity") {
          return sortingFunctions$1.sortByNumber(r0_col, r1_col, col.sort);
        } else if (col.id == "device_a" || col.id == "device_b") {
          return sortingFunctions$1.sortByName(r0_col.name, r1_col.name, col.sort);
        } else if (col.id == "port_a" || col.id == "port_b") {
          return sortingFunctions$1.sortByName(r0_col.name, r1_col.name, col.sort);
        }
      }

    }

    /* Get the number of rows of the table */
    function on_table_loaded() {
      total_rows.value = table_similarity.value.get_rows_num();
    }

    /* ******************************************************************** */

    /* Function to map columns data */
    const map_table_def_columns = (columns) => {
      let map_columns = {
        "device_a": (data, row) => {
          return `<a href='${device_href.replace('%host', data.ip)}'>${data.name}</a>`;
        },
        "device_b": (data, row) => {
          return `<a href='${device_href.replace('%host', data.ip)}'>${data.name}</a>`;
        },
        "port_a": (data, row) => {
          return `<a href='${interface_href.replace('%host', row.device_a.ip).replace('%ifid', data.port)}'>${data.name}</a>`;
        },
        "port_b": (data, row) => {
          return `<a href='${interface_href.replace('%host', row.device_b.ip).replace('%ifid', data.port)}'>${data.name}</a>`;
        },
        "average_traffic_a": (data, row) => {
          return formatterUtils.getFormatter("bps_no_scale")(data);
        },
        "average_traffic_b": (data, row) => {
          return formatterUtils.getFormatter("bps_no_scale")(data);
        },
      };
      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];
      });

      return columns;
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$17, [
        createVNode(script$2w, {
          ref_key: "table_similarity",
          ref: table_similarity,
          table_id: table_id.value,
          csrf: context.value.csrf,
          showLoading: true,
          f_map_columns: map_table_def_columns,
          get_extra_params_obj: get_extra_params_obj,
          f_sort_rows: columns_sorting,
          onRows_loaded: on_table_loaded
        }, null, 8 /* PROPS */, ["table_id", "csrf"]),
        createBaseVNode("div", _hoisted_2$Z, [
          createVNode(script$2u, { note_list: note_list })
        ])
      ]))
    }
    }

    };

    script$1f.__file = "http_src/vue/page-snmp-similarity.vue";

    const _hoisted_1$16 = {
      key: 0,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_2$Y = { class: "col-form-label col-sm-4" };
    const _hoisted_3$R = { class: "col-sm-5 pe-0" };
    const _hoisted_4$P = ["placeholder"];
    const _hoisted_5$H = { class: "text-muted" };
    const _hoisted_6$z = { class: "col-2 ps-0" };
    const _hoisted_7$u = ["placeholder"];
    const _hoisted_8$r = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_9$o = { class: "col-form-label col-sm-4" };
    const _hoisted_10$l = { class: "col-sm-5" };
    const _hoisted_11$j = { class: "text-muted" };
    const _hoisted_12$g = {
      key: 1,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_13$f = { class: "col-form-label col-sm-4" };
    const _hoisted_14$e = { class: "col-sm-5" };
    const _hoisted_15$d = ["placeholder"];
    const _hoisted_16$d = { class: "text-muted" };
    const _hoisted_17$d = {
      key: 2,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_18$a = { class: "col-form-label col-sm-4" };
    const _hoisted_19$9 = { class: "col-sm-5" };
    const _hoisted_20$9 = { class: "text-muted" };
    const _hoisted_21$9 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_22$9 = { class: "col-form-label col-sm-4" };
    const _hoisted_23$9 = { class: "col-sm-5" };
    const _hoisted_24$9 = { class: "text-muted" };
    const _hoisted_25$8 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_26$7 = { class: "col-form-label col-sm-4" };
    const _hoisted_27$7 = { class: "col-sm-5" };
    const _hoisted_28$6 = { class: "text-muted" };
    const _hoisted_29$6 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_30$6 = { class: "col-form-label col-sm-4" };
    const _hoisted_31$5 = { class: "col-sm-5" };
    const _hoisted_32$5 = { class: "text-muted" };
    const _hoisted_33$5 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_34$5 = { class: "col-form-label col-sm-4" };
    const _hoisted_35$5 = { class: "col-sm-5" };
    const _hoisted_36$5 = { class: "text-muted" };
    const _hoisted_37$4 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_38$4 = { class: "col-form-label col-sm-4" };
    const _hoisted_39$3 = { class: "col-sm-5" };
    const _hoisted_40$3 = { class: "text-muted" };
    const _hoisted_41$3 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_42$3 = { class: "col-form-label col-sm-4" };
    const _hoisted_43$3 = { class: "col-sm-5" };
    const _hoisted_44$3 = { class: "text-muted" };
    const _hoisted_45$3 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_46$3 = { class: "col-form-label col-sm-4" };
    const _hoisted_47$2 = { class: "col-sm-5" };
    const _hoisted_48$2 = { class: "text-muted" };
    const _hoisted_49$2 = {
      key: 0,
      class: "me-auto text-danger d-inline"
    };
    const _hoisted_50$2 = ["disabled"];
    const _hoisted_51$2 = ["disabled"];

    /* ****************************************************** */

    const CIDR_24 = 24;
    const CIDR_32 = 32;
    const CIDR_128 = 128;


    var script$1e = {
      __name: 'modal-add-snmp-device',
      props: {
        context: Object,
    },
      emits: ["add", "edit"],
      setup(__props, { expose: __expose, emit: __emit }) {

    /* Imports */
    const _i18n = (t) => i18n(t);
    const emit = __emit;
    const props = __props;

    /* Consts */
    const title = ref(_i18n("snmp.add_snmp_devices"));
    const host_placeholder = ref(_i18n("snmp.device_example").replace("%{example}", "192.168.1.2"));
    const community_place_holder = ref(_i18n("snmp.device_example").replace("%{example}", "public"));
    const cidr_placeholder = i18n("hosts_stats.page_scan_hosts.cidr_placeholder");

    const modal_id = ref(null);
    const row_to_edit_id = ref("");
    const activate_add_spinner = ref(false);
    const is_edit_page = ref(false);
    const ifid = ref(null);
    const snmp_host = ref(null);
    const snmp_read_community = ref(null);
    const snmp_context = ref(null);
    const snmp_write_community = ref(null);
    const is_enterprise_l = ref(null);
    const is_cidr_correct = ref(false);
    const is_host_correct = ref(false);
    const is_community_correct = ref(false);
    const is_snmp_auth_passphrase_valid = ref(true);
    const is_snmp_privacy_passphrase_valid = ref(true);
    const is_username_valid = ref(true);
    const no_host_feedback = ref("");
    const with_privacy = ref(false);
    const with_auth = ref(false);
    const enable_v3_options = ref(false);
    const default_snmp_community = props.context.default_snmp_community;

    const selected_version = ref(null);
    const snmp_versions = props.context.snmp_v3_available ?
        ref([
            { id: "0", label: "v1" },
            { id: "1", label: "v2c" },
            { id: "2", label: "v3" },
        ]) :
        ref([
            { id: "0", label: "v1" },
            { id: "1", label: "v2c" }]);

    const selected_snmp_level = ref(null);
    const snmp_levels = ref([
        { id: "authNoPriv", label: _i18n("snmp.snmp_authnopriv") },
        { id: "authPriv", label: _i18n("snmp.snmp_authpriv") },
        { id: "noAuthNoPriv", label: _i18n("snmp.snmp_noauthnopriv") }
    ]);

    const selected_auth_protocol = ref(null);
    const snmp_auth_protocols = ref([
        { id: "md5", label: "MD5" },
        { id: "sha", label: "SHA" },
        { id: "sha256", label: "SHA256" },
        { id: "sha384", label: "SHA384" },
        { id: "sha512", label: "SHA512" },
    ]);

    const snmp_privacy_protocols = ref([
        { id: "des", label: "DES" },
        { id: "aes", label: "AES" },
        { id: "aes128", label: "AES128" },
    ]);
    const selected_privacy_protocol = ref(null);

    const snmp_username = ref(null);
    const snmp_auth_passphrase = ref(null);
    const snmp_privacy_passphrase = ref("");

    const selected_cidr = ref(null);
    const is_data_not_ok = ref(false);
    ref(false);
    /* ****************************************************** */

    /*
     * Reset fields in modal form
     */
    const reset_modal_form = function () {
        snmp_host.value = "";
        selected_cidr.value = "";
        snmp_read_community.value = default_snmp_community;
        snmp_context.value = "";
        snmp_write_community.value = "";
        no_host_feedback.value = "";
        snmp_auth_passphrase.value = "";
        snmp_username.value = "";
        selected_snmp_level.value = snmp_levels.value[2];
        selected_auth_protocol.value = snmp_auth_protocols.value[0];
        selected_privacy_protocol.value = snmp_privacy_protocols.value[0];
        snmp_privacy_passphrase.value = "";
        is_cidr_correct.value = false;
        is_host_correct.value = false;
        check_community();
        activate_add_spinner.value = false;
        row_to_edit_id.value = null;
        is_edit_page.value = false;
        is_data_not_ok.value = false;
        selected_version.value = snmp_versions.value[0];
        enable_v3_options.value = false;
        is_snmp_auth_passphrase_valid.value = true;
        is_snmp_privacy_passphrase_valid.value = true;
        is_username_valid.value = true;
    };

    /* ****************************************************** */

    /*
     * Set row to edit
     */
    const set_row_to_edit = (row) => {
        is_edit_page.value = true;

        /* Set host values */
        snmp_host.value = row.ip;
        selected_cidr.value = row.cidr;

        is_host_correct.value = true;
        is_cidr_correct.value = true;
        is_community_correct.value = true;

        is_username_valid.value = true;

        selected_version.value = snmp_versions.value.find((item) => item.label == row.snmp_version);
        if (selected_version.value.id != '2') {
            snmp_read_community.value = row.community;
            snmp_write_community.value = row.write_community;
        } else {
            enable_v3_options.value = true;
            snmp_context.value = row.context;
            snmp_auth_passphrase.value = row.auth_passphrase;
            selected_auth_protocol.value = snmp_auth_protocols.value.find((item) => item.id == row.auth_protocol);
            selected_snmp_level.value = snmp_levels.value.find((item) => item.id == row.level);
            selected_privacy_protocol.value = snmp_privacy_protocols.value.find((item) => item.id == row.privacy_protocol);
            snmp_privacy_passphrase.value = row.privacy_passphrase;
            snmp_username.value = row.username;
            switch (selected_snmp_level.value.id) {
                case "authNoPriv": {
                    with_privacy.value = false;
                    with_auth.value = true;
                } break;
                case "authPriv": {
                    with_privacy.value = true;
                    with_auth.value = true;
                } break;
                case "noAuthNoPriv": {
                    with_privacy.value = false;
                    with_auth.value = false;
                }
            }
        }

    };

    /* ****************************************************** */

    /* This method is called whenever the modal is opened */
    const show = (row, _host) => {
        /* First of all reset all the data */
        reset_modal_form();
        title.value = i18n("snmp.add_snmp_devices");
        if (!dataUtils.isEmptyOrNull(row)) {
            /* In case row is not null then an edit is requested */
            const device_name = row.name != null && row.name != "" ? row.name : row.ip;
            title.value = `${_i18n("snmp.edit_snmp_devices")}: ${device_name}`;
            set_row_to_edit(row);
        }

        modal_id.value.show();
    };

    const update_levels = () => {

        is_community_correct.value = true;
        switch (selected_snmp_level.value.id) {
            case "authNoPriv": {
                with_privacy.value = false;
                with_auth.value = true;
                is_snmp_auth_passphrase_valid.value = false;
                is_snmp_privacy_passphrase_valid.value = true;

                is_username_valid.value = false;
                snmp_auth_passphrase.value = "";
                snmp_privacy_passphrase.value = "";
            } break;
            case "authPriv": {
                with_privacy.value = true;
                with_auth.value = true;
                is_snmp_auth_passphrase_valid.value = false;
                is_snmp_privacy_passphrase_valid.value = false;

                is_username_valid.value = false;
                snmp_auth_passphrase.value = "";
                snmp_privacy_passphrase.value = "";
            } break;
            case "noAuthNoPriv": {
                with_privacy.value = false;
                with_auth.value = false;
                is_snmp_auth_passphrase_valid.value = true;
                is_snmp_privacy_passphrase_valid.value = true;
                is_username_valid.value = true;
                snmp_auth_passphrase.value = "";
                snmp_privacy_passphrase.value = "";
            } break;
        }
    };

    const update_v3_fields = () => {

        if (selected_version.value.id == "2" && props.context.snmp_v3_available) {
            enable_v3_options.value = true;
            is_community_correct.value = true;
        } else {
            enable_v3_options.value = false;
            check_community();
        }
    };

    /* ****************************************************** */

    /* Regex to check if the host is correct or not */
    const check_host_regex = () => {
        const is_ipv4 = regexValidation.validateIPv4(snmp_host.value);
        const is_ipv6 = regexValidation.validateIPv6(snmp_host.value);
        const is_host_name = regexValidation.validateHostName(snmp_host.value);

        /* When it isn't the ipv4_netscan case the cidr selection is enabled */

        if (is_ipv4) {
            /* IPv4 */
            is_host_correct.value = true;
            selected_cidr.value = CIDR_32;
            is_cidr_correct.value = true;
        } else if (is_ipv6) {
            /* IPv6 */
            is_host_correct.value = true;
            selected_cidr.value = CIDR_128;
            is_cidr_correct.value = true;
        } else if (is_host_name) {
            /* Host Name */
            is_host_correct.value = true;

        } else {
            is_host_correct.value = false;
        }

    };

    const check_community = () => {
        if (snmp_read_community.value) {
            is_community_correct.value = true;
        } else {
            is_community_correct.value = false;
        }
    };

    const check_cidr = () => {
        if (selected_cidr.value == CIDR_24 || selected_cidr.value == CIDR_32 || selected_cidr.value == CIDR_128) {
            is_cidr_correct.value = true;
            return true;
        }
        is_cidr_correct.value = false;
        return false;
    };

    const check_username = () => {
        if (snmp_username.value != null && snmp_username.value != "") {
            is_username_valid.value = true;
        } else {
            is_username_valid.value = false;
        }
    };

    const check_snmp_auth_passphrase = () => {
        if (snmp_auth_passphrase.value != null && snmp_auth_passphrase.value.length >= 6) {
            is_snmp_auth_passphrase_valid.value = true;
        } else {
            is_snmp_auth_passphrase_valid.value = false;
        }
    };

    const check_snmp_privacy_passphrase = () => {
        if (snmp_privacy_passphrase.value != null && snmp_privacy_passphrase.value.length >= 6) {
            is_snmp_privacy_passphrase_valid.value = true;
        } else {
            is_snmp_privacy_passphrase_valid.value = false;
        }
    };

    /* ****************************************************** */

    /* Resolve hostname */
    async function resolve_host_name(host) {
        const resolve_host_name_url = `${http_prefix}/lua/rest/v2/get/host/resolve_host_name.lua`;
        const url = NtopUtils$1.buildURL(resolve_host_name_url, {
            host: host,
        });

        return await ntopng_utility.http_request(url);
    }

    /* ****************************************************** */

    /* Function called when the edit button is clicked */
    const edit_ = () => {
        add_(true);
    };

    /* ****************************************************** */

    /* Function called when the modal is closed */
    const close = () => {
        modal_id.value.close();
    };

    /* ****************************************************** */

    /* Function to add host to scan */
    const add_ = async (is_edit) => {
        const emit_event = (is_edit === true) ? "edit" : "add";

        /* Activate the spinner to give the user a feedback */
        activate_add_spinner.value = true;

        /* Check if it's an IP or not, if not it means it's an hostname */
        let new_host_name_resolved = true;
        let new_host = snmp_host.value;
        if (!regexValidation.validateIP(snmp_host.value)) {
            /* During the validation disable the add button */
            is_host_correct.value = false;
            new_host = await resolve_host_name(snmp_host.value);
            if (new_host === "no_success") {
                /* The resolution failed! */
                new_host_name_resolved = false;
                no_host_feedback.value = snmp_host.value + " " + i18n("hosts_stats.page_scan_hosts.host_not_resolved");
                is_data_not_ok.value = true;
                /* Hide the message after 3 seconds */
                setTimeout(() => {
                    is_data_not_ok.value = false;
                }, 4000);
            }
            /* Validation ended, re-enable the button */
            is_host_correct.value = true;
        }

        /* If the resolution was ok or no resolution at all was done emit the event */
        activate_add_spinner.value = new_host_name_resolved;

        if (new_host_name_resolved) {
            /* Emit the event, only if the resolution 
            was ok or no resolution at all was needed */
            let snmp_entry = {
                snmp_host: new_host,
                cidr: selected_cidr.value,
                snmp_version: selected_version.value.id,
                snmp_read_community: snmp_read_community.value,
                snmp_write_community: snmp_write_community.value

            };

            if (selected_version.value.id == '2') {
                let tmp_selected_auth_protocol = selected_auth_protocol.value != null ? selected_auth_protocol.value.id : snmp_auth_protocols.value[0].id;
                let tmp_selected_snmp_level = selected_snmp_level.value != null ? selected_snmp_level.value.id : snmp_levels.value[0].id;
                let tmp_selected_privacy_protocol = selected_privacy_protocol.value != null ? selected_privacy_protocol.value.id : snmp_privacy_protocols.value[0].id;

                snmp_entry = {
                    ...snmp_entry, ...{
                        snmp_auth_passphrase: snmp_auth_passphrase.value,
                        snmp_auth_protocol: tmp_selected_auth_protocol,
                        snmp_level: tmp_selected_snmp_level,
                        snmp_privacy_protocol: tmp_selected_privacy_protocol,
                        snmp_privacy_passphrase: snmp_privacy_passphrase.value,
                        snmp_username: snmp_username.value,
                        snmp_context: snmp_context.value
                    }
                };
            }
            is_data_not_ok.value = false;
            emit(emit_event,
                snmp_entry
            );
        }
    };

    /* ****************************************************** */

    /* Load the available metrics */
    const metricsLoaded = async (_scan_type_list, _ifid, _is_enterprise_l) => {
        const scan_types = _scan_type_list.sort((a, b) =>
            a.label.localeCompare(b.label)
        );
        ifid.value = _ifid;
        scan_type_list.value = scan_types;
        discovered_scan_type_list.value = scan_types.filter((item) => (item.id != 'ipv4_netscan'));
        is_enterprise_l.value = _is_enterprise_l;
        selected_scan_type.value = scan_type_list.value[0];
    };

    const show_bad_feedback = (feedback_msg) => {
        is_data_not_ok.value = true;
        no_host_feedback.value = feedback_msg;
        activate_add_spinner.value = false;
    };

    __expose({ show, close, metricsLoaded, show_bad_feedback });

    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("form", null, [
            createCommentVNode(" Host "),
            (!is_edit_page.value)
              ? (openBlock(), createElementBlock("div", _hoisted_1$16, [
                  createBaseVNode("label", _hoisted_2$Y, [
                    createBaseVNode("b", null, toDisplayString(_i18n("snmp.snmp_host")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_3$R, [
                    withDirectives(createBaseVNode("input", {
                      "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((snmp_host).value = $event)),
                      onInput: check_host_regex,
                      class: normalizeClass(["form-control", { 'invalid': !is_host_correct.value }]),
                      type: "text",
                      placeholder: host_placeholder.value,
                      required: ""
                    }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_4$P), [
                      [vModelText, snmp_host.value]
                    ]),
                    createBaseVNode("small", _hoisted_5$H, toDisplayString(_i18n("snmp.descriptions.host")), 1 /* TEXT */)
                  ]),
                  _cache[12] || (_cache[12] = createBaseVNode("div", { class: "col-1 ps-5 pe-0 mt-1" }, [
                    createBaseVNode("span", null, "/")
                  ], -1 /* CACHED */)),
                  createBaseVNode("div", _hoisted_6$z, [
                    withDirectives(createBaseVNode("input", {
                      "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((selected_cidr).value = $event)),
                      onInput: check_cidr,
                      class: "form-control",
                      type: "text",
                      placeholder: unref(cidr_placeholder),
                      required: ""
                    }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_7$u), [
                      [vModelText, selected_cidr.value]
                    ])
                  ])
                ]))
              : createCommentVNode("v-if", true),
            createCommentVNode(" SNMP Version "),
            createBaseVNode("div", _hoisted_8$r, [
              createBaseVNode("label", _hoisted_9$o, [
                createBaseVNode("b", null, toDisplayString(_i18n("snmp.snmp_version")), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_10$l, [
                createVNode(script$2H, {
                  selected_option: selected_version.value,
                  "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((selected_version).value = $event)),
                  options: unref(snmp_versions),
                  onSelect_option: update_v3_fields
                }, null, 8 /* PROPS */, ["selected_option", "options"]),
                createBaseVNode("small", _hoisted_11$j, toDisplayString(_i18n("snmp.descriptions.version")), 1 /* TEXT */)
              ])
            ]),
            createCommentVNode(" SNMP Read Community "),
            (enable_v3_options.value == false)
              ? (openBlock(), createElementBlock("div", _hoisted_12$g, [
                  createBaseVNode("label", _hoisted_13$f, [
                    createBaseVNode("b", null, toDisplayString(_i18n("snmp.snmp_read_community")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_14$e, [
                    withDirectives(createBaseVNode("input", {
                      "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((snmp_read_community).value = $event)),
                      class: normalizeClass(["form-control", { 'invalid': !is_community_correct.value }]),
                      type: "text",
                      placeholder: community_place_holder.value,
                      onInput: check_community,
                      required: ""
                    }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_15$d), [
                      [vModelText, snmp_read_community.value]
                    ]),
                    createBaseVNode("small", _hoisted_16$d, toDisplayString(_i18n("snmp.descriptions.read_community")), 1 /* TEXT */)
                  ])
                ]))
              : createCommentVNode("v-if", true),
            createCommentVNode(" SNMP Write Community "),
            (props.context.snmp_set_available && enable_v3_options.value == false)
              ? (openBlock(), createElementBlock("div", _hoisted_17$d, [
                  createBaseVNode("label", _hoisted_18$a, toDisplayString(_i18n("snmp.snmp_write_community")), 1 /* TEXT */),
                  createBaseVNode("div", _hoisted_19$9, [
                    withDirectives(createBaseVNode("input", {
                      "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((snmp_write_community).value = $event)),
                      class: "form-control",
                      type: "text"
                    }, null, 512 /* NEED_PATCH */), [
                      [vModelText, snmp_write_community.value]
                    ]),
                    createBaseVNode("small", _hoisted_20$9, toDisplayString(_i18n("snmp.descriptions.write_community")), 1 /* TEXT */)
                  ])
                ]))
              : createCommentVNode("v-if", true),
            createCommentVNode(" SNMP V3 "),
            (enable_v3_options.value)
              ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [
                  createCommentVNode(" SNMP Context "),
                  createBaseVNode("div", _hoisted_21$9, [
                    createBaseVNode("label", _hoisted_22$9, [
                      createBaseVNode("b", null, toDisplayString(_i18n("snmp.context")), 1 /* TEXT */)
                    ]),
                    createBaseVNode("div", _hoisted_23$9, [
                      withDirectives(createBaseVNode("input", {
                        "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((snmp_context).value = $event)),
                        class: "form-control",
                        type: "text"
                      }, null, 512 /* NEED_PATCH */), [
                        [vModelText, snmp_context.value]
                      ]),
                      createBaseVNode("small", _hoisted_24$9, toDisplayString(_i18n("snmp.descriptions.context")), 1 /* TEXT */)
                    ])
                  ]),
                  createCommentVNode(" SNMP Level "),
                  createBaseVNode("div", _hoisted_25$8, [
                    createBaseVNode("label", _hoisted_26$7, [
                      createBaseVNode("b", null, toDisplayString(_i18n("snmp.snmp_level")), 1 /* TEXT */)
                    ]),
                    createBaseVNode("div", _hoisted_27$7, [
                      createVNode(script$2H, {
                        selected_option: selected_snmp_level.value,
                        "onUpdate:selected_option": _cache[6] || (_cache[6] = $event => ((selected_snmp_level).value = $event)),
                        options: snmp_levels.value,
                        onSelect_option: update_levels
                      }, null, 8 /* PROPS */, ["selected_option", "options"]),
                      createBaseVNode("small", _hoisted_28$6, toDisplayString(_i18n("snmp.descriptions.level")), 1 /* TEXT */)
                    ])
                  ]),
                  (with_auth.value)
                    ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                        createCommentVNode(" SNMP Username "),
                        createBaseVNode("div", _hoisted_29$6, [
                          createBaseVNode("label", _hoisted_30$6, [
                            createBaseVNode("b", null, toDisplayString(_i18n("snmp.snmp_username")), 1 /* TEXT */)
                          ]),
                          createBaseVNode("div", _hoisted_31$5, [
                            withDirectives(createBaseVNode("input", {
                              "onUpdate:modelValue": _cache[7] || (_cache[7] = $event => ((snmp_username).value = $event)),
                              class: normalizeClass(["form-control", { 'invalid': !is_username_valid.value }]),
                              type: "text",
                              autocomplete: "current-username",
                              onInput: check_username,
                              required: ""
                            }, null, 34 /* CLASS, NEED_HYDRATION */), [
                              [vModelText, snmp_username.value]
                            ]),
                            createBaseVNode("small", _hoisted_32$5, toDisplayString(_i18n("snmp.descriptions.username")), 1 /* TEXT */)
                          ])
                        ]),
                        createCommentVNode(" SNMP Auth Protocol "),
                        createBaseVNode("div", _hoisted_33$5, [
                          createBaseVNode("label", _hoisted_34$5, [
                            createBaseVNode("b", null, toDisplayString(_i18n("snmp.authentication_protocol")), 1 /* TEXT */)
                          ]),
                          createBaseVNode("div", _hoisted_35$5, [
                            createVNode(script$2H, {
                              selected_option: selected_auth_protocol.value,
                              "onUpdate:selected_option": _cache[8] || (_cache[8] = $event => ((selected_auth_protocol).value = $event)),
                              options: snmp_auth_protocols.value
                            }, null, 8 /* PROPS */, ["selected_option", "options"]),
                            createBaseVNode("small", _hoisted_36$5, toDisplayString(_i18n("snmp.descriptions.authentication_protocol")), 1 /* TEXT */)
                          ])
                        ]),
                        createCommentVNode(" SNMP Auth PassaPhrase "),
                        createBaseVNode("div", _hoisted_37$4, [
                          createBaseVNode("label", _hoisted_38$4, [
                            createBaseVNode("b", null, toDisplayString(_i18n("snmp.authentication_passphrase")), 1 /* TEXT */)
                          ]),
                          createBaseVNode("div", _hoisted_39$3, [
                            withDirectives(createBaseVNode("input", {
                              "onUpdate:modelValue": _cache[9] || (_cache[9] = $event => ((snmp_auth_passphrase).value = $event)),
                              class: normalizeClass(["form-control", { 'invalid': !is_snmp_auth_passphrase_valid.value }]),
                              type: "password",
                              autocomplete: "current-password",
                              required: "",
                              onInput: check_snmp_auth_passphrase
                            }, null, 34 /* CLASS, NEED_HYDRATION */), [
                              [vModelText, snmp_auth_passphrase.value]
                            ]),
                            createBaseVNode("small", _hoisted_40$3, toDisplayString(_i18n("snmp.descriptions.authentication_passphrase")), 1 /* TEXT */)
                          ])
                        ])
                      ], 64 /* STABLE_FRAGMENT */))
                    : createCommentVNode("v-if", true),
                  createCommentVNode(" SNMP Privacy Protocol "),
                  (with_privacy.value)
                    ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                        createBaseVNode("div", _hoisted_41$3, [
                          createBaseVNode("label", _hoisted_42$3, [
                            createBaseVNode("b", null, toDisplayString(_i18n("snmp.privacy_protocol")), 1 /* TEXT */)
                          ]),
                          createBaseVNode("div", _hoisted_43$3, [
                            createVNode(script$2H, {
                              selected_option: selected_privacy_protocol.value,
                              "onUpdate:selected_option": _cache[10] || (_cache[10] = $event => ((selected_privacy_protocol).value = $event)),
                              options: snmp_privacy_protocols.value
                            }, null, 8 /* PROPS */, ["selected_option", "options"]),
                            createBaseVNode("small", _hoisted_44$3, toDisplayString(_i18n("snmp.descriptions.privacy_protocol")), 1 /* TEXT */)
                          ])
                        ]),
                        createCommentVNode(" SNMP Privacy PassaPhrase "),
                        createBaseVNode("div", _hoisted_45$3, [
                          createBaseVNode("label", _hoisted_46$3, [
                            createBaseVNode("b", null, toDisplayString(_i18n("snmp.privacy_passphrase")), 1 /* TEXT */)
                          ]),
                          createBaseVNode("div", _hoisted_47$2, [
                            withDirectives(createBaseVNode("input", {
                              "onUpdate:modelValue": _cache[11] || (_cache[11] = $event => ((snmp_privacy_passphrase).value = $event)),
                              class: normalizeClass(["form-control", { 'invalid': !is_snmp_privacy_passphrase_valid.value }]),
                              autocomplete: "current-password",
                              type: "password",
                              required: "",
                              onInput: check_snmp_privacy_passphrase
                            }, null, 34 /* CLASS, NEED_HYDRATION */), [
                              [vModelText, snmp_privacy_passphrase.value]
                            ]),
                            createBaseVNode("small", _hoisted_48$2, toDisplayString(_i18n("snmp.descriptions.privacy_passphrase")), 1 /* TEXT */)
                          ])
                        ])
                      ], 64 /* STABLE_FRAGMENT */))
                    : createCommentVNode("v-if", true)
                ], 64 /* STABLE_FRAGMENT */))
              : createCommentVNode("v-if", true)
          ])
        ]),
        footer: withCtx(() => [
          (is_data_not_ok.value)
            ? (openBlock(), createElementBlock("div", _hoisted_49$2, toDisplayString(no_host_feedback.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createVNode(script$2v, {
              show: activate_add_spinner.value,
              size: "1rem",
              class: "me-2"
            }, null, 8 /* PROPS */, ["show"]),
            (is_edit_page.value == false)
              ? (openBlock(), createElementBlock("button", {
                  key: 0,
                  type: "button",
                  onClick: add_,
                  class: "btn btn-primary",
                  disabled: !(is_cidr_correct.value && is_host_correct.value && is_username_valid.value && is_snmp_auth_passphrase_valid.value && is_snmp_privacy_passphrase_valid.value && is_community_correct.value)
                }, toDisplayString(_i18n("add")), 9 /* TEXT, PROPS */, _hoisted_50$2))
              : (openBlock(), createElementBlock("button", {
                  key: 1,
                  type: "button",
                  onClick: edit_,
                  class: "btn btn-primary",
                  disabled: !(is_cidr_correct.value && is_host_correct.value && is_username_valid.value && is_snmp_auth_passphrase_valid.value && is_snmp_privacy_passphrase_valid.value && is_community_correct.value)
                }, toDisplayString(_i18n("apply")), 9 /* TEXT, PROPS */, _hoisted_51$2))
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1e.__file = "http_src/vue/modal-add-snmp-device.vue";

    const _hoisted_1$15 = {
      key: 0,
      class: "alert alert-danger text-start"
    };
    const _hoisted_2$X = {
      key: 1,
      class: "alert alert-warning text-start"
    };
    const _hoisted_3$Q = {
      key: 2,
      class: "alert alert-danger text-start"
    };
    const _hoisted_4$O = {
      key: 3,
      class: "alert alert-danger text-start"
    };

    /* ****************************************************** */


    var script$1d = {
      __name: 'modal-delete-snmp-device',
      emits: ["delete", "ping_all", "prune", "delete_all"],
      setup(__props, { expose: __expose, emit: __emit }) {

    /* Imports */
    const _i18n = (t) => i18n(t);
    const emit = __emit;


    /* Consts */
    const title = ref('');
    const body = ref('');
    const device = ref('');
    const modal_type = ref(null);

    const modal_id = ref(null);

    /* ****************************************************** */

    /* This method is called whenever the modal is opened */
    const show = (type, row) => {

        modal_type.value = type;
        switch (type) {
            case 1: {
                // delete single row case
                title.value = `${_i18n("delete")} ${_i18n("snmp.snmp_device")}: `;
                device.value = row.ip;
                body.value = `${_i18n("snmp.remove_snmp_device_confirm")}: `;
            } break;
            case 2: {
                // ping all devices case
                title.value = `${_i18n("snmp.ping_all_snmp_devices")}`;
                body.value = `${_i18n("snmp.ping_all_snmp_devices_confirm")}`;
            } break;
            case 3: {
                // prune all unresponsive devices case
                title.value = `${_i18n("snmp.prune_unresponsive_snmp_devices")}`;
                body.value = `${_i18n("snmp.prune_unresponsive_snmp_devices_confirm")}`;
            } break;
            case 4: {
                // prune all unresponsive devices case
                title.value = `${_i18n("snmp.remove_all_snmp_devices")}`;
                body.value = `${_i18n("snmp.remove_all_snmp_devices_confirm")}`;
            } break;
        }

        modal_id.value.show();
    };

    const delete_ = () => {
        emit('delete');
        close();
    };

    const ping_ = () => {
        emit('ping_all');
        close();
    };

    const delete_all_unresponsive = () => {
        emit('prune');
        close();
    };

    const delete_all = () => {
        emit('delete_all');
        close();
    };

    const close = () => {
        modal_id.value.close();
    };

    __expose({ show, close });

    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value) + toDisplayString(device.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          (modal_type.value == 1)
            ? (openBlock(), createElementBlock("div", _hoisted_1$15, toDisplayString(body.value) + toDisplayString(device.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (modal_type.value == 2)
            ? (openBlock(), createElementBlock("div", _hoisted_2$X, toDisplayString(body.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (modal_type.value == 3)
            ? (openBlock(), createElementBlock("div", _hoisted_3$Q, toDisplayString(body.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (modal_type.value == 4)
            ? (openBlock(), createElementBlock("div", _hoisted_4$O, toDisplayString(body.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          _cache[0] || (_cache[0] = createBaseVNode("span", {
            class: "invalid-feedback",
            id: "delete-modal-feedback"
          }, null, -1 /* CACHED */))
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", null, [
            (modal_type.value == 1)
              ? (openBlock(), createElementBlock("button", {
                  key: 0,
                  type: "button",
                  onClick: delete_,
                  class: "btn btn-danger"
                }, toDisplayString(_i18n("delete")), 1 /* TEXT */))
              : createCommentVNode("v-if", true),
            (modal_type.value == 2)
              ? (openBlock(), createElementBlock("button", {
                  key: 1,
                  type: "button",
                  onClick: ping_,
                  class: "btn btn-warning"
                }, toDisplayString(_i18n("add")), 1 /* TEXT */))
              : createCommentVNode("v-if", true),
            (modal_type.value == 3)
              ? (openBlock(), createElementBlock("button", {
                  key: 2,
                  type: "button",
                  onClick: delete_all_unresponsive,
                  class: "btn btn-danger"
                }, toDisplayString(_i18n("delete")), 1 /* TEXT */))
              : createCommentVNode("v-if", true),
            (modal_type.value == 4)
              ? (openBlock(), createElementBlock("button", {
                  key: 3,
                  type: "button",
                  onClick: delete_all,
                  class: "btn btn-danger"
                }, toDisplayString(_i18n("delete")), 1 /* TEXT */))
              : createCommentVNode("v-if", true)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1d.__file = "http_src/vue/modal-delete-snmp-device.vue";

    const _hoisted_1$14 = { class: "custom-file" };
    const _hoisted_2$W = {
      class: "form-label",
      for: "import-input"
    };
    const _hoisted_3$P = { class: "w-100" };
    const _hoisted_4$N = {
      key: 0,
      class: "me-auto text-danger d-inline"
    };
    const _hoisted_5$G = ["disabled"];

    const max_size$1 = 256000;

    var script$1c = {
      __name: 'modal-import-snmp-devices',
      props: {
      ifid_list: Array,
      snmp_devices_list: Array,
      snmp_metric_list: Array,
      frequency_list: Array,
      init_func: Function,
      page_csrf: String,
    },
      emits: ['add', 'edit'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    const activate_import_spinner = ref(false);
    const is_data_not_ok = ref(false);
    const is_not_empty_file = ref(false);
    let title = _i18n('snmp.import_devices');
    let message = "";
    const json_file = ref(null);
    const import_input = ref(null);

    const note_list = [
      i18n("snmp.snmp_import_devices"),
      i18n("snmp.snmp_import_devices_issues")
    ];

    const showed = () => { };

    /**
     * 
     * Reset fields in modal form 
     */
    const reset_modal_form = function () {
      json_file.value = null;
      activate_import_spinner.value = false;
      is_data_not_ok.value = false;
      is_not_empty_file.value = false;
      import_input.value.value = null;
    };

    const show = (row) => {
      reset_modal_form();
      modal_id.value.show();
    };

    const handleFileUpload = (event) => {
      json_file.value = event.target.files[0];
      const size = json_file.value.size;
      is_not_empty_file.value = json_file.value != null && size < max_size$1; //256KB
      if (size > max_size$1) {
        is_data_not_ok.value = true;
        message = _i18n("file_to_large");
      } else {
        is_data_not_ok.value = false;
        message = "";
      }
    };

    /**
     * Function to add rule to rules list
     */
    const _import = (is_edit) => {
      activate_import_spinner.value = true;
      is_data_not_ok.value = false;
      const fileReader = new FileReader();
      fileReader.readAsText(json_file.value, "UTF-8");

      fileReader.onload = () => {
        // Set file content to data property
        const is_json = fileReader.result.contains('\"');

        if (is_json) {
          emit('add', {
            devices: fileReader.result
          });
        } else {
          emit('add', {
            devices_csv: fileReader.result
          });
        }


      };
    };

    const show_bad_feedback = (bad_message) => {
      message = bad_message;
      is_data_not_ok.value = true;
      activate_import_spinner.value = false;
    };

    const close = () => {
      activate_import_spinner.value = false;
      is_data_not_ok.value = false;
      modal_id.value.close();
    };

    __expose({ show, close, show_bad_feedback });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[0] || (_cache[0] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Target information, here an IP is put "),
          createBaseVNode("div", _hoisted_1$14, [
            createBaseVNode("label", _hoisted_2$W, toDisplayString(_i18n("browse_snmp_devices")), 1 /* TEXT */),
            createBaseVNode("input", {
              required: "",
              class: "custom-file-input form-control",
              ref_key: "import_input",
              ref: import_input,
              id: "import-input",
              name: "CSV",
              type: "file",
              onChange: handleFileUpload,
              accept: ".json,.csv"
            }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", _hoisted_3$P, [
            createVNode(script$2u, { note_list: note_list })
          ]),
          (is_data_not_ok.value)
            ? (openBlock(), createElementBlock("div", _hoisted_4$N, toDisplayString(unref(message)), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createVNode(script$2v, {
              show: activate_import_spinner.value,
              size: "1rem",
              class: "me-2"
            }, null, 8 /* PROPS */, ["show"]),
            createBaseVNode("button", {
              type: "button",
              disabled: !is_not_empty_file.value,
              onClick: _import,
              class: "btn btn-primary"
            }, toDisplayString(_i18n('import')), 9 /* TEXT, PROPS */, _hoisted_5$G)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1c.__file = "http_src/vue/modal-import-snmp-devices.vue";

    /*
      (C) 2013-23 - ntop.org
     */

    /*
     * Here a list of functions used to retrieve URLs; in this way
     * only here needs to change the URL in case of changes 
     */

    /* ***** IMPORTANT: http_prefix is not returned ***** */

    /* Returns the URL of the exporters details */
    const getExporterDetailsPageURL = (exporter_info, http_prefix) => {
        const exporter_ip = exporter_info.ip;
        let probe_uuid = '';
        let exporter_uuid = '';
        let probe_ip = '';
        
        if (exporter_info.probe_uuid) {
            probe_uuid = exporter_info.probe_uuid;
        }
        if (exporter_info.exporter_uuid) {
            exporter_uuid = exporter_info.exporter_uuid;
        }
        if (exporter_info.probe_ip) {
            probe_ip = exporter_info.probe_ip;
        }
        return `${http_prefix}/lua/pro/enterprise/exporter_interfaces.lua?ip=${exporter_ip}&exporter_uuid=${exporter_uuid}&probe_uuid=${probe_uuid}&probe_ip=${probe_ip}`
    };

    /* ******************************************************************** */

    /* Returns the URL of the exporter details */
    const getExporterInterfaceDetailsPageURL = (exporter_ip, interface_id, ifid, http_prefix) => {
        return `${http_prefix}/lua/pro/enterprise/flowdevice_interface_details.lua?ip=${exporter_ip}&snmp_port_idx=${interface_id}&ifid=${ifid}`
    };

    /* ******************************************************************** */

    /* Returns the URL of the exporter details */
    const getSNMPDetailsPageURL = (device_ip, http_prefix) => {
        return `${http_prefix}/lua/pro/enterprise/snmp_device_details.lua?host=${device_ip}`
    };

    /* ******************************************************************** */

    /* Returns the URL of the exporter details */
    const getSNMPInterfaceDetailsPageURL = (device_ip, interface_id, http_prefix) => {
        return `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?&host=${device_ip}&snmp_port_idx=${interface_id}`
    };

    /* ******************************************************************** */

    /* Returns the URL of the exporter details */
    const getAggregatedFlowsURL = (filters, aggregation_criteria, http_prefix) => {
        let filters_string = '';
        for (const filter in filters) {
            filters_string = `${filters_string}${filter}=${filters[filter]}&`;
        }
        return `${http_prefix}/lua/flows_stats.lua?page=analysis&aggregation_criteria=${aggregation_criteria}&${filters_string}`
    };

    /* ******************************************************************** */

    /* Returns the URL of the exporter details */
    const getFlowExporterInterfaceOverviewPageURL = (device_ip, interface_id, http_prefix) => {
        return `${http_prefix}/lua/pro/exporter_interface_overview.lua?deviceIP=${device_ip}&ifIdx=${interface_id}`
    };

    /* ******************************************************************** */

    const linksUtils = function () {
        return {
            getExporterDetailsPageURL,
            getExporterInterfaceDetailsPageURL,
            getSNMPDetailsPageURL,
            getSNMPInterfaceDetailsPageURL,
            getAggregatedFlowsURL,
            getFlowExporterInterfaceOverviewPageURL
        };
    }();

    const _hoisted_1$13 = { class: "m-2 mb-3" };
    const _hoisted_2$V = {
      key: 0,
      class: "alert alert-warning alert-dismissable"
    };
    const _hoisted_3$O = ["innerHTML"];
    const _hoisted_4$M = {
      key: 1,
      class: "alert alert-danger alert-dismissable"
    };
    const _hoisted_5$F = ["innerHTML"];
    const _hoisted_6$y = { class: "card card-shadow" };
    const _hoisted_7$t = { class: "card-body" };
    const _hoisted_8$q = {
      key: 0,
      class: "alert alert-success alert-dismissable"
    };
    const _hoisted_9$n = ["title"];
    const _hoisted_10$k = ["title"];
    const _hoisted_11$i = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_12$f = { class: "no-wrap d-flex align-items-center filters-label" };
    const _hoisted_13$e = { class: "d-flex justify-content-center align-items-center" };
    const _hoisted_14$d = { class: "card-footer mt-3" };


    /* ************************************** */

    const tableRefreshRate = 10000;


    var script$1b = {
      __name: 'page-snmp-devices',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const props = __props;

    /* ************************************** */

    const table_id = ref("snmp_devices");
    const table_snmp_devices = ref(null);
    const csrf = props.context.csrf;
    //const chart = ref(null);
    const filter_table_array = ref([]);
    const filters = ref([]);
    const modal_add_snmp_device = ref();
    const modal_import_snmp_devices = ref();
    const import_with_success = ref(false);
    const import_ok_text = ref(null);
    const modal_delete_snmp_device = ref();
    const row_to_delete = ref();
    const total_rows = ref(0);
    const config_export_url = `${http_prefix}/lua/rest/v2/export/snmp/config.lua?download=1`;
    const delete_snmp_device_url = `${http_prefix}/lua/pro/rest/v2/delete/snmp/device.lua`;
    const download_snmp_device_log_url = `${http_prefix}/lua/pro/rest/v2/get/snmp/snmp_debug.lua`;
    const add_snmp_device_url = `${http_prefix}/lua/pro/rest/v2/add/snmp/device.lua`;
    const import_snmp_devices_url = `${http_prefix}/lua/pro/rest/v2/add/snmp/import_devices.lua`;
    const edit_snmp_device_url = `${http_prefix}/lua/pro/rest/v2/edit/snmp/device/device.lua`;
    const manage_config_url = `${http_prefix}/lua/admin/manage_configurations.lua?item=snmp`;
    const ping_all_devices_url = `${http_prefix}/lua/pro/rest/v2/check/snmp/ping_all_devices.lua`;
    const delete_snmp_unresponsive_devices_url = `${http_prefix}/lua/pro/rest/v2/delete/snmp/unresponsive_devices.lua`;
    const delete_all_snmp_devices_url = `${http_prefix}/lua/pro/rest/v2/delete/snmp/all_devices.lua`;
    const active_alert_text = _i18n('enable_snmp_polling_warning').replace("%{base_prefix}", `${http_prefix}`);
    const max_num_reached_alert_text = _i18n('snmp_max_num_devices_configured').replace("%{max_num}", props.context.max_devices).replace("%{configured_devices}", props.context.devices_configured);

    const loading = ref(false);

    const timeoutId = ref(null);
    const rest_params = {
        csrf: props.context.csrf,
    };

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "ip": (data, row) => {
                let rsp = '';
                if (row.polling_in_progress) {
                    rsp = `<i class='fas fa-sync' data-bs-toggle='tooltip' data-bs-placement='top' title='${_i18n('snmp.polling_in_progress')}'></i>`;
                }
                if (row.is_unreachable) {
                    rsp =  `<span class='badge bg-warning' data-bs-toggle='tooltip' data-bs-placement='top' title='${_i18n('snmp.snmp_device_does_not_respond')}'>
                            <i class="fas fa-exclamation-triangle"></i>
                        </span> ${rsp}`;
                }
                if (row.last_update) {
                    return `<a href='${linksUtils.getSNMPDetailsPageURL(data, http_prefix)}'>${data}</a> ${rsp}`;
                }
                return `${data} ${rsp}`;
            },
            "interfaces_with_errors": (data, row) => {
                if (data === 0) return "";
                return `<a href='${linksUtils.getSNMPDetailsPageURL(row.ip, http_prefix)}&page=interfaces&err_counters_filter=errors_or_discards'>${data}</a>`;
            },
            "last_update": (value, row) => {
                if (value > 0) {
                    return NtopUtils$1.secondsToTime(value)
                }
                return ''
            },
            "last_poll_duration": (value, row) => {
                if (value > 0) {
                    return NtopUtils$1.secondsToTime(value)
                }
                return ''
            }

        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    edit: true,
                    delete: true,
                    timeseries: props.context.timeseriesAvailable,
                    export: true,
                };
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        // if is not defined is enabled
                        if (!visible_dict[b.id]) {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            /* Setting the basic label */
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            /* Getting the currently selected filter */
            const url_entry = ntopng_url_manager$1.get_url_entry(el.id);
            el.options.forEach((option) => {
                if (option.value.toString() === url_entry) {
                    el.current_option = option;
                }
            });
        });
    }

    /* ************************************** */

    function change_filter_labels() {
        total_rows.value = table_snmp_devices.value.get_rows_num();
    }

    /* ************************************** */

    function add_table_filter(opt, opt2) {
        ntopng_url_manager$1.set_key_to_url(opt.key, `${opt.value}`);
        if (opt2) {
            ntopng_url_manager$1.set_key_to_url(opt2.key, `${opt2.value}`);
        }
        table_snmp_devices.value.refresh_table();
        load_table_filters_array();
    }

    /* ************************************** */

    function set_filters_list(res) {
        if (!res) {
            filter_table_array.value = filters.value.filter((t) => {
                if (t.show_with_key) {
                    const key = ntopng_url_manager$1.get_url_entry(t.show_with_key);
                    if (key !== t.show_with_value) {
                        return false
                    }
                }
                return true
            });
        } else {
            filters.value = res.map((t) => {
                const key_in_url = ntopng_url_manager$1.get_url_entry(t.name);
                if (key_in_url === null) {
                    ntopng_url_manager$1.set_key_to_url(t.name, ``);
                }
                return {
                    id: t.name,
                    label: t.label,
                    title: t.tooltip,
                    options: t.value,
                    show_with_key: t.show_with_key,
                    show_with_value: t.show_with_value,
                };
            });
            set_filters_list();
            return;
        }
        set_filter_array_label();
    }

    /* ************************************** */

    async function load_table_filters_array() {
        loading.value = true;
        set_filters_list(props.context.filters);
        loading.value = false;
    }

    /* ************************************** */

    function reset_filters() {
        filter_table_array.value.forEach((el, index) => {
            /* Getting the currently selected filter */
            ntopng_url_manager$1.set_key_to_url(el.id, ``);
        });
        load_table_filters_array();
        table_snmp_devices.value.refresh_table();
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "ip") {
                return sortingFunctions$1.sortByIP(r0.ip, r1.ip, col.sort);
            } else if (col.id == "snmp_version") {
                return sortingFunctions$1.sortByName(r0.snmp_version, r1.snmp_version, col.sort);
            } else if (col.id == "name") {
                return sortingFunctions$1.sortByName(r0.name, r1.name, col.sort);
            } else if (col.id == "description") {
                return sortingFunctions$1.sortByName(r0.description, r1.description, col.sort);
            } else if (col.id == "interfaces_with_errors") {
                return sortingFunctions$1.sortByNumber(r0.interfaces_with_errors, r1.interfaces_with_errors, col.sort);
            } else if (col.id == "last_poll_duration") {
                return sortingFunctions$1.sortByNumber(r0.last_poll_duration, r1.last_poll_duration, col.sort);
            } else if (col.id == "last_update") {
                return sortingFunctions$1.sortByNumber(r0.last_update, r1.last_update, col.sort);
            }
            // Default option
            return sortingFunctions$1.sortByName(r0.ip, r1.ip, col.sort);
        }
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function import_snmp_devices() {
        modal_import_snmp_devices.value.show();
    }

    function refresh_feedback_messages() {
        import_with_success.value = false;
        import_ok_text.value = null;
    }

    const import_snmp_devices_rest = async function (params) {
        const url = import_snmp_devices_url;
        const result = await ntopng_utility.http_post_request(url, { ...rest_params, ...params }, false, true);

        if (result == null) {
            modal_import_snmp_devices.value.show_bad_feedback(_i18n("import_snmp_devices_error"));
        } else if (result.rsp?.err && result.rsp?.err < 0) {
            modal_import_snmp_devices.value.show_bad_feedback(result.rsp.feedback);
        } else {
            import_with_success.value = true;
            modal_import_snmp_devices.value.close();
            import_ok_text.value = result.rsp.feedback;
            setTimeout(refresh_feedback_messages, 10000);

            table_snmp_devices.value.refresh_table();
        }
    };

    /* ************************************** */
    /* Functions to handle the add of devices */

    function add_snmp_device() {
        modal_add_snmp_device.value.show();
    }

    const add_snmp_device_rest = async function (params) {
        const url = add_snmp_device_url;

        const result = await ntopng_utility.http_post_request(url, { ...rest_params, ...params }, false, true);
        if (result.rc < 0) {
            modal_add_snmp_device.value.show_bad_feedback(result.rc_str_hr);
            table_snmp_devices.value.refresh_table();
        } else {
            modal_add_snmp_device.value.close();
            table_snmp_devices.value.refresh_table();
        }
    };

    const edit = async function (params) {
        const url = edit_snmp_device_url;

        const result = await ntopng_utility.http_post_request(url, { ...rest_params, ...params }, false, true);
        if (result.rc < 0) {
            modal_add_snmp_device.value.show_bad_feedback(result.rc_str_hr);
        } else {
            modal_add_snmp_device.value.close();
            table_snmp_devices.value.refresh_table();
        }
    };

    /* ************************************** */
    /* Functions to handle the deletes */

    /* Function to handle delete button */
    function click_button_delete(event) {
        row_to_delete.value = event.row;
        modal_delete_snmp_device.value.show(1, row_to_delete.value);
    }

    /* Function to delete host to scan */
    const delete_row = async function () {

        const row = row_to_delete.value;
        const url = delete_snmp_device_url;
        rest_params.host = row.ip;
        await ntopng_utility.http_post_request(url, rest_params);
        table_snmp_devices.value.refresh_table();
    };

    /* ************************************** */

    function click_button_export_snmp_device_data(event) {
        const row = event.row;
        const params = {
            host: row.ip
        };
        ntopng_url_manager$1.go_to_url(`${download_snmp_device_log_url}?host=${row.ip}`, params);
    }
    /* ************************************** */

    function click_button_edit(event) {
        const row_to_edit = event.row;
        modal_add_snmp_device.value.show(row_to_edit);
    }

    /* ************************************** */

    function click_button_timeseries(event) {
        const row = event.row;
        debugger;
        window.location.href = `${linksUtils.getSNMPDetailsPageURL(row.column_ip, http_prefix)}&page=historical`;
    }

    /* ************************************** */
    /* Table Buttons handler */
    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_edit": click_button_edit,
            "click_button_delete": click_button_delete,
            "click_button_export_snmp_device_data": click_button_export_snmp_device_data,
            "click_button_timeseries": click_button_timeseries
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */
    /* Functions to handle footer buttons */

    // function to open modal
    function ping_all_devices() {
        modal_delete_snmp_device.value.show(2);
    }

    // function to exec command
    const exec_ping_all = async function () {
        const url = ping_all_devices_url;
        await ntopng_utility.http_post_request(url, rest_params);
    };

    // function to open modal
    function delete_all_unresponsive_devices() {
        modal_delete_snmp_device.value.show(3);
    }

    // function to open modal
    function delete_all_devices() {
        modal_delete_snmp_device.value.show(4);
    }

    // function to exec command
    const delete_unresponsive = async function () {
        const url = delete_snmp_unresponsive_devices_url;
        await ntopng_utility.http_post_request(url, rest_params);
        table_snmp_devices.value.refresh_table();
    };

    // function to exec command
    const delete_all = async function () {
        const url = delete_all_snmp_devices_url;
        await ntopng_utility.http_post_request(url, rest_params);
        table_snmp_devices.value.refresh_table();
    };

    // function to periodically refresh table content
    const refreshTableContent = function () {
        table_snmp_devices.value.refresh_table(true);

        // set next refresh timeout
        timeoutId.value = setTimeout(() => {
            refreshTableContent();
        }, tableRefreshRate);
    };

    /* ************************************** */

    onBeforeMount(() => {
        ntopng_url_manager$1.set_key_to_url("verbose", true);
        load_table_filters_array();

        // start table refresh
        timeoutId.value = setTimeout(() => {
            refreshTableContent();
        }, tableRefreshRate);
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$13, [
          (!props.context.is_polling_enabled)
            ? (openBlock(), createElementBlock("div", _hoisted_2$V, [
                createBaseVNode("span", { innerHTML: unref(active_alert_text) }, null, 8 /* PROPS */, _hoisted_3$O)
              ]))
            : createCommentVNode("v-if", true),
          (props.context.devices_limit_crossed)
            ? (openBlock(), createElementBlock("div", _hoisted_4$M, [
                createBaseVNode("span", { innerHTML: unref(max_num_reached_alert_text) }, null, 8 /* PROPS */, _hoisted_5$F)
              ]))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", _hoisted_6$y, [
            createBaseVNode("div", _hoisted_7$t, [
              (import_with_success.value)
                ? (openBlock(), createElementBlock("div", _hoisted_8$q, [
                    _cache[0] || (_cache[0] = createBaseVNode("span", { class: "text-success me-1" }, null, -1 /* CACHED */)),
                    createBaseVNode("span", null, toDisplayString(import_ok_text.value), 1 /* TEXT */)
                  ]))
                : createCommentVNode("v-if", true),
              createVNode(script$2w, {
                ref_key: "table_snmp_devices",
                ref: table_snmp_devices,
                table_id: table_id.value,
                csrf: unref(csrf),
                showLoading: true,
                f_map_columns: map_table_def_columns,
                get_extra_params_obj: get_extra_params_obj,
                f_sort_rows: columns_sorting,
                onCustom_event: on_table_custom_event,
                onRows_loaded: change_filter_labels
              }, {
                custom_buttons: withCtx(() => [
                  createVNode(script$1d, {
                    ref_key: "modal_delete_snmp_device",
                    ref: modal_delete_snmp_device,
                    onDelete: delete_row,
                    onPing_all: exec_ping_all,
                    onPrune: delete_unresponsive,
                    onDelete_all: delete_all
                  }, null, 512 /* NEED_PATCH */),
                  createBaseVNode("button", {
                    class: "btn btn-link",
                    type: "button",
                    ref: "add_snmp_device",
                    onClick: add_snmp_device
                  }, [...(_cache[1] || (_cache[1] = [
                    createBaseVNode("i", { class: "fas fa-plus" }, null, -1 /* CACHED */)
                  ]))], 512 /* NEED_PATCH */),
                  createBaseVNode("button", {
                    class: "btn btn-link",
                    type: "button",
                    onClick: import_snmp_devices
                  }, [
                    createBaseVNode("i", {
                      class: "fa-solid fa-file-arrow-down",
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      title: _i18n('snmp.import_devices')
                    }, null, 8 /* PROPS */, _hoisted_9$n)
                  ]),
                  createBaseVNode("a", {
                    class: "btn btn-link",
                    download: "snmp_config.json",
                    href: config_export_url
                  }, [
                    createBaseVNode("i", {
                      class: "fa-solid fa-file-arrow-up",
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      title: _i18n('snmp.export_devices')
                    }, null, 8 /* PROPS */, _hoisted_10$k)
                  ])
                ]),
                custom_header: withCtx(() => [
                  (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
                    return (openBlock(), createElementBlock("div", _hoisted_11$i, [
                      createBaseVNode("span", _hoisted_12$f, [
                        createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                      ]),
                      createVNode(script$2H, {
                        selected_option: item['current_option'],
                        "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                        theme: "bootstrap-5",
                        dropdown_size: "small",
                        disabled: loading.value,
                        options: item['options'],
                        onSelect_option: add_table_filter
                      }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "disabled", "options"])
                    ]))
                  }), 256 /* UNKEYED_FRAGMENT */)),
                  createBaseVNode("div", _hoisted_13$e, [
                    createBaseVNode("div", {
                      class: "btn btn-sm btn-primary mt-2 me-3",
                      type: "button",
                      onClick: reset_filters
                    }, toDisplayString(_i18n('reset')), 1 /* TEXT */),
                    createVNode(script$2v, {
                      show: loading.value,
                      size: "1rem",
                      class: "me-1"
                    }, null, 8 /* PROPS */, ["show"])
                  ])
                ]),
                _: 1 /* STABLE */
              }, 8 /* PROPS */, ["table_id", "csrf"])
            ])
          ]),
          createBaseVNode("div", _hoisted_14$d, [
            createBaseVNode("a", {
              href: manage_config_url,
              class: "btn btn-secondary"
            }, [
              _cache[2] || (_cache[2] = createBaseVNode("i", { class: "fas fa-tasks" }, null, -1 /* CACHED */)),
              createTextVNode(" " + toDisplayString(_i18n("manage_configurations.manage_configuration")), 1 /* TEXT */)
            ]),
            (props.context.buttonsVisibility.pingDevices)
              ? (openBlock(), createElementBlock("button", {
                  key: 0,
                  type: "button",
                  ref: "ping_all",
                  onClick: ping_all_devices,
                  class: normalizeClass(["btn btn-warning ms-1", { disabled: total_rows.value == 0 }])
                }, [
                  _cache[3] || (_cache[3] = createBaseVNode("i", { class: "fas fa-heartbeat" }, null, -1 /* CACHED */)),
                  createTextVNode(" " + toDisplayString(_i18n("snmp.ping_devices")), 1 /* TEXT */)
                ], 2 /* CLASS */))
              : createCommentVNode("v-if", true),
            (props.context.buttonsVisibility.isAdministrator)
              ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                  createBaseVNode("button", {
                    type: "button",
                    ref: "delete_all_unresponsive",
                    onClick: delete_all_unresponsive_devices,
                    class: normalizeClass(["btn btn-danger ms-1", { disabled: props.context.buttonsVisibility.pruneDevices == 0 }])
                  }, [
                    _cache[4] || (_cache[4] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                    createTextVNode(" " + toDisplayString(_i18n("snmp.delete_unresponsive_devices")), 1 /* TEXT */)
                  ], 2 /* CLASS */),
                  createBaseVNode("button", {
                    type: "button",
                    ref: "delete_all",
                    onClick: delete_all_devices,
                    class: normalizeClass(["btn btn-danger ms-1", { disabled: total_rows.value == 0 }])
                  }, [
                    _cache[5] || (_cache[5] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                    createTextVNode(" " + toDisplayString(_i18n("snmp.delete_all_devices")), 1 /* TEXT */)
                  ], 2 /* CLASS */)
                ], 64 /* STABLE_FRAGMENT */))
              : createCommentVNode("v-if", true)
          ])
        ]),
        createVNode(script$1e, {
          ref_key: "modal_add_snmp_device",
          ref: modal_add_snmp_device,
          context: __props.context,
          onAdd: add_snmp_device_rest,
          onEdit: edit
        }, null, 8 /* PROPS */, ["context"]),
        createVNode(script$1c, {
          ref_key: "modal_import_snmp_devices",
          ref: modal_import_snmp_devices,
          context: __props.context,
          onAdd: import_snmp_devices_rest
        }, null, 8 /* PROPS */, ["context"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$1b.__file = "http_src/vue/page-snmp-devices.vue";

    const _hoisted_1$12 = { class: "row form-group mb-3 has-feedback" };
    const _hoisted_2$U = { class: "col col-md-12" };
    const _hoisted_3$N = { class: "form-label" };
    const _hoisted_4$L = { class: "row form-group mb-3 has-feedback" };
    const _hoisted_5$E = { class: "col col-md-12" };
    const _hoisted_6$x = { class: "form-label" };
    const _hoisted_7$s = { class: "row form-group mb-3" };
    const _hoisted_8$p = { class: "col col-md-12" };
    const _hoisted_9$m = { class: "form-label" };
    const _hoisted_10$j = { class: "form-check form-switch" };
    const _hoisted_11$h = { class: "row form-group mb-3" };
    const _hoisted_12$e = { class: "col col-md-6" };
    const _hoisted_13$d = { class: "form-label" };
    const _hoisted_14$c = {
      name: "category",
      class: "form-select",
      readonly: "",
      disabled: "disabled"
    };
    const _hoisted_15$c = { class: "col col-md-6" };
    const _hoisted_16$c = { class: "form-label" };
    const _hoisted_17$c = ["disabled"];
    const _hoisted_18$9 = ["disabled"];


    var script$1a = {
      __name: 'modal-edit-blacklist',
      props: {},
      emits: ['edit_blacklist', 'reset_blacklist'],
      setup(__props, { expose: __expose, emit: __emit }) {

    let title_delete = ref(i18n('category_lists.edit_list'));
    const modal_id = ref(null);
    const list_name = ref(null);
    const daily_frequency = ref(null);
    const hourly_frequency = ref(null);
    const manual_frequency = ref(null);
    const url = ref(null);
    const list_update = ref(null);
    const enable_blacklist = ref(null);
    const category_name = ref(null);
    const emit = __emit;
    const valid_url = ref(true);

    const show = (blacklist) => {
        list_name.value.value = blacklist.name;
        url.value.value = blacklist.url;
        category_name.value.value = blacklist.category_id;
        category_name.value.innerHTML = blacklist.category;
        category_name.value.setAttribute('selected', 'selected');
        if (blacklist.status == "enabled") {
            enable_blacklist.value.value = true;
            enable_blacklist.value.checked = true;
        } else {
            enable_blacklist.value.value = false;
            enable_blacklist.value.checked = false;
        }

        if (blacklist.update_frequency == 86400) {
            daily_frequency.value.setAttribute('selected', 'selected');
        } else if (blacklist.update_frequency == 3600) {
            hourly_frequency.value.setAttribute('selected', 'selected');
        } else {
            manual_frequency.value.setAttribute('selected', 'selected');
        }

        modal_id.value.show();
    };

    const validate_url = function () {
        valid_url.value = regexValidation.validateURL(url.value.value);
    };

    const change_checkbox = function () {
        let checked = enable_blacklist.value.value;
        if (checked == "true") {
            enable_blacklist.value.value = false;
        } else {
            enable_blacklist.value.value = true;
        }
    };

    const edit_blacklist_ = () => {
        let checked = enable_blacklist.value.value;
        if (checked == "true") {
            checked = 'on';
        } else {
            checked = 'off';
        }

        const params = {
            list_name: list_name.value.value,
            url: url.value.value,
            category: category_name.value.value,
            list_enabled: checked,
            list_update: list_update.value[list_update.value.selectedIndex].value
        };

        emit('edit_blacklist', params);

        close();
    };


    const load_default_blacklist = () => {
        let checked = enable_blacklist.value.value;
        if (checked == "true") {
            checked = 'on';
        } else {
            checked = 'off';
        }

        const params = {
            list_name: list_name.value.value,
            category: category_name.value.value,
            list_enabled: checked,
            list_update: list_update.value[list_update.value.selectedIndex].value
        };

        emit('reset_blacklist', params);

        close();
    };

    const close = () => {
        modal_id.value.close();
    };


    __expose({ show, close });

    onMounted(() => { });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title_delete)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Modal "),
          createBaseVNode("div", _hoisted_1$12, [
            createBaseVNode("div", _hoisted_2$U, [
              createBaseVNode("label", _hoisted_3$N, toDisplayString(_i18n("name")), 1 /* TEXT */),
              createBaseVNode("input", {
                ref_key: "list_name",
                ref: list_name,
                class: "form-control",
                type: "text",
                disabled: "disabled",
                readonly: ""
              }, null, 512 /* NEED_PATCH */)
            ])
          ]),
          createBaseVNode("div", _hoisted_4$L, [
            createBaseVNode("div", _hoisted_5$E, [
              createBaseVNode("label", _hoisted_6$x, toDisplayString(_i18n("flow_details.url")), 1 /* TEXT */),
              createBaseVNode("input", {
                ref_key: "url",
                ref: url,
                class: "form-control",
                type: "text",
                onInput: validate_url
              }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
            ])
          ]),
          createBaseVNode("div", _hoisted_7$s, [
            createBaseVNode("div", _hoisted_8$p, [
              createBaseVNode("label", _hoisted_9$m, toDisplayString(_i18n("category_lists.enabled")), 1 /* TEXT */),
              createBaseVNode("div", _hoisted_10$j, [
                createBaseVNode("input", {
                  class: "form-check-input",
                  type: "checkbox",
                  ref_key: "enable_blacklist",
                  ref: enable_blacklist,
                  id: "enable_blacklist",
                  onClick: change_checkbox
                }, null, 512 /* NEED_PATCH */)
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_11$h, [
            createBaseVNode("div", _hoisted_12$e, [
              createBaseVNode("label", _hoisted_13$d, toDisplayString(_i18n("category")), 1 /* TEXT */),
              createBaseVNode("select", _hoisted_14$c, [
                createBaseVNode("option", {
                  ref_key: "category_name",
                  ref: category_name,
                  selected: ""
                }, null, 512 /* NEED_PATCH */)
              ])
            ]),
            createBaseVNode("div", _hoisted_15$c, [
              createBaseVNode("label", _hoisted_16$c, toDisplayString(_i18n("category_lists.update_frequency")), 1 /* TEXT */),
              createBaseVNode("select", {
                ref_key: "list_update",
                ref: list_update,
                class: "form-select"
              }, [
                createBaseVNode("option", {
                  ref_key: "daily_frequency",
                  ref: daily_frequency,
                  value: "86400"
                }, toDisplayString(_i18n("alerts_thresholds_config.daily")), 513 /* TEXT, NEED_PATCH */),
                createBaseVNode("option", {
                  ref_key: "hourly_frequency",
                  ref: hourly_frequency,
                  value: "3600"
                }, toDisplayString(_i18n("alerts_thresholds_config.hourly")), 513 /* TEXT, NEED_PATCH */),
                createBaseVNode("option", {
                  ref_key: "manual_frequency",
                  ref: manual_frequency,
                  value: "0"
                }, toDisplayString(_i18n("alerts_thresholds_config.manual")), 513 /* TEXT, NEED_PATCH */)
              ], 512 /* NEED_PATCH */)
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: load_default_blacklist,
            disabled: !valid_url.value,
            class: "btn btn-secondary btn-block"
          }, toDisplayString(_i18n('category_lists.reset_list')), 9 /* TEXT, PROPS */, _hoisted_17$c),
          createBaseVNode("button", {
            type: "button",
            onClick: edit_blacklist_,
            disabled: !valid_url.value,
            class: "btn btn-primary btn-block"
          }, toDisplayString(_i18n('category_lists.edit_list')), 9 /* TEXT, PROPS */, _hoisted_18$9)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$1a.__file = "http_src/vue/modal-edit-blacklist.vue";

    const _hoisted_1$11 = { class: "m-2 mb-3" };
    const _hoisted_2$T = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_3$M = { class: "no-wrap d-flex align-items-center filters-label" };

    /* ************************************** */


    var script$19 = {
      __name: 'page-blacklists',
      props: {
        context: Object,
    },
      setup(__props) {
    const props = __props;

    /* ************************************** */

    const table_id = ref('blacklists');
    const table_blacklists = ref(null);
    const csrf = props.context.csrf;
    const filter_table_array = ref([]);
    const filters = ref([]);
    const loading = ref(false);
    const blacklist_chart_url = `${http_prefix}/lua/admin/blacklists.lua?page=charts&ts_query=blacklist_name:_BS_NAME_&ts_schema=top:blacklist:hits`;
    const modal_edit_blacklist = ref(null);

    /* ************************************** */
    function column_data(col, row) {
        if (col.id == "column_err_interfaces") {
            return row["column_err_interfaces_num"];
        }
        return row[col.data.data_field];
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            let r0_col = column_data(col, r0);
            let r1_col = column_data(col, r1);

            if (col.id == "name" || col.id == "status" || col.id == "category") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else {
                const lower_value = 0;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            }
        }
    }

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "name": (value, row) => {
                return value;
            },
            "status": (value, row) => {
                if (value === "enabled") {
                    return `<span class="badge bg-success">${i18n("category_lists.enabled")}</span>`
                } else if (value === "disabled") {
                    return `<span class="badge bg-danger">${i18n("nedge.status_disabled")}</span>`
                } else {
                    return `<span class="badge bg-danger">${i18n("error")}</span>`
                }
            },
            "category": (value, row) => {
                return value;
            },
            "update_frequency": (value, row) => {
                if (value == 86400) {
                    return i18n("alerts_thresholds_config.daily")
                } else if (value == 3600) {
                    return i18n("alerts_thresholds_config.hourly")
                } else {
                    return i18n("alerts_thresholds_config.manual")
                }
            },
            "last_update": (value, row) => {
                if (value > 0) {
                    return NtopUtils$1.secondsToTime((Math.round(new Date().getTime() / 1000)) - value)
                }
                return ''
            },
            "entries": (value, row) => {
                if (value > 0) {
                    let danger_icon = '';
                    return `${formatterUtils.getFormatter("full_number")(value)}${danger_icon}`
                }
                return ''
            },
            "hits": (value, row) => {
                if (value > 0) {
                    let danger_icon = '';
                    return `${formatterUtils.getFormatter("full_number")(value)}${danger_icon}`
                }
                return ''
            },
        };
        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];

            if (c.id == "actions") {
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        current_class = current_class.filter((class_item) => class_item != "disabled");
                        if ((row.status != "enabled" || row.hits == 0) && b.id == "chart_blacklist") {
                            current_class.push("disabled");
                        } else if ((row.status != "error" && row.status != "enabled") && b.id == "refresh_blacklist") {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            /* Setting the basic label */
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            /* Getting the currently selected filter */
            const url_entry = ntopng_url_manager.get_url_entry(el.id);
            el.options.forEach((option) => {
                if (option.value.toString() === url_entry) {
                    el.current_option = option;
                }
            });
        });
    }

    /* ************************************** */

    function add_filters_to_rows() {
        const filters = document.querySelectorAll('.tableFilter');
        filters.forEach(filter => {
            filter.addEventListener('click', add_filter_from_table_element);
        });
    }

    /* ************************************** */

    function change_filter_labels() {
        add_filters_to_rows();
    }

    /* ************************************** */

    function add_table_filter(opt) {
        ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
        table_blacklists.value.refresh_table();
        load_table_filters_array();
    }

    /* ************************************** */

    function set_filters_list(res) {
        if (!res) {
            filter_table_array.value = filters.value.filter((t) => {
                if (t.show_with_key) {
                    const key = ntopng_url_manager.get_url_entry(t.show_with_key);
                    if (key !== t.show_with_value) {
                        return false
                    }
                }
                return true
            });
        } else {
            filters.value = res.map((t) => {
                const key_in_url = ntopng_url_manager.get_url_entry(t.name);
                if (key_in_url === null) {
                    ntopng_url_manager.set_key_to_url(t.name, t.value[0].value);
                }
                return {
                    id: t.name,
                    label: t.label,
                    title: t.tooltip,
                    options: t.value,
                    show_with_key: t.show_with_key,
                    show_with_value: t.show_with_value,
                };
            });
            set_filters_list();
            return;
        }
        set_filter_array_label();
    }

    /* ************************************** */

    async function load_table_filters_array() {
        /* Clear the interval 2 times just in case, being this function async, 
            it could happen some strange behavior */
        loading.value = true;
        let extra_params = get_extra_params_obj();
        let url_params = ntopng_url_manager.obj_to_url_params(extra_params);
        const url = `${http_prefix}/lua/rest/v2/get/system/blacklists/blacklists_filters.lua?${url_params}`;
        const res = await ntopng_utility.http_request(url);
        set_filters_list(res);
        loading.value = false;
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    async function click_button_charts(event) {
        const row = event.row;
        window.open(blacklist_chart_url.replace("_BS_NAME_", row.name));
    }

    /* ************************************** */

    async function edit_blacklist(params) {
        params.csrf = props.context.csrf;
        let url = `${http_prefix}/lua/rest/v2/edit/system/edit_blacklist.lua`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
        } catch (err) {
            console.error(err);
        }
        setTimeout(() => { table_blacklists.value.refresh_table(); }, 2000 /* resfresh after 5 seconds */);
    }

    async function reset_blacklist(params) {
        params.csrf = props.context.csrf;
        params.reset_url = true;
        let url = `${http_prefix}/lua/rest/v2/edit/system/edit_blacklist.lua`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            await ntopng_utility.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
        } catch (err) {
            console.error(err);
        }
        setTimeout(() => { table_blacklists.value.refresh_table(); }, 2000 /* resfresh after 5 seconds */);
    }

    /* ************************************** */

    async function click_button_edit(event) {
        modal_edit_blacklist.value.show(event.row);
    }

    /* ************************************** */

    async function click_button_refresh(event) {
        const row = event.row;
        const url = `${http_prefix}/lua/rest/v2/get/system/blacklists/update_blacklist.lua?list_name=${row.name}`;
        await ntopng_utility.http_request(url);
        table_blacklists.value.refresh_table();
        setTimeout(() => { table_blacklists.value.refresh_table(); }, 2000 /* resfresh after 5 seconds */);
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_chart": click_button_charts,
            "click_edit": click_button_edit,
            "click_refresh": click_button_refresh,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    onBeforeMount(() => {
        load_table_filters_array();
    });

    /* ************************************** */

    onMounted(() => { });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$11, [
          createVNode(script$2w, {
            ref_key: "table_blacklists",
            ref: table_blacklists,
            table_id: table_id.value,
            csrf: unref(csrf),
            f_map_columns: map_table_def_columns,
            get_extra_params_obj: get_extra_params_obj,
            f_sort_rows: columns_sorting,
            onCustom_event: on_table_custom_event,
            onRows_loaded: change_filter_labels,
            showLoading: true
          }, {
            custom_header: withCtx(() => [
              (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
                return (openBlock(), createElementBlock("div", _hoisted_2$T, [
                  createBaseVNode("span", _hoisted_3$M, [
                    createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                  ]),
                  createVNode(script$2H, {
                    selected_option: item['current_option'],
                    "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                    theme: "bootstrap-5",
                    dropdown_size: "small",
                    disabled: loading.value,
                    options: item['options'],
                    onSelect_option: add_table_filter
                  }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "disabled", "options"])
                ]))
              }), 256 /* UNKEYED_FRAGMENT */))
            ]),
            _: 1 /* STABLE */
          }, 8 /* PROPS */, ["table_id", "csrf"])
        ]),
        createVNode(script$1a, {
          onEdit_blacklist: edit_blacklist,
          onReset_blacklist: reset_blacklist,
          ref_key: "modal_edit_blacklist",
          ref: modal_edit_blacklist
        }, null, 512 /* NEED_PATCH */)
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$19.__file = "http_src/vue/page-blacklists.vue";

    const _hoisted_1$10 = { class: "row" };
    const _hoisted_2$S = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$L = { class: "mt-4 card card-shadow" };
    const _hoisted_4$K = { class: "card-body" };


    var script$18 = {
      __name: 'page-historical-flow-details',
      props: {
      context: Object,
    },
      setup(__props) {

    const table_id = ref('historical_flow_details');
    const props = __props;

    const stats_rows = ref([]);

    const print_html_title = function (name) {
      return (name || "");
    };

    const print_stats_row = function (value) {
      return value;
    };

    onMounted(async () => {
      const extra_params = ntopng_url_manager.get_url_object();
      const url_params = ntopng_url_manager.obj_to_url_params(extra_params);
      const historical_flow_stats = await ntopng_utility.http_request(`${http_prefix}${props.context.url}?${url_params}`);
      stats_rows.value = historical_flow_stats;
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$10, [
        createBaseVNode("div", _hoisted_2$S, [
          createBaseVNode("div", _hoisted_3$L, [
            createBaseVNode("div", _hoisted_4$K, [
              createVNode(script$2d, {
                horizontal: true,
                id: table_id.value,
                rows: stats_rows.value,
                print_html_title: print_html_title,
                print_html_row: print_stats_row
              }, null, 8 /* PROPS */, ["id", "rows"])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$18.__file = "http_src/vue/page-historical-flow-details.vue";

    const _hoisted_1$$ = { class: "col-12 mb-2 mt-2" };
    const _hoisted_2$R = { key: 0 };
    const _hoisted_3$K = { class: "button-group mb-2 d-flex align-items-center" };
    const _hoisted_4$J = {
      class: "form-group d-flex align-items-end",
      style: {"flex-wrap":"wrap"}
    };
    const _hoisted_5$D = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_6$w = { class: "no-wrap d-flex align-items-center filters-label fs-6" };
    const _hoisted_7$r = { class: "d-flex justify-content-center align-items-center" };
    const _hoisted_8$o = { class: "card h-100 overflow-hidden" };
    const _hoisted_9$l = { class: "mt-3" };
    const _hoisted_10$i = { class: "m-3 card card-shadow" };
    const _hoisted_11$g = { class: "card-body" };
    const _hoisted_12$d = { class: "card-footer" };
    const _hoisted_13$c = {
      key: 1,
      class: "col-12 alert alert-info alert-dismissable"
    };

    /* ******************************************************************** */

    const id_date_time_picker = "date_time_picker";
    const min_time_interval_id = "10_min";
    const basic_source_type = "snmp_qos";
    const height_per_row$3 = 62.5;

    var script$17 = {
      __name: 'page-snmp-qos',
      props: {
      context: Object,
    },
      setup(__props) {

    /* Imports */
    const _i18n = (t) => i18n(t);
    const props = __props;

    /* Consts */
    const chart_type = ntopChartApex.typeChart.TS_LINE;
    const all_qos_id = ref("chart_qos_all");
    const group_option_mode = timeseriesUtils.getGroupOptionMode('1_chart_x_metric');
    const filter_table_array = ref([]);
    const loading = ref(true);
    const loading_chart = ref(true);
    const filters = ref([]);
    const is_qos_polled = ref(false);
    const qos_not_polled_yet = _i18n('snmp.snmp_qos_info_not_polled');
    const note_list = [
      _i18n("snmp.snmp_note_periodic_interfaces_polling"),
      _i18n("snmp.snmp_note_thpt_calc"),
      _i18n("snmp.snmp_lldp_cdp_descr")
    ];
    const time_preset_list = [
      { value: "10_min", label: i18n('show_alerts.presets.10_min'), currently_active: false },
      { value: "30_min", label: i18n('show_alerts.presets.30_min'), currently_active: true },
      { value: "hour", label: i18n('show_alerts.presets.hour'), currently_active: false },
      { value: "2_hours", label: i18n('show_alerts.presets.2_hours'), currently_active: false },
      { value: "6_hours", label: i18n('show_alerts.presets.6_hours'), currently_active: false },
      { value: "12_hours", label: i18n('show_alerts.presets.12_hours'), currently_active: false },
      { value: "day", label: i18n('show_alerts.presets.day'), currently_active: false },
      { value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
      { value: "month", label: i18n('show_alerts.presets.month'), currently_active: false },
      { value: "year", label: i18n('show_alerts.presets.year'), currently_active: false },
      { value: "custom", label: i18n('show_alerts.presets.custom'), currently_active: false, disabled: true, },
    ];
    const stats_columns = [
      { id: "metric", label: i18n("page_stats.metric") },
      { id: "avg", label: i18n("page_stats.average"), class: "text-end" },
      { id: "perc_95", label: i18n("page_stats.95_perc"), class: "text-end" },
      { id: "max", label: i18n("page_stats.max"), class: "text-end" },
      { id: "min", label: i18n("page_stats.min"), class: "text-end" },
    ];

    /* Height and width of the charts */
    const height = ref(null);

    /* Consts */

    /* *************************************************** */

    /* Return the base url of the REST API */
    const base_url = computed$1(() => {
      return `${http_prefix}/lua/pro/rest/v2/get/timeseries/ts_multi.lua`;
    });

    /* Refs */
    const all_qos_chart = ref(null);
    const timeseries_groups = ref([]);
    const ts_request = ref([{
      ts_query: "ifid:-1,device:%host,if_index:%interface_id,qos_class_id:%qos_class",
      ts_schema: "snmp_if:cbqos",
      tskey: "%interface_id",
      source_def: [
        "-1", /* System Interface */
        "%host",
        "%interface_id",
        "%qos_class"
      ]
    }]);
    const stats_rows = ref([]);

    function set_stats_rows(result) {
      stats_rows.value = [];
      result.forEach((options, i) => {
        options.series?.forEach((s, j) => {
          const ts_stats = s.statistics;
          const name = timeseries_groups.value[0].metric.timeseries[s.id].label;
          const formatter = formatterUtils.getFormatter(timeseries_groups.value[0].metric.measure_unit);
          const row = {
            metric: name,
            perc_95: formatter(ts_stats["95th_percentile"]),
            avg: formatter(ts_stats.average),
            max: formatter(ts_stats.max_val),
            min: formatter(ts_stats.min_val),
          };
          stats_rows.value.push(row);
        });
      });
    }

    /* *************************************************** */

    function print_stats_column(col) {
      return col.label;
    }

    /* *************************************************** */

    function print_stats_row(col, row) {
      let label = row[col.id];
      return label;
    }

    /* *************************************************** */

    function search_timeseries() {
      /* This is a trick to reload the entire timeseries component
       * if not reloaded there could be some issues with parameters
       */
      all_qos_id.value = ntopng_url_manager$1.get_url_entry("qos_class_id");
    }

    /* *************************************************** */

    async function add_filter(opt) {
      ntopng_url_manager$1.set_key_to_url(opt.key, `${opt.value}`);
      await load_table_filters_array(opt);
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager$1.get_url_object();
      return extra_params;
    };

    /* ************************************** */

    function set_filter_array_label() {
      filter_table_array.value.forEach((el, index) => {
        /* Setting the basic label */
        if (el.basic_label == null) {
          el.basic_label = el.label;
        }

        /* Getting the currently selected filter */
        const url_entry = ntopng_url_manager$1.get_url_entry(el.id);
        el.options.forEach((option) => {
          if (option.value.toString() === url_entry) {
            el.current_option = option;
          }
        });
      });
    }

    /* ************************************** */

    function set_filters_list(res, opt) {
      if (!res) {
        filter_table_array.value = filters.value.filter((t) => {
          if (t.show_with_key) {
            const key = ntopng_url_manager$1.get_url_entry(t.show_with_key);
            if (key !== t.show_with_value) {
              return false
            }
            const first_option = t.options[0];
            if (opt && opt.key !== first_option.key) {
              /* Changing the dropdown, changing the option too */
              ntopng_url_manager$1.set_key_to_url(first_option.key, first_option.value);
            }
          }
          return true
        });
      } else {
        filters.value = res.filter(t => t.value.length > 0).map((t) => {
          /* Do not add filters if no values are found */
          if (t.value.length == 0)
            return;

          const key_in_url = ntopng_url_manager$1.get_url_entry(t.name);
          if ((key_in_url === null || key_in_url === '') && t.value[0]) {
            ntopng_url_manager$1.set_key_to_url(t.name, t.value[0].value);
          }
          return {
            id: t.name,
            label: t.label,
            title: t.tooltip,
            options: t.value,
            show_with_key: t.show_with_key,
            show_with_value: t.show_with_value,
          };
        });
        if (filters.value.length > 0) {
          is_qos_polled.value = true;
          set_filters_list(null, opt);
        } else {
          is_qos_polled.value = false;
          setTimeout(load_table_filters_array, 10000);
        }
        return;
      }
      set_filter_array_label();
    }

    /* ************************************** */

    function substitute_params(params) {
      let res = {};
      const host = ntopng_url_manager$1.get_url_entry("host");
      const interface_id = ntopng_url_manager$1.get_url_entry("snmp_port_idx") || "0"; /* Default value */
      const qos_class = ntopng_url_manager$1.get_url_entry("qos_class_id") || "0"; /* Default value */
      if (!(host && interface_id)) {
        /* Safe check */
        return params
      }
      for (const value of params) {
        res.ts_query = value.ts_query.replace('%host', host).replace('%interface_id', interface_id).replace('%qos_class', qos_class);
        res.tskey = value.tskey.replace('%interface_id', interface_id);
        res.ts_schema = value.ts_schema;
        res.source_def = [];
        value.source_def.forEach((source, index) => {
          let tmp = source.replace('%host', host);
          tmp = tmp.replace('%interface_id', interface_id);
          tmp = tmp.replace('%qos_class', qos_class);
          res.source_def[index] = tmp;
        });
      }
      return res
    }

    /* ************************************** */

    async function load_table_filters_array(opt) {
      loading.value = true;
      let extra_params = get_extra_params_obj();
      let url_params = ntopng_url_manager$1.obj_to_url_params(extra_params);
      const url = `${http_prefix}/lua/pro/rest/v2/get/snmp/device/qos_filters.lua?${url_params}`;
      const res = await ntopng_utility.http_request(url);
      set_filters_list(res, opt);
      loading.value = false;
    }

    /* *************************************************** */

    /* The source_type can be found in the json and the source_array is automatically generated
     * by using the source_type
     */
    async function get_timeseries_groups_from_metric(metric_schema, source_def) {
      const status = {
        epoch_begin: ntopng_url_manager$1.get_url_entry("epoch_begin"),
        epoch_end: ntopng_url_manager$1.get_url_entry("epoch_end"),
      };
      const source_type = metricsManager.get_source_type_from_id(basic_source_type);
      const source_array = await metricsManager.get_source_array_from_value_array(http_prefix, source_type, source_def);
      const metric = await metricsManager.get_metric_from_schema(http_prefix, source_type, source_array, metric_schema, null, status);
      const ts_group = metricsManager.get_ts_group(source_type, source_array, metric, { past: false });
      return ts_group;
    }

    /* *************************************************** */

    async function retrieve_basic_info() {
      const tmp = substitute_params(ts_request.value);
      const metric_schema = tmp?.ts_schema;
      const source_def = tmp.source_def;
      if (source_def && metric_schema) {
        delete tmp.source_def; /* Remove the property otherwise it's going to be added to the REST */
        /* Return the timeseries group, info found in the json */
        if (timeseries_groups.value.length == 0) {
          const group = await get_timeseries_groups_from_metric(metric_schema, source_def);
          timeseries_groups.value.push(group);
        }
        return [tmp];
      }
      return null;
    }

    /* *************************************************** */

    /* Remove the property otherwise it's going to be added to the REST */
    function remove_extra_params(tmp) {
      for (const value of tmp) {
        if (value.source_def) {
          delete value.source_def;
        }
      }
    }

    /* *************************************************** */

    async function get_component_data(url, url_params, post_params) {
      let info = null;
      const data_url = `${url}?${url_params}`;
      info = ntopng_utility.http_post_request(data_url, post_params);

      return info;
    }

    /* *************************************************** */

    async function check_params() {
      const qos_class = ntopng_url_manager$1.get_url_entry("qos_class_id");
      if (!qos_class) {
        await load_table_filters_array();
      }
    }

    /* *************************************************** */

    /* This function run the REST API with the data */
    async function get_chart_options() {
      loading_chart.value = true;
      await check_params();
      const tmp = await retrieve_basic_info();
      let result = [];
      if (tmp) {
        remove_extra_params(tmp);
        const url = base_url.value;
        const post_params = {
          csrf: props.context.csrf,
          ifid: props.context.ifid,
          epoch_begin: ntopng_url_manager$1.get_url_entry("epoch_begin"),
          epoch_end: ntopng_url_manager$1.get_url_entry("epoch_end"),
          ts_requests: tmp
        };
        /* Have to be used this get_component_data, in order to create report too */
        result = await get_component_data(url, '', post_params);
      }
      set_stats_rows(result);
      /* Format the result in the format needed by Dygraph */
      result = timeseriesUtils.tsArrayToOptionsArray(result, timeseries_groups.value, group_option_mode, '');
      if (result[0]) {
        result[0].height = height.value;
      }
      loading_chart.value = false;
      return result?.[0];
    }

    /* *************************************************** */

    /* Run the init here */
    onBeforeMount(async () => {
      load_table_filters_array();
      height.value = 4 * height_per_row$3;
    });

    /* *************************************************** */

    onMounted(async () => { });

    /* *************************************************** */

    function epoch_change() {
      refresh_chart();
    }

    /* *************************************************** */

    /* Refresh function */
    async function refresh_chart() {
      if (all_qos_chart.value) {
        const result = await get_chart_options();
        all_qos_chart.value.update_chart_series(result.data);
      }
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$$, [
        (is_qos_polled.value)
          ? (openBlock(), createElementBlock("div", _hoisted_2$R, [
              createBaseVNode("div", _hoisted_3$K, [
                createCommentVNode(" TableHeader "),
                createBaseVNode("div", _hoisted_4$J, [
                  (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
                    return (openBlock(), createElementBlock("div", _hoisted_5$D, [
                      createBaseVNode("span", _hoisted_6$w, [
                        createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                      ]),
                      createVNode(script$2H, {
                        selected_option: item['current_option'],
                        "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                        theme: "bootstrap-5",
                        dropdown_size: "medium",
                        disabled: loading.value,
                        options: item['options'],
                        onSelect_option: add_filter
                      }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "disabled", "options"])
                    ]))
                  }), 256 /* UNKEYED_FRAGMENT */)),
                  createBaseVNode("div", _hoisted_7$r, [
                    createBaseVNode("div", {
                      class: "btn btn-sm btn-primary mb-1 me-3",
                      type: "button",
                      onClick: search_timeseries
                    }, toDisplayString(_i18n('search')), 1 /* TEXT */),
                    createVNode(script$2v, {
                      show: loading.value,
                      size: "1rem",
                      class: "me-1"
                    }, null, 8 /* PROPS */, ["show"])
                  ])
                ])
              ]),
              createBaseVNode("div", _hoisted_8$o, [
                createVNode(script$2y, { isLoading: loading_chart.value }, null, 8 /* PROPS */, ["isLoading"]),
                createVNode(script$2G, {
                  style: {"margin-top":"0.5rem"},
                  class: "ms-1",
                  id: id_date_time_picker,
                  enable_refresh: true,
                  ref: "date_time_picker",
                  onEpoch_change: epoch_change,
                  min_time_interval_id: min_time_interval_id,
                  custom_time_interval_list: time_preset_list
                }, null, 512 /* NEED_PATCH */),
                createBaseVNode("div", _hoisted_9$l, [
                  (openBlock(), createBlock(script$2i, {
                    ref_key: "all_qos_chart",
                    ref: all_qos_chart,
                    id: all_qos_id.value,
                    chart_type: unref(chart_type),
                    base_url_request: base_url.value,
                    get_custom_chart_options: get_chart_options,
                    register_on_status_change: false,
                    key: all_qos_id.value
                  }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"]))
                ]),
                createBaseVNode("div", _hoisted_10$i, [
                  createBaseVNode("div", _hoisted_11$g, [
                    createVNode(script$2d, {
                      id: "page_stats_bootstrap_table",
                      columns: stats_columns,
                      rows: stats_rows.value,
                      print_html_column: (col) => print_stats_column(col),
                      print_html_row: (col, row) => print_stats_row(col, row)
                    }, null, 8 /* PROPS */, ["rows", "print_html_column", "print_html_row"])
                  ])
                ])
              ]),
              createBaseVNode("div", _hoisted_12$d, [
                createVNode(script$2u, { note_list: note_list })
              ])
            ]))
          : (openBlock(), createElementBlock("div", _hoisted_13$c, [
              createBaseVNode("span", null, toDisplayString(unref(qos_not_polled_yet)), 1 /* TEXT */),
              _cache[0] || (_cache[0] = createBaseVNode("span", {
                class: "ms-2 spinner-border spinner-border-sm",
                role: "status"
              }, null, -1 /* CACHED */))
            ]))
      ]))
    }
    }

    };

    script$17.__file = "http_src/vue/page-snmp-qos.vue";

    var type = "Topology";
    var objects = {
    	countries: {
    		type: "GeometryCollection",
    		geometries: [
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							0
    						]
    					],
    					[
    						[
    							1
    						]
    					]
    				],
    				id: "242",
    				properties: {
    					name: "Fiji"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						2,
    						3,
    						4,
    						5,
    						6,
    						7,
    						8,
    						9,
    						10
    					]
    				],
    				id: "834",
    				properties: {
    					name: "Tanzania"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						11,
    						12,
    						13,
    						14
    					]
    				],
    				id: "732",
    				properties: {
    					name: "W. Sahara"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							15,
    							16,
    							17,
    							18
    						]
    					],
    					[
    						[
    							19
    						]
    					],
    					[
    						[
    							20
    						]
    					],
    					[
    						[
    							21
    						]
    					],
    					[
    						[
    							22
    						]
    					],
    					[
    						[
    							23
    						]
    					],
    					[
    						[
    							24
    						]
    					],
    					[
    						[
    							25
    						]
    					],
    					[
    						[
    							26
    						]
    					],
    					[
    						[
    							27
    						]
    					],
    					[
    						[
    							28
    						]
    					],
    					[
    						[
    							29
    						]
    					],
    					[
    						[
    							30
    						]
    					],
    					[
    						[
    							31
    						]
    					],
    					[
    						[
    							32
    						]
    					],
    					[
    						[
    							33
    						]
    					],
    					[
    						[
    							34
    						]
    					],
    					[
    						[
    							35
    						]
    					],
    					[
    						[
    							36
    						]
    					],
    					[
    						[
    							37
    						]
    					],
    					[
    						[
    							38
    						]
    					],
    					[
    						[
    							39
    						]
    					],
    					[
    						[
    							40
    						]
    					],
    					[
    						[
    							41
    						]
    					],
    					[
    						[
    							42
    						]
    					],
    					[
    						[
    							43
    						]
    					],
    					[
    						[
    							44
    						]
    					],
    					[
    						[
    							45
    						]
    					],
    					[
    						[
    							46
    						]
    					],
    					[
    						[
    							47
    						]
    					]
    				],
    				id: "124",
    				properties: {
    					name: "Canada"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-19,
    							48,
    							49,
    							50
    						]
    					],
    					[
    						[
    							51
    						]
    					],
    					[
    						[
    							52
    						]
    					],
    					[
    						[
    							53
    						]
    					],
    					[
    						[
    							54
    						]
    					],
    					[
    						[
    							55
    						]
    					],
    					[
    						[
    							56
    						]
    					],
    					[
    						[
    							57
    						]
    					],
    					[
    						[
    							-17,
    							58
    						]
    					],
    					[
    						[
    							59
    						]
    					]
    				],
    				id: "840",
    				properties: {
    					name: "United States of America"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						60,
    						61,
    						62,
    						63,
    						64,
    						65
    					]
    				],
    				id: "398",
    				properties: {
    					name: "Kazakhstan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-63,
    						66,
    						67,
    						68,
    						69
    					]
    				],
    				id: "860",
    				properties: {
    					name: "Uzbekistan"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							70,
    							71
    						]
    					],
    					[
    						[
    							72
    						]
    					],
    					[
    						[
    							73
    						]
    					],
    					[
    						[
    							74
    						]
    					]
    				],
    				id: "598",
    				properties: {
    					name: "Papua New Guinea"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-72,
    							75
    						]
    					],
    					[
    						[
    							76,
    							77
    						]
    					],
    					[
    						[
    							78
    						]
    					],
    					[
    						[
    							79,
    							80
    						]
    					],
    					[
    						[
    							81
    						]
    					],
    					[
    						[
    							82
    						]
    					],
    					[
    						[
    							83
    						]
    					],
    					[
    						[
    							84
    						]
    					],
    					[
    						[
    							85
    						]
    					],
    					[
    						[
    							86
    						]
    					],
    					[
    						[
    							87
    						]
    					],
    					[
    						[
    							88
    						]
    					],
    					[
    						[
    							89
    						]
    					]
    				],
    				id: "360",
    				properties: {
    					name: "Indonesia"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							90,
    							91
    						]
    					],
    					[
    						[
    							92,
    							93,
    							94,
    							95,
    							96,
    							97
    						]
    					]
    				],
    				id: "032",
    				properties: {
    					name: "Argentina"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-92,
    							98
    						]
    					],
    					[
    						[
    							99,
    							-95,
    							100,
    							101
    						]
    					]
    				],
    				id: "152",
    				properties: {
    					name: "Chile"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-8,
    						102,
    						103,
    						104,
    						105,
    						106,
    						107,
    						108,
    						109,
    						110,
    						111
    					]
    				],
    				id: "180",
    				properties: {
    					name: "Dem. Rep. Congo"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						112,
    						113,
    						114,
    						115
    					]
    				],
    				id: "706",
    				properties: {
    					name: "Somalia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-3,
    						116,
    						117,
    						118,
    						-113,
    						119
    					]
    				],
    				id: "404",
    				properties: {
    					name: "Kenya"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						120,
    						121,
    						122,
    						123,
    						124,
    						125,
    						126,
    						127
    					]
    				],
    				id: "729",
    				properties: {
    					name: "Sudan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-122,
    						128,
    						129,
    						130,
    						131
    					]
    				],
    				id: "148",
    				properties: {
    					name: "Chad"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						132,
    						133
    					]
    				],
    				id: "332",
    				properties: {
    					name: "Haiti"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-133,
    						134
    					]
    				],
    				id: "214",
    				properties: {
    					name: "Dominican Rep."
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							135
    						]
    					],
    					[
    						[
    							136
    						]
    					],
    					[
    						[
    							137
    						]
    					],
    					[
    						[
    							138
    						]
    					],
    					[
    						[
    							139
    						]
    					],
    					[
    						[
    							140
    						]
    					],
    					[
    						[
    							141,
    							142,
    							143
    						]
    					],
    					[
    						[
    							144
    						]
    					],
    					[
    						[
    							145
    						]
    					],
    					[
    						[
    							146,
    							147,
    							148,
    							149,
    							-66,
    							150,
    							151,
    							152,
    							153,
    							154,
    							155,
    							156,
    							157,
    							158,
    							159,
    							160,
    							161
    						]
    					],
    					[
    						[
    							162
    						]
    					],
    					[
    						[
    							163,
    							164
    						]
    					]
    				],
    				id: "643",
    				properties: {
    					name: "Russia"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							165
    						]
    					],
    					[
    						[
    							166
    						]
    					],
    					[
    						[
    							167
    						]
    					]
    				],
    				id: "044",
    				properties: {
    					name: "Bahamas"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						168
    					]
    				],
    				id: "238",
    				properties: {
    					name: "Falkland Is."
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							169
    						]
    					],
    					[
    						[
    							-161,
    							170,
    							171,
    							172
    						]
    					],
    					[
    						[
    							173
    						]
    					],
    					[
    						[
    							174
    						]
    					]
    				],
    				id: "578",
    				properties: {
    					name: "Norway"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						175
    					]
    				],
    				id: "304",
    				properties: {
    					name: "Greenland"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						176
    					]
    				],
    				id: "260",
    				properties: {
    					name: "Fr. S. Antarctic Lands"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						177,
    						-77
    					]
    				],
    				id: "626",
    				properties: {
    					name: "Timor-Leste"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						178,
    						179,
    						180,
    						181,
    						182,
    						183,
    						184
    					],
    					[
    						185
    					]
    				],
    				id: "710",
    				properties: {
    					name: "South Africa"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-186
    					]
    				],
    				id: "426",
    				properties: {
    					name: "Lesotho"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-50,
    						186,
    						187,
    						188,
    						189
    					]
    				],
    				id: "484",
    				properties: {
    					name: "Mexico"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						190,
    						191,
    						-93
    					]
    				],
    				id: "858",
    				properties: {
    					name: "Uruguay"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-191,
    						-98,
    						192,
    						193,
    						194,
    						195,
    						196,
    						197,
    						198,
    						199,
    						200
    					]
    				],
    				id: "076",
    				properties: {
    					name: "Brazil"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-194,
    						201,
    						-96,
    						-100,
    						202
    					]
    				],
    				id: "068",
    				properties: {
    					name: "Bolivia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-195,
    						-203,
    						-102,
    						203,
    						204,
    						205
    					]
    				],
    				id: "604",
    				properties: {
    					name: "Peru"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-196,
    						-206,
    						206,
    						207,
    						208,
    						209,
    						210
    					]
    				],
    				id: "170",
    				properties: {
    					name: "Colombia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-209,
    						211,
    						212,
    						213
    					]
    				],
    				id: "591",
    				properties: {
    					name: "Panama"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-213,
    						214,
    						215,
    						216
    					]
    				],
    				id: "188",
    				properties: {
    					name: "Costa Rica"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-216,
    						217,
    						218,
    						219
    					]
    				],
    				id: "558",
    				properties: {
    					name: "Nicaragua"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-219,
    						220,
    						221,
    						222,
    						223
    					]
    				],
    				id: "340",
    				properties: {
    					name: "Honduras"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-222,
    						224,
    						225
    					]
    				],
    				id: "222",
    				properties: {
    					name: "El Salvador"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-189,
    						226,
    						227,
    						-223,
    						-226,
    						228
    					]
    				],
    				id: "320",
    				properties: {
    					name: "Guatemala"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-188,
    						229,
    						-227
    					]
    				],
    				id: "084",
    				properties: {
    					name: "Belize"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-197,
    						-211,
    						230,
    						231
    					]
    				],
    				id: "862",
    				properties: {
    					name: "Venezuela"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-198,
    						-232,
    						232,
    						233
    					]
    				],
    				id: "328",
    				properties: {
    					name: "Guyana"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-199,
    						-234,
    						234,
    						235
    					]
    				],
    				id: "740",
    				properties: {
    					name: "Suriname"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-200,
    							-236,
    							236
    						]
    					],
    					[
    						[
    							237,
    							238,
    							239,
    							240,
    							241,
    							242,
    							243,
    							244
    						]
    					],
    					[
    						[
    							245
    						]
    					]
    				],
    				id: "250",
    				properties: {
    					name: "France"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-205,
    						246,
    						-207
    					]
    				],
    				id: "218",
    				properties: {
    					name: "Ecuador"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						247
    					]
    				],
    				id: "630",
    				properties: {
    					name: "Puerto Rico"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						248
    					]
    				],
    				id: "388",
    				properties: {
    					name: "Jamaica"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						249
    					]
    				],
    				id: "192",
    				properties: {
    					name: "Cuba"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-181,
    						250,
    						251,
    						252
    					]
    				],
    				id: "716",
    				properties: {
    					name: "Zimbabwe"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-180,
    						253,
    						254,
    						-251
    					]
    				],
    				id: "072",
    				properties: {
    					name: "Botswana"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-179,
    						255,
    						256,
    						257,
    						-254
    					]
    				],
    				id: "516",
    				properties: {
    					name: "Namibia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						258,
    						259,
    						260,
    						261,
    						262,
    						263,
    						264
    					]
    				],
    				id: "686",
    				properties: {
    					name: "Senegal"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-261,
    						265,
    						266,
    						267,
    						268,
    						269,
    						270
    					]
    				],
    				id: "466",
    				properties: {
    					name: "Mali"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-13,
    						271,
    						-266,
    						-260,
    						272
    					]
    				],
    				id: "478",
    				properties: {
    					name: "Mauritania"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						273,
    						274,
    						275,
    						276,
    						277
    					]
    				],
    				id: "204",
    				properties: {
    					name: "Benin"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-131,
    						278,
    						279,
    						-277,
    						280,
    						-268,
    						281,
    						282
    					]
    				],
    				id: "562",
    				properties: {
    					name: "Niger"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-278,
    						-280,
    						283,
    						284
    					]
    				],
    				id: "566",
    				properties: {
    					name: "Nigeria"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-130,
    						285,
    						286,
    						287,
    						288,
    						289,
    						-284,
    						-279
    					]
    				],
    				id: "120",
    				properties: {
    					name: "Cameroon"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-275,
    						290,
    						291,
    						292
    					]
    				],
    				id: "768",
    				properties: {
    					name: "Togo"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-292,
    						293,
    						294,
    						295
    					]
    				],
    				id: "288",
    				properties: {
    					name: "Ghana"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-270,
    						296,
    						-295,
    						297,
    						298,
    						299
    					]
    				],
    				id: "384",
    				properties: {
    					name: "Côte d'Ivoire"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-262,
    						-271,
    						-300,
    						300,
    						301,
    						302,
    						303
    					]
    				],
    				id: "324",
    				properties: {
    					name: "Guinea"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-263,
    						-304,
    						304
    					]
    				],
    				id: "624",
    				properties: {
    					name: "Guinea-Bissau"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-299,
    						305,
    						306,
    						-301
    					]
    				],
    				id: "430",
    				properties: {
    					name: "Liberia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-302,
    						-307,
    						307
    					]
    				],
    				id: "694",
    				properties: {
    					name: "Sierra Leone"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-269,
    						-281,
    						-276,
    						-293,
    						-296,
    						-297
    					]
    				],
    				id: "854",
    				properties: {
    					name: "Burkina Faso"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-108,
    						308,
    						-286,
    						-129,
    						-121,
    						309
    					]
    				],
    				id: "140",
    				properties: {
    					name: "Central African Rep."
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-107,
    						310,
    						311,
    						312,
    						-287,
    						-309
    					]
    				],
    				id: "178",
    				properties: {
    					name: "Congo"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-288,
    						-313,
    						313,
    						314
    					]
    				],
    				id: "266",
    				properties: {
    					name: "Gabon"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-289,
    						-315,
    						315
    					]
    				],
    				id: "226",
    				properties: {
    					name: "Eq. Guinea"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-7,
    						316,
    						317,
    						-252,
    						-255,
    						-258,
    						318,
    						-103
    					]
    				],
    				id: "894",
    				properties: {
    					name: "Zambia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-6,
    						319,
    						-317
    					]
    				],
    				id: "454",
    				properties: {
    					name: "Malawi"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-5,
    						320,
    						-184,
    						321,
    						-182,
    						-253,
    						-318,
    						-320
    					]
    				],
    				id: "508",
    				properties: {
    					name: "Mozambique"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-183,
    						-322
    					]
    				],
    				id: "748",
    				properties: {
    					name: "eSwatini"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-106,
    							322,
    							-311
    						]
    					],
    					[
    						[
    							-104,
    							-319,
    							-257,
    							323
    						]
    					]
    				],
    				id: "024",
    				properties: {
    					name: "Angola"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-9,
    						-112,
    						324
    					]
    				],
    				id: "108",
    				properties: {
    					name: "Burundi"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						325,
    						326,
    						327,
    						328,
    						329,
    						330,
    						331
    					]
    				],
    				id: "376",
    				properties: {
    					name: "Israel"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-331,
    						332,
    						333
    					]
    				],
    				id: "422",
    				properties: {
    					name: "Lebanon"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						334
    					]
    				],
    				id: "450",
    				properties: {
    					name: "Madagascar"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-327,
    						335
    					]
    				],
    				id: "275",
    				properties: {
    					name: "Palestine"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-265,
    						336
    					]
    				],
    				id: "270",
    				properties: {
    					name: "Gambia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						337,
    						338,
    						339
    					]
    				],
    				id: "788",
    				properties: {
    					name: "Tunisia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-12,
    						340,
    						341,
    						-338,
    						342,
    						-282,
    						-267,
    						-272
    					]
    				],
    				id: "012",
    				properties: {
    					name: "Algeria"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-326,
    						343,
    						344,
    						345,
    						346,
    						-328,
    						-336
    					]
    				],
    				id: "400",
    				properties: {
    					name: "Jordan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						347,
    						348,
    						349,
    						350,
    						351
    					]
    				],
    				id: "784",
    				properties: {
    					name: "United Arab Emirates"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						352,
    						353
    					]
    				],
    				id: "634",
    				properties: {
    					name: "Qatar"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						354,
    						355,
    						356
    					]
    				],
    				id: "414",
    				properties: {
    					name: "Kuwait"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-345,
    						357,
    						358,
    						359,
    						360,
    						-357,
    						361
    					]
    				],
    				id: "368",
    				properties: {
    					name: "Iraq"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-351,
    							362,
    							363,
    							364
    						]
    					],
    					[
    						[
    							-349,
    							365
    						]
    					]
    				],
    				id: "512",
    				properties: {
    					name: "Oman"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							366
    						]
    					],
    					[
    						[
    							367
    						]
    					]
    				],
    				id: "548",
    				properties: {
    					name: "Vanuatu"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						368,
    						369,
    						370,
    						371
    					]
    				],
    				id: "116",
    				properties: {
    					name: "Cambodia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-369,
    						372,
    						373,
    						374,
    						375,
    						376
    					]
    				],
    				id: "764",
    				properties: {
    					name: "Thailand"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-370,
    						-377,
    						377,
    						378,
    						379
    					]
    				],
    				id: "418",
    				properties: {
    					name: "Laos"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-376,
    						380,
    						381,
    						382,
    						383,
    						-378
    					]
    				],
    				id: "104",
    				properties: {
    					name: "Myanmar"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-371,
    						-380,
    						384,
    						385
    					]
    				],
    				id: "704",
    				properties: {
    					name: "Vietnam"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							386,
    							386,
    							386
    						]
    					],
    					[
    						[
    							-147,
    							387,
    							388,
    							389,
    							390
    						]
    					]
    				],
    				id: "408",
    				properties: {
    					name: "North Korea"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-389,
    						391
    					]
    				],
    				id: "410",
    				properties: {
    					name: "South Korea"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-149,
    						392
    					]
    				],
    				id: "496",
    				properties: {
    					name: "Mongolia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-383,
    						393,
    						394,
    						395,
    						396,
    						397,
    						398,
    						399,
    						400
    					]
    				],
    				id: "356",
    				properties: {
    					name: "India"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-382,
    						401,
    						-394
    					]
    				],
    				id: "050",
    				properties: {
    					name: "Bangladesh"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-400,
    						402
    					]
    				],
    				id: "064",
    				properties: {
    					name: "Bhutan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-398,
    						403
    					]
    				],
    				id: "524",
    				properties: {
    					name: "Nepal"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-396,
    						404,
    						405,
    						406,
    						407
    					]
    				],
    				id: "586",
    				properties: {
    					name: "Pakistan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-69,
    						408,
    						409,
    						-407,
    						410,
    						411
    					]
    				],
    				id: "004",
    				properties: {
    					name: "Afghanistan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-68,
    						412,
    						413,
    						-409
    					]
    				],
    				id: "762",
    				properties: {
    					name: "Tajikistan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-62,
    						414,
    						-413,
    						-67
    					]
    				],
    				id: "417",
    				properties: {
    					name: "Kyrgyzstan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-64,
    						-70,
    						-412,
    						415,
    						416
    					]
    				],
    				id: "795",
    				properties: {
    					name: "Turkmenistan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-360,
    						417,
    						418,
    						419,
    						420,
    						421,
    						-416,
    						-411,
    						-406,
    						422
    					]
    				],
    				id: "364",
    				properties: {
    					name: "Iran"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-332,
    						-334,
    						423,
    						424,
    						-358,
    						-344
    					]
    				],
    				id: "760",
    				properties: {
    					name: "Syria"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-420,
    						425,
    						426,
    						427,
    						428
    					]
    				],
    				id: "051",
    				properties: {
    					name: "Armenia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-172,
    						429,
    						430
    					]
    				],
    				id: "752",
    				properties: {
    					name: "Sweden"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-156,
    						431,
    						432,
    						433,
    						434
    					]
    				],
    				id: "112",
    				properties: {
    					name: "Belarus"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-155,
    						435,
    						-164,
    						436,
    						437,
    						438,
    						439,
    						440,
    						441,
    						442,
    						-432
    					]
    				],
    				id: "804",
    				properties: {
    					name: "Ukraine"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-433,
    						-443,
    						443,
    						444,
    						445,
    						446,
    						-142,
    						447
    					]
    				],
    				id: "616",
    				properties: {
    					name: "Poland"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						448,
    						449,
    						450,
    						451,
    						452,
    						453,
    						454
    					]
    				],
    				id: "040",
    				properties: {
    					name: "Austria"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-441,
    						455,
    						456,
    						457,
    						458,
    						-449,
    						459
    					]
    				],
    				id: "348",
    				properties: {
    					name: "Hungary"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-439,
    						460
    					]
    				],
    				id: "498",
    				properties: {
    					name: "Moldova"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-438,
    						461,
    						462,
    						463,
    						-456,
    						-440,
    						-461
    					]
    				],
    				id: "642",
    				properties: {
    					name: "Romania"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-434,
    						-448,
    						-144,
    						464,
    						465
    					]
    				],
    				id: "440",
    				properties: {
    					name: "Lithuania"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-157,
    						-435,
    						-466,
    						466,
    						467
    					]
    				],
    				id: "428",
    				properties: {
    					name: "Latvia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-158,
    						-468,
    						468
    					]
    				],
    				id: "233",
    				properties: {
    					name: "Estonia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-446,
    						469,
    						-453,
    						470,
    						-238,
    						471,
    						472,
    						473,
    						474,
    						475,
    						476
    					]
    				],
    				id: "276",
    				properties: {
    					name: "Germany"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-463,
    						477,
    						478,
    						479,
    						480,
    						481
    					]
    				],
    				id: "100",
    				properties: {
    					name: "Bulgaria"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							482
    						]
    					],
    					[
    						[
    							-480,
    							483,
    							484,
    							485,
    							486
    						]
    					]
    				],
    				id: "300",
    				properties: {
    					name: "Greece"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-359,
    							-425,
    							487,
    							488,
    							-427,
    							-418
    						]
    					],
    					[
    						[
    							-479,
    							489,
    							-484
    						]
    					]
    				],
    				id: "792",
    				properties: {
    					name: "Turkey"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-486,
    						490,
    						491,
    						492,
    						493
    					]
    				],
    				id: "008",
    				properties: {
    					name: "Albania"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-458,
    						494,
    						495,
    						496,
    						497,
    						498
    					]
    				],
    				id: "191",
    				properties: {
    					name: "Croatia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-452,
    						499,
    						-239,
    						-471
    					]
    				],
    				id: "756",
    				properties: {
    					name: "Switzerland"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-472,
    						-245,
    						500
    					]
    				],
    				id: "442",
    				properties: {
    					name: "Luxembourg"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-473,
    						-501,
    						-244,
    						501,
    						502
    					]
    				],
    				id: "056",
    				properties: {
    					name: "Belgium"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-474,
    						-503,
    						503
    					]
    				],
    				id: "528",
    				properties: {
    					name: "Netherlands"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						504,
    						505
    					]
    				],
    				id: "620",
    				properties: {
    					name: "Portugal"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-505,
    						506,
    						-242,
    						507
    					]
    				],
    				id: "724",
    				properties: {
    					name: "Spain"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						508,
    						509
    					]
    				],
    				id: "372",
    				properties: {
    					name: "Ireland"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						510
    					]
    				],
    				id: "540",
    				properties: {
    					name: "New Caledonia"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							511
    						]
    					],
    					[
    						[
    							512
    						]
    					],
    					[
    						[
    							513
    						]
    					],
    					[
    						[
    							514
    						]
    					],
    					[
    						[
    							515
    						]
    					]
    				],
    				id: "090",
    				properties: {
    					name: "Solomon Is."
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							516
    						]
    					],
    					[
    						[
    							517
    						]
    					]
    				],
    				id: "554",
    				properties: {
    					name: "New Zealand"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							518
    						]
    					],
    					[
    						[
    							519
    						]
    					]
    				],
    				id: "036",
    				properties: {
    					name: "Australia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						520
    					]
    				],
    				id: "144",
    				properties: {
    					name: "Sri Lanka"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							521
    						]
    					],
    					[
    						[
    							-61,
    							-150,
    							-393,
    							-148,
    							-391,
    							522,
    							-385,
    							-379,
    							-384,
    							-401,
    							-403,
    							-399,
    							-404,
    							-397,
    							-408,
    							-410,
    							-414,
    							-415
    						]
    					]
    				],
    				id: "156",
    				properties: {
    					name: "China"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						523
    					]
    				],
    				id: "158",
    				properties: {
    					name: "Taiwan"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-451,
    							524,
    							525,
    							-240,
    							-500
    						]
    					],
    					[
    						[
    							526
    						]
    					],
    					[
    						[
    							527
    						]
    					]
    				],
    				id: "380",
    				properties: {
    					name: "Italy"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-476,
    							528
    						]
    					],
    					[
    						[
    							529
    						]
    					]
    				],
    				id: "208",
    				properties: {
    					name: "Denmark"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-510,
    							530
    						]
    					],
    					[
    						[
    							531
    						]
    					]
    				],
    				id: "826",
    				properties: {
    					name: "United Kingdom"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						532
    					]
    				],
    				id: "352",
    				properties: {
    					name: "Iceland"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-152,
    							533,
    							-421,
    							-429,
    							534
    						]
    					],
    					[
    						[
    							-419,
    							-426
    						]
    					]
    				],
    				id: "031",
    				properties: {
    					name: "Azerbaijan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-153,
    						-535,
    						-428,
    						-489,
    						535
    					]
    				],
    				id: "268",
    				properties: {
    					name: "Georgia"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							536
    						]
    					],
    					[
    						[
    							537
    						]
    					],
    					[
    						[
    							538
    						]
    					],
    					[
    						[
    							539
    						]
    					],
    					[
    						[
    							540
    						]
    					],
    					[
    						[
    							541
    						]
    					],
    					[
    						[
    							542
    						]
    					]
    				],
    				id: "608",
    				properties: {
    					name: "Philippines"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							-374,
    							543
    						]
    					],
    					[
    						[
    							-81,
    							544,
    							545,
    							546
    						]
    					]
    				],
    				id: "458",
    				properties: {
    					name: "Malaysia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-546,
    						547
    					]
    				],
    				id: "096",
    				properties: {
    					name: "Brunei"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-450,
    						-459,
    						-499,
    						548,
    						-525
    					]
    				],
    				id: "705",
    				properties: {
    					name: "Slovenia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-160,
    						549,
    						-430,
    						-171
    					]
    				],
    				id: "246",
    				properties: {
    					name: "Finland"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-442,
    						-460,
    						-455,
    						550,
    						-444
    					]
    				],
    				id: "703",
    				properties: {
    					name: "Slovakia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-445,
    						-551,
    						-454,
    						-470
    					]
    				],
    				id: "203",
    				properties: {
    					name: "Czechia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-126,
    						551,
    						552,
    						553
    					]
    				],
    				id: "232",
    				properties: {
    					name: "Eritrea"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							554
    						]
    					],
    					[
    						[
    							555
    						]
    					],
    					[
    						[
    							556
    						]
    					]
    				],
    				id: "392",
    				properties: {
    					name: "Japan"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-193,
    						-97,
    						-202
    					]
    				],
    				id: "600",
    				properties: {
    					name: "Paraguay"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-364,
    						557,
    						558
    					]
    				],
    				id: "887",
    				properties: {
    					name: "Yemen"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-346,
    						-362,
    						-356,
    						559,
    						-354,
    						560,
    						-352,
    						-365,
    						-559,
    						561
    					]
    				],
    				id: "682",
    				properties: {
    					name: "Saudi Arabia"
    				}
    			},
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							562
    						]
    					],
    					[
    						[
    							563
    						]
    					],
    					[
    						[
    							564
    						]
    					],
    					[
    						[
    							565
    						]
    					],
    					[
    						[
    							566
    						]
    					],
    					[
    						[
    							567
    						]
    					],
    					[
    						[
    							568
    						]
    					],
    					[
    						[
    							569
    						]
    					]
    				],
    				id: "010",
    				properties: {
    					name: "Antarctica"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						570,
    						571
    					]
    				],
    				properties: {
    					name: "N. Cyprus"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-572,
    						572
    					]
    				],
    				id: "196",
    				properties: {
    					name: "Cyprus"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-341,
    						-15,
    						573
    					]
    				],
    				id: "504",
    				properties: {
    					name: "Morocco"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-124,
    						574,
    						575,
    						-329,
    						576
    					]
    				],
    				id: "818",
    				properties: {
    					name: "Egypt"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-123,
    						-132,
    						-283,
    						-343,
    						-340,
    						577,
    						-575
    					]
    				],
    				id: "434",
    				properties: {
    					name: "Libya"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-114,
    						-119,
    						578,
    						-127,
    						-554,
    						579,
    						580
    					]
    				],
    				id: "231",
    				properties: {
    					name: "Ethiopia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-553,
    						581,
    						582,
    						-580
    					]
    				],
    				id: "262",
    				properties: {
    					name: "Djibouti"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-115,
    						-581,
    						-583,
    						583
    					]
    				],
    				properties: {
    					name: "Somaliland"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-11,
    						584,
    						-110,
    						585,
    						-117
    					]
    				],
    				id: "800",
    				properties: {
    					name: "Uganda"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-10,
    						-325,
    						-111,
    						-585
    					]
    				],
    				id: "646",
    				properties: {
    					name: "Rwanda"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-496,
    						586,
    						587
    					]
    				],
    				id: "070",
    				properties: {
    					name: "Bosnia and Herz."
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-481,
    						-487,
    						-494,
    						588,
    						589
    					]
    				],
    				id: "807",
    				properties: {
    					name: "Macedonia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-457,
    						-464,
    						-482,
    						-590,
    						590,
    						591,
    						-587,
    						-495
    					]
    				],
    				id: "688",
    				properties: {
    					name: "Serbia"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-492,
    						592,
    						-497,
    						-588,
    						-592,
    						593
    					]
    				],
    				id: "499",
    				properties: {
    					name: "Montenegro"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-493,
    						-594,
    						-591,
    						-589
    					]
    				],
    				properties: {
    					name: "Kosovo"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						594
    					]
    				],
    				id: "780",
    				properties: {
    					name: "Trinidad and Tobago"
    				}
    			},
    			{
    				type: "Polygon",
    				arcs: [
    					[
    						-109,
    						-310,
    						-128,
    						-579,
    						-118,
    						-586
    					]
    				],
    				id: "728",
    				properties: {
    					name: "S. Sudan"
    				}
    			}
    		]
    	},
    	land: {
    		type: "GeometryCollection",
    		geometries: [
    			{
    				type: "MultiPolygon",
    				arcs: [
    					[
    						[
    							0
    						]
    					],
    					[
    						[
    							1
    						]
    					],
    					[
    						[
    							3,
    							320,
    							184,
    							255,
    							323,
    							104,
    							322,
    							311,
    							313,
    							315,
    							289,
    							284,
    							273,
    							290,
    							293,
    							297,
    							305,
    							307,
    							302,
    							304,
    							263,
    							336,
    							258,
    							272,
    							13,
    							573,
    							341,
    							338,
    							577,
    							575,
    							329,
    							332,
    							423,
    							487,
    							535,
    							153,
    							435,
    							164,
    							436,
    							461,
    							477,
    							489,
    							484,
    							490,
    							592,
    							497,
    							548,
    							525,
    							240,
    							507,
    							505,
    							506,
    							242,
    							501,
    							503,
    							474,
    							528,
    							476,
    							446,
    							142,
    							464,
    							466,
    							468,
    							158,
    							549,
    							430,
    							172,
    							161,
    							387,
    							391,
    							389,
    							522,
    							385,
    							371,
    							372,
    							543,
    							374,
    							380,
    							401,
    							394,
    							404,
    							422,
    							360,
    							354,
    							559,
    							352,
    							560,
    							347,
    							365,
    							349,
    							362,
    							557,
    							561,
    							346,
    							576,
    							124,
    							551,
    							581,
    							583,
    							115,
    							119
    						],
    						[
    							421,
    							416,
    							64,
    							150,
    							533
    						]
    					],
    					[
    						[
    							17,
    							48,
    							186,
    							229,
    							227,
    							223,
    							219,
    							216,
    							213,
    							209,
    							230,
    							232,
    							234,
    							236,
    							200,
    							191,
    							93,
    							100,
    							203,
    							246,
    							207,
    							211,
    							214,
    							217,
    							220,
    							224,
    							228,
    							189,
    							50,
    							15,
    							58
    						]
    					],
    					[
    						[
    							19
    						]
    					],
    					[
    						[
    							20
    						]
    					],
    					[
    						[
    							21
    						]
    					],
    					[
    						[
    							22
    						]
    					],
    					[
    						[
    							23
    						]
    					],
    					[
    						[
    							24
    						]
    					],
    					[
    						[
    							25
    						]
    					],
    					[
    						[
    							26
    						]
    					],
    					[
    						[
    							27
    						]
    					],
    					[
    						[
    							28
    						]
    					],
    					[
    						[
    							29
    						]
    					],
    					[
    						[
    							30
    						]
    					],
    					[
    						[
    							31
    						]
    					],
    					[
    						[
    							32
    						]
    					],
    					[
    						[
    							33
    						]
    					],
    					[
    						[
    							34
    						]
    					],
    					[
    						[
    							35
    						]
    					],
    					[
    						[
    							36
    						]
    					],
    					[
    						[
    							37
    						]
    					],
    					[
    						[
    							38
    						]
    					],
    					[
    						[
    							39
    						]
    					],
    					[
    						[
    							40
    						]
    					],
    					[
    						[
    							41
    						]
    					],
    					[
    						[
    							42
    						]
    					],
    					[
    						[
    							43
    						]
    					],
    					[
    						[
    							44
    						]
    					],
    					[
    						[
    							45
    						]
    					],
    					[
    						[
    							46
    						]
    					],
    					[
    						[
    							47
    						]
    					],
    					[
    						[
    							51
    						]
    					],
    					[
    						[
    							52
    						]
    					],
    					[
    						[
    							53
    						]
    					],
    					[
    						[
    							54
    						]
    					],
    					[
    						[
    							55
    						]
    					],
    					[
    						[
    							56
    						]
    					],
    					[
    						[
    							57
    						]
    					],
    					[
    						[
    							59
    						]
    					],
    					[
    						[
    							70,
    							75
    						]
    					],
    					[
    						[
    							72
    						]
    					],
    					[
    						[
    							73
    						]
    					],
    					[
    						[
    							74
    						]
    					],
    					[
    						[
    							77,
    							177
    						]
    					],
    					[
    						[
    							78
    						]
    					],
    					[
    						[
    							546,
    							79,
    							544,
    							547
    						]
    					],
    					[
    						[
    							81
    						]
    					],
    					[
    						[
    							82
    						]
    					],
    					[
    						[
    							83
    						]
    					],
    					[
    						[
    							84
    						]
    					],
    					[
    						[
    							85
    						]
    					],
    					[
    						[
    							86
    						]
    					],
    					[
    						[
    							87
    						]
    					],
    					[
    						[
    							88
    						]
    					],
    					[
    						[
    							89
    						]
    					],
    					[
    						[
    							90,
    							98
    						]
    					],
    					[
    						[
    							133,
    							134
    						]
    					],
    					[
    						[
    							135
    						]
    					],
    					[
    						[
    							136
    						]
    					],
    					[
    						[
    							137
    						]
    					],
    					[
    						[
    							138
    						]
    					],
    					[
    						[
    							139
    						]
    					],
    					[
    						[
    							140
    						]
    					],
    					[
    						[
    							144
    						]
    					],
    					[
    						[
    							145
    						]
    					],
    					[
    						[
    							162
    						]
    					],
    					[
    						[
    							165
    						]
    					],
    					[
    						[
    							166
    						]
    					],
    					[
    						[
    							167
    						]
    					],
    					[
    						[
    							168
    						]
    					],
    					[
    						[
    							169
    						]
    					],
    					[
    						[
    							173
    						]
    					],
    					[
    						[
    							174
    						]
    					],
    					[
    						[
    							175
    						]
    					],
    					[
    						[
    							176
    						]
    					],
    					[
    						[
    							245
    						]
    					],
    					[
    						[
    							247
    						]
    					],
    					[
    						[
    							248
    						]
    					],
    					[
    						[
    							249
    						]
    					],
    					[
    						[
    							334
    						]
    					],
    					[
    						[
    							366
    						]
    					],
    					[
    						[
    							367
    						]
    					],
    					[
    						[
    							482
    						]
    					],
    					[
    						[
    							508,
    							530
    						]
    					],
    					[
    						[
    							510
    						]
    					],
    					[
    						[
    							511
    						]
    					],
    					[
    						[
    							512
    						]
    					],
    					[
    						[
    							513
    						]
    					],
    					[
    						[
    							514
    						]
    					],
    					[
    						[
    							515
    						]
    					],
    					[
    						[
    							516
    						]
    					],
    					[
    						[
    							517
    						]
    					],
    					[
    						[
    							518
    						]
    					],
    					[
    						[
    							519
    						]
    					],
    					[
    						[
    							520
    						]
    					],
    					[
    						[
    							521
    						]
    					],
    					[
    						[
    							523
    						]
    					],
    					[
    						[
    							526
    						]
    					],
    					[
    						[
    							527
    						]
    					],
    					[
    						[
    							529
    						]
    					],
    					[
    						[
    							531
    						]
    					],
    					[
    						[
    							532
    						]
    					],
    					[
    						[
    							536
    						]
    					],
    					[
    						[
    							537
    						]
    					],
    					[
    						[
    							538
    						]
    					],
    					[
    						[
    							539
    						]
    					],
    					[
    						[
    							540
    						]
    					],
    					[
    						[
    							541
    						]
    					],
    					[
    						[
    							542
    						]
    					],
    					[
    						[
    							554
    						]
    					],
    					[
    						[
    							555
    						]
    					],
    					[
    						[
    							556
    						]
    					],
    					[
    						[
    							562
    						]
    					],
    					[
    						[
    							563
    						]
    					],
    					[
    						[
    							564
    						]
    					],
    					[
    						[
    							565
    						]
    					],
    					[
    						[
    							566
    						]
    					],
    					[
    						[
    							567
    						]
    					],
    					[
    						[
    							568
    						]
    					],
    					[
    						[
    							569
    						]
    					],
    					[
    						[
    							570,
    							572
    						]
    					],
    					[
    						[
    							594
    						]
    					]
    				]
    			}
    		]
    	}
    };
    var arcs = [
    	[
    		[
    			99478,
    			40237
    		],
    		[
    			69,
    			98
    		],
    		[
    			96,
    			-171
    		],
    		[
    			-46,
    			-308
    		],
    		[
    			-172,
    			-81
    		],
    		[
    			-153,
    			73
    		],
    		[
    			-27,
    			260
    		],
    		[
    			107,
    			203
    		],
    		[
    			126,
    			-74
    		]
    	],
    	[
    		[
    			0,
    			41087
    		],
    		[
    			57,
    			27
    		],
    		[
    			-34,
    			-284
    		],
    		[
    			-23,
    			-32
    		],
    		[
    			99822,
    			-145
    		],
    		[
    			-177,
    			-124
    		],
    		[
    			-36,
    			220
    		],
    		[
    			139,
    			121
    		],
    		[
    			88,
    			33
    		],
    		[
    			163,
    			184
    		],
    		[
    			-99999,
    			0
    		]
    	],
    	[
    		[
    			59417,
    			50018
    		],
    		[
    			47,
    			-65
    		],
    		[
    			1007,
    			-1203
    		],
    		[
    			19,
    			-343
    		],
    		[
    			399,
    			-590
    		]
    	],
    	[
    		[
    			60889,
    			47817
    		],
    		[
    			-128,
    			-728
    		],
    		[
    			16,
    			-335
    		],
    		[
    			178,
    			-216
    		],
    		[
    			8,
    			-153
    		],
    		[
    			-76,
    			-357
    		],
    		[
    			16,
    			-180
    		],
    		[
    			-18,
    			-282
    		],
    		[
    			97,
    			-370
    		],
    		[
    			115,
    			-583
    		],
    		[
    			101,
    			-129
    		]
    	],
    	[
    		[
    			61198,
    			44484
    		],
    		[
    			-221,
    			-342
    		],
    		[
    			-303,
    			-230
    		],
    		[
    			-167,
    			10
    		],
    		[
    			-99,
    			-177
    		],
    		[
    			-193,
    			-16
    		],
    		[
    			-73,
    			-74
    		],
    		[
    			-334,
    			166
    		],
    		[
    			-209,
    			-48
    		]
    	],
    	[
    		[
    			59599,
    			43773
    		],
    		[
    			-77,
    			804
    		],
    		[
    			-95,
    			275
    		],
    		[
    			-55,
    			164
    		],
    		[
    			-273,
    			110
    		]
    	],
    	[
    		[
    			59099,
    			45126
    		],
    		[
    			-157,
    			177
    		],
    		[
    			-177,
    			100
    		],
    		[
    			-111,
    			99
    		],
    		[
    			-116,
    			150
    		]
    	],
    	[
    		[
    			58538,
    			45652
    		],
    		[
    			-150,
    			745
    		],
    		[
    			-161,
    			330
    		],
    		[
    			-55,
    			343
    		],
    		[
    			27,
    			307
    		],
    		[
    			-50,
    			544
    		]
    	],
    	[
    		[
    			58149,
    			47921
    		],
    		[
    			115,
    			28
    		],
    		[
    			101,
    			214
    		],
    		[
    			108,
    			308
    		],
    		[
    			69,
    			124
    		],
    		[
    			-3,
    			192
    		],
    		[
    			-60,
    			134
    		],
    		[
    			-16,
    			233
    		]
    	],
    	[
    		[
    			58463,
    			49154
    		],
    		[
    			80,
    			74
    		],
    		[
    			16,
    			348
    		],
    		[
    			-110,
    			333
    		]
    	],
    	[
    		[
    			58449,
    			49909
    		],
    		[
    			98,
    			71
    		],
    		[
    			304,
    			-7
    		],
    		[
    			566,
    			45
    		]
    	],
    	[
    		[
    			47592,
    			66920
    		],
    		[
    			1,
    			-40
    		],
    		[
    			-6,
    			-114
    		]
    	],
    	[
    		[
    			47587,
    			66766
    		],
    		[
    			-1,
    			-895
    		],
    		[
    			-911,
    			31
    		],
    		[
    			9,
    			-1512
    		],
    		[
    			-261,
    			-53
    		],
    		[
    			-68,
    			-304
    		],
    		[
    			53,
    			-853
    		],
    		[
    			-1088,
    			4
    		],
    		[
    			-60,
    			-197
    		]
    	],
    	[
    		[
    			45260,
    			62987
    		],
    		[
    			12,
    			249
    		]
    	],
    	[
    		[
    			45272,
    			63236
    		],
    		[
    			5,
    			-1
    		],
    		[
    			625,
    			48
    		],
    		[
    			33,
    			213
    		],
    		[
    			114,
    			265
    		],
    		[
    			92,
    			816
    		],
    		[
    			386,
    			637
    		],
    		[
    			131,
    			745
    		],
    		[
    			86,
    			44
    		],
    		[
    			91,
    			460
    		],
    		[
    			234,
    			63
    		],
    		[
    			100,
    			-76
    		],
    		[
    			126,
    			0
    		],
    		[
    			90,
    			134
    		],
    		[
    			172,
    			19
    		],
    		[
    			-7,
    			317
    		],
    		[
    			42,
    			0
    		]
    	],
    	[
    		[
    			15878,
    			79530
    		],
    		[
    			-38,
    			1
    		],
    		[
    			-537,
    			581
    		],
    		[
    			-199,
    			255
    		],
    		[
    			-503,
    			244
    		],
    		[
    			-155,
    			523
    		],
    		[
    			40,
    			363
    		],
    		[
    			-356,
    			252
    		],
    		[
    			-48,
    			476
    		],
    		[
    			-336,
    			429
    		],
    		[
    			-6,
    			304
    		]
    	],
    	[
    		[
    			13740,
    			82958
    		],
    		[
    			154,
    			285
    		],
    		[
    			-7,
    			373
    		],
    		[
    			-473,
    			376
    		],
    		[
    			-284,
    			674
    		],
    		[
    			-173,
    			424
    		],
    		[
    			-255,
    			266
    		],
    		[
    			-187,
    			242
    		],
    		[
    			-147,
    			306
    		],
    		[
    			-279,
    			-192
    		],
    		[
    			-270,
    			-330
    		],
    		[
    			-247,
    			388
    		],
    		[
    			-194,
    			259
    		],
    		[
    			-271,
    			164
    		],
    		[
    			-273,
    			17
    		],
    		[
    			1,
    			3364
    		],
    		[
    			2,
    			2193
    		]
    	],
    	[
    		[
    			10837,
    			91767
    		],
    		[
    			518,
    			-142
    		],
    		[
    			438,
    			-285
    		],
    		[
    			289,
    			-54
    		],
    		[
    			244,
    			247
    		],
    		[
    			336,
    			184
    		],
    		[
    			413,
    			-72
    		],
    		[
    			416,
    			259
    		],
    		[
    			455,
    			148
    		],
    		[
    			191,
    			-245
    		],
    		[
    			207,
    			138
    		],
    		[
    			62,
    			278
    		],
    		[
    			192,
    			-63
    		],
    		[
    			470,
    			-530
    		],
    		[
    			369,
    			401
    		],
    		[
    			38,
    			-449
    		],
    		[
    			341,
    			97
    		],
    		[
    			105,
    			173
    		],
    		[
    			337,
    			-34
    		],
    		[
    			424,
    			-248
    		],
    		[
    			650,
    			-217
    		],
    		[
    			383,
    			-100
    		],
    		[
    			272,
    			38
    		],
    		[
    			374,
    			-300
    		],
    		[
    			-390,
    			-293
    		],
    		[
    			502,
    			-127
    		],
    		[
    			750,
    			70
    		],
    		[
    			236,
    			103
    		],
    		[
    			296,
    			-354
    		],
    		[
    			302,
    			299
    		],
    		[
    			-283,
    			251
    		],
    		[
    			179,
    			202
    		],
    		[
    			338,
    			27
    		],
    		[
    			223,
    			59
    		],
    		[
    			224,
    			-141
    		],
    		[
    			279,
    			-321
    		],
    		[
    			310,
    			47
    		],
    		[
    			491,
    			-266
    		],
    		[
    			431,
    			94
    		],
    		[
    			405,
    			-14
    		],
    		[
    			-32,
    			367
    		],
    		[
    			247,
    			103
    		],
    		[
    			431,
    			-200
    		],
    		[
    			-2,
    			-559
    		],
    		[
    			177,
    			471
    		],
    		[
    			223,
    			-16
    		],
    		[
    			126,
    			594
    		],
    		[
    			-298,
    			364
    		],
    		[
    			-324,
    			239
    		],
    		[
    			22,
    			653
    		],
    		[
    			329,
    			429
    		],
    		[
    			366,
    			-95
    		],
    		[
    			281,
    			-261
    		],
    		[
    			378,
    			-666
    		],
    		[
    			-247,
    			-290
    		],
    		[
    			517,
    			-120
    		],
    		[
    			-1,
    			-604
    		],
    		[
    			371,
    			463
    		],
    		[
    			332,
    			-380
    		],
    		[
    			-83,
    			-438
    		],
    		[
    			269,
    			-399
    		],
    		[
    			290,
    			427
    		],
    		[
    			202,
    			510
    		],
    		[
    			16,
    			649
    		],
    		[
    			394,
    			-46
    		],
    		[
    			411,
    			-87
    		],
    		[
    			373,
    			-293
    		],
    		[
    			17,
    			-293
    		],
    		[
    			-207,
    			-315
    		],
    		[
    			196,
    			-316
    		],
    		[
    			-36,
    			-288
    		],
    		[
    			-544,
    			-413
    		],
    		[
    			-386,
    			-91
    		],
    		[
    			-287,
    			178
    		],
    		[
    			-83,
    			-297
    		],
    		[
    			-268,
    			-498
    		],
    		[
    			-81,
    			-259
    		],
    		[
    			-322,
    			-399
    		],
    		[
    			-397,
    			-39
    		],
    		[
    			-220,
    			-250
    		],
    		[
    			-18,
    			-384
    		],
    		[
    			-323,
    			-74
    		],
    		[
    			-340,
    			-479
    		],
    		[
    			-301,
    			-665
    		],
    		[
    			-108,
    			-466
    		],
    		[
    			-16,
    			-686
    		],
    		[
    			409,
    			-99
    		],
    		[
    			125,
    			-553
    		],
    		[
    			130,
    			-448
    		],
    		[
    			388,
    			117
    		],
    		[
    			517,
    			-256
    		],
    		[
    			277,
    			-225
    		],
    		[
    			199,
    			-279
    		],
    		[
    			348,
    			-163
    		],
    		[
    			294,
    			-248
    		],
    		[
    			459,
    			-34
    		],
    		[
    			302,
    			-58
    		],
    		[
    			-45,
    			-511
    		],
    		[
    			86,
    			-594
    		],
    		[
    			201,
    			-661
    		],
    		[
    			414,
    			-561
    		],
    		[
    			214,
    			192
    		],
    		[
    			150,
    			607
    		],
    		[
    			-145,
    			934
    		],
    		[
    			-196,
    			311
    		],
    		[
    			445,
    			276
    		],
    		[
    			314,
    			415
    		],
    		[
    			154,
    			411
    		],
    		[
    			-23,
    			395
    		],
    		[
    			-188,
    			502
    		],
    		[
    			-338,
    			445
    		],
    		[
    			328,
    			619
    		],
    		[
    			-121,
    			535
    		],
    		[
    			-93,
    			922
    		],
    		[
    			194,
    			137
    		],
    		[
    			476,
    			-161
    		],
    		[
    			286,
    			-57
    		],
    		[
    			230,
    			155
    		],
    		[
    			258,
    			-200
    		],
    		[
    			342,
    			-343
    		],
    		[
    			85,
    			-229
    		],
    		[
    			495,
    			-45
    		],
    		[
    			-8,
    			-496
    		],
    		[
    			92,
    			-747
    		],
    		[
    			254,
    			-92
    		],
    		[
    			201,
    			-348
    		],
    		[
    			402,
    			328
    		],
    		[
    			266,
    			652
    		],
    		[
    			184,
    			274
    		],
    		[
    			216,
    			-527
    		],
    		[
    			362,
    			-754
    		],
    		[
    			307,
    			-709
    		],
    		[
    			-112,
    			-371
    		],
    		[
    			370,
    			-333
    		],
    		[
    			250,
    			-338
    		],
    		[
    			442,
    			-152
    		],
    		[
    			179,
    			-189
    		],
    		[
    			110,
    			-500
    		],
    		[
    			216,
    			-78
    		],
    		[
    			112,
    			-223
    		],
    		[
    			20,
    			-664
    		],
    		[
    			-202,
    			-222
    		],
    		[
    			-199,
    			-207
    		],
    		[
    			-458,
    			-210
    		],
    		[
    			-349,
    			-486
    		],
    		[
    			-470,
    			-96
    		],
    		[
    			-594,
    			125
    		],
    		[
    			-417,
    			4
    		],
    		[
    			-287,
    			-41
    		],
    		[
    			-233,
    			-424
    		],
    		[
    			-354,
    			-262
    		],
    		[
    			-401,
    			-782
    		],
    		[
    			-320,
    			-545
    		],
    		[
    			236,
    			97
    		],
    		[
    			446,
    			776
    		],
    		[
    			583,
    			493
    		],
    		[
    			415,
    			58
    		],
    		[
    			246,
    			-289
    		],
    		[
    			-262,
    			-397
    		],
    		[
    			88,
    			-637
    		],
    		[
    			91,
    			-446
    		],
    		[
    			361,
    			-295
    		],
    		[
    			459,
    			86
    		],
    		[
    			278,
    			664
    		],
    		[
    			19,
    			-429
    		],
    		[
    			180,
    			-214
    		],
    		[
    			-344,
    			-387
    		],
    		[
    			-615,
    			-351
    		],
    		[
    			-276,
    			-239
    		],
    		[
    			-310,
    			-426
    		],
    		[
    			-211,
    			44
    		],
    		[
    			-11,
    			500
    		],
    		[
    			483,
    			488
    		],
    		[
    			-445,
    			-19
    		],
    		[
    			-309,
    			-72
    		]
    	],
    	[
    		[
    			31350,
    			77248
    		],
    		[
    			-181,
    			334
    		],
    		[
    			0,
    			805
    		],
    		[
    			-123,
    			171
    		],
    		[
    			-187,
    			-100
    		],
    		[
    			-92,
    			155
    		],
    		[
    			-212,
    			-446
    		],
    		[
    			-84,
    			-460
    		],
    		[
    			-99,
    			-269
    		],
    		[
    			-118,
    			-91
    		],
    		[
    			-89,
    			-30
    		],
    		[
    			-28,
    			-146
    		],
    		[
    			-512,
    			0
    		],
    		[
    			-422,
    			-4
    		],
    		[
    			-125,
    			-109
    		],
    		[
    			-294,
    			-425
    		],
    		[
    			-34,
    			-46
    		],
    		[
    			-89,
    			-231
    		],
    		[
    			-255,
    			1
    		],
    		[
    			-273,
    			-3
    		],
    		[
    			-125,
    			-93
    		],
    		[
    			44,
    			-116
    		],
    		[
    			25,
    			-181
    		],
    		[
    			-5,
    			-60
    		],
    		[
    			-363,
    			-293
    		],
    		[
    			-286,
    			-93
    		],
    		[
    			-323,
    			-316
    		],
    		[
    			-70,
    			0
    		],
    		[
    			-94,
    			93
    		],
    		[
    			-31,
    			85
    		],
    		[
    			6,
    			61
    		],
    		[
    			61,
    			207
    		],
    		[
    			131,
    			325
    		],
    		[
    			81,
    			349
    		],
    		[
    			-56,
    			514
    		],
    		[
    			-59,
    			536
    		],
    		[
    			-290,
    			277
    		],
    		[
    			35,
    			105
    		],
    		[
    			-41,
    			73
    		],
    		[
    			-76,
    			0
    		],
    		[
    			-56,
    			93
    		],
    		[
    			-14,
    			140
    		],
    		[
    			-54,
    			-61
    		],
    		[
    			-75,
    			18
    		],
    		[
    			17,
    			59
    		],
    		[
    			-65,
    			58
    		],
    		[
    			-27,
    			155
    		],
    		[
    			-216,
    			189
    		],
    		[
    			-224,
    			197
    		],
    		[
    			-272,
    			229
    		],
    		[
    			-261,
    			214
    		],
    		[
    			-248,
    			-167
    		],
    		[
    			-91,
    			-6
    		],
    		[
    			-342,
    			154
    		],
    		[
    			-225,
    			-77
    		],
    		[
    			-269,
    			183
    		],
    		[
    			-284,
    			94
    		],
    		[
    			-194,
    			36
    		],
    		[
    			-86,
    			100
    		],
    		[
    			-49,
    			325
    		],
    		[
    			-94,
    			-3
    		],
    		[
    			-1,
    			-227
    		],
    		[
    			-575,
    			0
    		],
    		[
    			-951,
    			0
    		],
    		[
    			-944,
    			0
    		],
    		[
    			-833,
    			0
    		],
    		[
    			-834,
    			0
    		],
    		[
    			-819,
    			0
    		],
    		[
    			-847,
    			0
    		],
    		[
    			-273,
    			0
    		],
    		[
    			-824,
    			0
    		],
    		[
    			-789,
    			0
    		]
    	],
    	[
    		[
    			26668,
    			87478
    		],
    		[
    			207,
    			273
    		],
    		[
    			381,
    			-6
    		],
    		[
    			-6,
    			-114
    		],
    		[
    			-325,
    			-326
    		],
    		[
    			-196,
    			13
    		],
    		[
    			-61,
    			160
    		]
    	],
    	[
    		[
    			27840,
    			93593
    		],
    		[
    			-306,
    			313
    		],
    		[
    			12,
    			213
    		],
    		[
    			133,
    			39
    		],
    		[
    			636,
    			-63
    		],
    		[
    			479,
    			-325
    		],
    		[
    			25,
    			-163
    		],
    		[
    			-296,
    			17
    		],
    		[
    			-299,
    			13
    		],
    		[
    			-304,
    			-80
    		],
    		[
    			-80,
    			36
    		]
    	],
    	[
    		[
    			27690,
    			87261
    		],
    		[
    			107,
    			177
    		],
    		[
    			114,
    			-13
    		],
    		[
    			70,
    			-121
    		],
    		[
    			-108,
    			-310
    		],
    		[
    			-123,
    			50
    		],
    		[
    			-73,
    			176
    		],
    		[
    			13,
    			41
    		]
    	],
    	[
    		[
    			23996,
    			94879
    		],
    		[
    			-151,
    			-229
    		],
    		[
    			-403,
    			44
    		],
    		[
    			-337,
    			155
    		],
    		[
    			148,
    			266
    		],
    		[
    			399,
    			159
    		],
    		[
    			243,
    			-208
    		],
    		[
    			101,
    			-187
    		]
    	],
    	[
    		[
    			23933,
    			96380
    		],
    		[
    			-126,
    			-17
    		],
    		[
    			-521,
    			38
    		],
    		[
    			-74,
    			165
    		],
    		[
    			559,
    			-9
    		],
    		[
    			195,
    			-109
    		],
    		[
    			-33,
    			-68
    		]
    	],
    	[
    		[
    			23124,
    			97116
    		],
    		[
    			332,
    			-205
    		],
    		[
    			-76,
    			-214
    		],
    		[
    			-411,
    			-122
    		],
    		[
    			-226,
    			138
    		],
    		[
    			-119,
    			221
    		],
    		[
    			-22,
    			245
    		],
    		[
    			360,
    			-24
    		],
    		[
    			162,
    			-39
    		]
    	],
    	[
    		[
    			25514,
    			94532
    		],
    		[
    			-449,
    			73
    		],
    		[
    			-738,
    			190
    		],
    		[
    			-96,
    			325
    		],
    		[
    			-34,
    			293
    		],
    		[
    			-279,
    			258
    		],
    		[
    			-574,
    			72
    		],
    		[
    			-322,
    			183
    		],
    		[
    			104,
    			242
    		],
    		[
    			573,
    			-37
    		],
    		[
    			308,
    			-190
    		],
    		[
    			547,
    			1
    		],
    		[
    			240,
    			-194
    		],
    		[
    			-64,
    			-222
    		],
    		[
    			319,
    			-134
    		],
    		[
    			177,
    			-140
    		],
    		[
    			374,
    			-26
    		],
    		[
    			406,
    			-50
    		],
    		[
    			441,
    			128
    		],
    		[
    			566,
    			51
    		],
    		[
    			451,
    			-42
    		],
    		[
    			298,
    			-223
    		],
    		[
    			62,
    			-244
    		],
    		[
    			-174,
    			-157
    		],
    		[
    			-414,
    			-127
    		],
    		[
    			-355,
    			72
    		],
    		[
    			-797,
    			-91
    		],
    		[
    			-570,
    			-11
    		]
    	],
    	[
    		[
    			19093,
    			96754
    		],
    		[
    			392,
    			-92
    		],
    		[
    			-93,
    			-177
    		],
    		[
    			-518,
    			-170
    		],
    		[
    			-411,
    			191
    		],
    		[
    			224,
    			188
    		],
    		[
    			406,
    			60
    		]
    	],
    	[
    		[
    			19177,
    			97139
    		],
    		[
    			361,
    			-120
    		],
    		[
    			-339,
    			-115
    		],
    		[
    			-461,
    			1
    		],
    		[
    			5,
    			84
    		],
    		[
    			285,
    			177
    		],
    		[
    			149,
    			-27
    		]
    	],
    	[
    		[
    			34555,
    			80899
    		],
    		[
    			-148,
    			-372
    		],
    		[
    			-184,
    			-517
    		],
    		[
    			181,
    			199
    		],
    		[
    			187,
    			-126
    		],
    		[
    			-98,
    			-206
    		],
    		[
    			247,
    			-162
    		],
    		[
    			128,
    			144
    		],
    		[
    			277,
    			-182
    		],
    		[
    			-86,
    			-433
    		],
    		[
    			194,
    			101
    		],
    		[
    			36,
    			-313
    		],
    		[
    			86,
    			-367
    		],
    		[
    			-117,
    			-520
    		],
    		[
    			-125,
    			-22
    		],
    		[
    			-183,
    			111
    		],
    		[
    			60,
    			484
    		],
    		[
    			-77,
    			75
    		],
    		[
    			-322,
    			-513
    		],
    		[
    			-166,
    			21
    		],
    		[
    			196,
    			277
    		],
    		[
    			-267,
    			144
    		],
    		[
    			-298,
    			-35
    		],
    		[
    			-539,
    			18
    		],
    		[
    			-43,
    			175
    		],
    		[
    			173,
    			208
    		],
    		[
    			-121,
    			160
    		],
    		[
    			234,
    			356
    		],
    		[
    			287,
    			941
    		],
    		[
    			172,
    			336
    		],
    		[
    			241,
    			204
    		],
    		[
    			129,
    			-26
    		],
    		[
    			-54,
    			-160
    		]
    	],
    	[
    		[
    			26699,
    			89048
    		],
    		[
    			304,
    			-203
    		],
    		[
    			318,
    			-184
    		],
    		[
    			25,
    			-281
    		],
    		[
    			204,
    			46
    		],
    		[
    			199,
    			-196
    		],
    		[
    			-247,
    			-186
    		],
    		[
    			-432,
    			142
    		],
    		[
    			-156,
    			266
    		],
    		[
    			-275,
    			-314
    		],
    		[
    			-396,
    			-306
    		],
    		[
    			-95,
    			346
    		],
    		[
    			-377,
    			-57
    		],
    		[
    			242,
    			292
    		],
    		[
    			35,
    			465
    		],
    		[
    			95,
    			542
    		],
    		[
    			201,
    			-49
    		],
    		[
    			51,
    			-259
    		],
    		[
    			143,
    			91
    		],
    		[
    			161,
    			-155
    		]
    	],
    	[
    		[
    			28119,
    			93327
    		],
    		[
    			263,
    			235
    		],
    		[
    			616,
    			-299
    		],
    		[
    			383,
    			-282
    		],
    		[
    			36,
    			-258
    		],
    		[
    			515,
    			134
    		],
    		[
    			290,
    			-376
    		],
    		[
    			670,
    			-234
    		],
    		[
    			242,
    			-238
    		],
    		[
    			263,
    			-553
    		],
    		[
    			-510,
    			-275
    		],
    		[
    			654,
    			-386
    		],
    		[
    			441,
    			-130
    		],
    		[
    			400,
    			-543
    		],
    		[
    			437,
    			-39
    		],
    		[
    			-87,
    			-414
    		],
    		[
    			-487,
    			-687
    		],
    		[
    			-342,
    			253
    		],
    		[
    			-437,
    			568
    		],
    		[
    			-359,
    			-74
    		],
    		[
    			-35,
    			-338
    		],
    		[
    			292,
    			-344
    		],
    		[
    			377,
    			-272
    		],
    		[
    			114,
    			-157
    		],
    		[
    			181,
    			-584
    		],
    		[
    			-96,
    			-425
    		],
    		[
    			-350,
    			160
    		],
    		[
    			-697,
    			473
    		],
    		[
    			393,
    			-509
    		],
    		[
    			289,
    			-357
    		],
    		[
    			45,
    			-206
    		],
    		[
    			-753,
    			236
    		],
    		[
    			-596,
    			343
    		],
    		[
    			-337,
    			287
    		],
    		[
    			97,
    			167
    		],
    		[
    			-414,
    			304
    		],
    		[
    			-405,
    			286
    		],
    		[
    			5,
    			-171
    		],
    		[
    			-803,
    			-94
    		],
    		[
    			-235,
    			203
    		],
    		[
    			183,
    			435
    		],
    		[
    			522,
    			10
    		],
    		[
    			571,
    			76
    		],
    		[
    			-92,
    			211
    		],
    		[
    			96,
    			294
    		],
    		[
    			360,
    			576
    		],
    		[
    			-77,
    			261
    		],
    		[
    			-107,
    			203
    		],
    		[
    			-425,
    			286
    		],
    		[
    			-563,
    			201
    		],
    		[
    			178,
    			150
    		],
    		[
    			-294,
    			367
    		],
    		[
    			-245,
    			34
    		],
    		[
    			-219,
    			201
    		],
    		[
    			-149,
    			-175
    		],
    		[
    			-503,
    			-76
    		],
    		[
    			-1011,
    			132
    		],
    		[
    			-588,
    			174
    		],
    		[
    			-450,
    			89
    		],
    		[
    			-231,
    			207
    		],
    		[
    			290,
    			270
    		],
    		[
    			-394,
    			2
    		],
    		[
    			-88,
    			599
    		],
    		[
    			213,
    			528
    		],
    		[
    			286,
    			241
    		],
    		[
    			717,
    			158
    		],
    		[
    			-204,
    			-382
    		],
    		[
    			219,
    			-369
    		],
    		[
    			256,
    			477
    		],
    		[
    			704,
    			242
    		],
    		[
    			477,
    			-611
    		],
    		[
    			-42,
    			-387
    		],
    		[
    			550,
    			172
    		]
    	],
    	[
    		[
    			23749,
    			94380
    		],
    		[
    			579,
    			-20
    		],
    		[
    			530,
    			-144
    		],
    		[
    			-415,
    			-526
    		],
    		[
    			-331,
    			-115
    		],
    		[
    			-298,
    			-442
    		],
    		[
    			-317,
    			22
    		],
    		[
    			-173,
    			519
    		],
    		[
    			4,
    			294
    		],
    		[
    			145,
    			251
    		],
    		[
    			276,
    			161
    		]
    	],
    	[
    		[
    			15873,
    			95551
    		],
    		[
    			472,
    			442
    		],
    		[
    			570,
    			383
    		],
    		[
    			426,
    			-9
    		],
    		[
    			381,
    			87
    		],
    		[
    			-38,
    			-454
    		],
    		[
    			-214,
    			-205
    		],
    		[
    			-259,
    			-29
    		],
    		[
    			-517,
    			-252
    		],
    		[
    			-444,
    			-91
    		],
    		[
    			-377,
    			128
    		]
    	],
    	[
    		[
    			13136,
    			82508
    		],
    		[
    			267,
    			47
    		],
    		[
    			-84,
    			-671
    		],
    		[
    			242,
    			-475
    		],
    		[
    			-111,
    			1
    		],
    		[
    			-167,
    			270
    		],
    		[
    			-103,
    			272
    		],
    		[
    			-140,
    			184
    		],
    		[
    			-51,
    			260
    		],
    		[
    			16,
    			188
    		],
    		[
    			131,
    			-76
    		]
    	],
    	[
    		[
    			20696,
    			97433
    		],
    		[
    			546,
    			-81
    		],
    		[
    			751,
    			-215
    		],
    		[
    			212,
    			-281
    		],
    		[
    			108,
    			-247
    		],
    		[
    			-453,
    			66
    		],
    		[
    			-457,
    			192
    		],
    		[
    			-619,
    			21
    		],
    		[
    			268,
    			176
    		],
    		[
    			-335,
    			142
    		],
    		[
    			-21,
    			227
    		]
    	],
    	[
    		[
    			15692,
    			79240
    		],
    		[
    			-140,
    			-82
    		],
    		[
    			-456,
    			269
    		],
    		[
    			-84,
    			209
    		],
    		[
    			-248,
    			207
    		],
    		[
    			-50,
    			168
    		],
    		[
    			-286,
    			107
    		],
    		[
    			-107,
    			321
    		],
    		[
    			24,
    			137
    		],
    		[
    			291,
    			-129
    		],
    		[
    			171,
    			-89
    		],
    		[
    			261,
    			-63
    		],
    		[
    			94,
    			-204
    		],
    		[
    			138,
    			-280
    		],
    		[
    			277,
    			-244
    		],
    		[
    			115,
    			-327
    		]
    	],
    	[
    		[
    			16239,
    			94566
    		],
    		[
    			397,
    			-123
    		],
    		[
    			709,
    			-33
    		],
    		[
    			270,
    			-171
    		],
    		[
    			298,
    			-249
    		],
    		[
    			-349,
    			-149
    		],
    		[
    			-681,
    			-415
    		],
    		[
    			-344,
    			-414
    		],
    		[
    			0,
    			-257
    		],
    		[
    			-731,
    			-285
    		],
    		[
    			-147,
    			259
    		],
    		[
    			-641,
    			312
    		],
    		[
    			119,
    			250
    		],
    		[
    			192,
    			432
    		],
    		[
    			241,
    			388
    		],
    		[
    			-272,
    			362
    		],
    		[
    			939,
    			93
    		]
    	],
    	[
    		[
    			20050,
    			95391
    		],
    		[
    			247,
    			99
    		],
    		[
    			291,
    			-26
    		],
    		[
    			49,
    			-289
    		],
    		[
    			-169,
    			-281
    		],
    		[
    			-940,
    			-91
    		],
    		[
    			-701,
    			-256
    		],
    		[
    			-423,
    			-14
    		],
    		[
    			-35,
    			193
    		],
    		[
    			577,
    			261
    		],
    		[
    			-1255,
    			-70
    		],
    		[
    			-389,
    			106
    		],
    		[
    			379,
    			577
    		],
    		[
    			262,
    			165
    		],
    		[
    			782,
    			-199
    		],
    		[
    			493,
    			-350
    		],
    		[
    			485,
    			-45
    		],
    		[
    			-397,
    			565
    		],
    		[
    			255,
    			215
    		],
    		[
    			286,
    			-68
    		],
    		[
    			94,
    			-282
    		],
    		[
    			109,
    			-210
    		]
    	],
    	[
    		[
    			20410,
    			93755
    		],
    		[
    			311,
    			-239
    		],
    		[
    			175,
    			-575
    		],
    		[
    			86,
    			-417
    		],
    		[
    			466,
    			-293
    		],
    		[
    			502,
    			-279
    		],
    		[
    			-31,
    			-260
    		],
    		[
    			-456,
    			-48
    		],
    		[
    			178,
    			-227
    		],
    		[
    			-94,
    			-217
    		],
    		[
    			-503,
    			93
    		],
    		[
    			-478,
    			160
    		],
    		[
    			-322,
    			-36
    		],
    		[
    			-522,
    			-201
    		],
    		[
    			-704,
    			-88
    		],
    		[
    			-494,
    			-56
    		],
    		[
    			-151,
    			279
    		],
    		[
    			-379,
    			161
    		],
    		[
    			-246,
    			-66
    		],
    		[
    			-343,
    			468
    		],
    		[
    			185,
    			62
    		],
    		[
    			429,
    			101
    		],
    		[
    			392,
    			-26
    		],
    		[
    			362,
    			103
    		],
    		[
    			-537,
    			138
    		],
    		[
    			-594,
    			-47
    		],
    		[
    			-394,
    			12
    		],
    		[
    			-146,
    			217
    		],
    		[
    			644,
    			237
    		],
    		[
    			-428,
    			-9
    		],
    		[
    			-485,
    			156
    		],
    		[
    			233,
    			443
    		],
    		[
    			193,
    			235
    		],
    		[
    			744,
    			359
    		],
    		[
    			284,
    			-114
    		],
    		[
    			-139,
    			-277
    		],
    		[
    			618,
    			179
    		],
    		[
    			386,
    			-298
    		],
    		[
    			314,
    			302
    		],
    		[
    			254,
    			-194
    		],
    		[
    			227,
    			-580
    		],
    		[
    			140,
    			244
    		],
    		[
    			-197,
    			606
    		],
    		[
    			244,
    			86
    		],
    		[
    			276,
    			-94
    		]
    	],
    	[
    		[
    			22100,
    			93536
    		],
    		[
    			-306,
    			386
    		],
    		[
    			329,
    			286
    		],
    		[
    			331,
    			-124
    		],
    		[
    			496,
    			75
    		],
    		[
    			72,
    			-172
    		],
    		[
    			-259,
    			-283
    		],
    		[
    			420,
    			-254
    		],
    		[
    			-50,
    			-532
    		],
    		[
    			-455,
    			-229
    		],
    		[
    			-268,
    			50
    		],
    		[
    			-192,
    			225
    		],
    		[
    			-690,
    			456
    		],
    		[
    			5,
    			189
    		],
    		[
    			567,
    			-73
    		]
    	],
    	[
    		[
    			20389,
    			94064
    		],
    		[
    			372,
    			24
    		],
    		[
    			211,
    			-130
    		],
    		[
    			-244,
    			-390
    		],
    		[
    			-434,
    			413
    		],
    		[
    			95,
    			83
    		]
    	],
    	[
    		[
    			22639,
    			95907
    		],
    		[
    			212,
    			-273
    		],
    		[
    			9,
    			-303
    		],
    		[
    			-127,
    			-440
    		],
    		[
    			-458,
    			-60
    		],
    		[
    			-298,
    			94
    		],
    		[
    			5,
    			345
    		],
    		[
    			-455,
    			-46
    		],
    		[
    			-18,
    			457
    		],
    		[
    			299,
    			-18
    		],
    		[
    			419,
    			201
    		],
    		[
    			390,
    			-34
    		],
    		[
    			22,
    			77
    		]
    	],
    	[
    		[
    			23329,
    			98201
    		],
    		[
    			192,
    			180
    		],
    		[
    			285,
    			42
    		],
    		[
    			-122,
    			135
    		],
    		[
    			646,
    			30
    		],
    		[
    			355,
    			-315
    		],
    		[
    			468,
    			-127
    		],
    		[
    			455,
    			-112
    		],
    		[
    			220,
    			-390
    		],
    		[
    			334,
    			-190
    		],
    		[
    			-381,
    			-176
    		],
    		[
    			-513,
    			-445
    		],
    		[
    			-492,
    			-42
    		],
    		[
    			-575,
    			76
    		],
    		[
    			-299,
    			240
    		],
    		[
    			4,
    			215
    		],
    		[
    			220,
    			157
    		],
    		[
    			-508,
    			-4
    		],
    		[
    			-306,
    			196
    		],
    		[
    			-176,
    			268
    		],
    		[
    			193,
    			262
    		]
    	],
    	[
    		[
    			24559,
    			98965
    		],
    		[
    			413,
    			112
    		],
    		[
    			324,
    			19
    		],
    		[
    			545,
    			96
    		],
    		[
    			409,
    			220
    		],
    		[
    			344,
    			-30
    		],
    		[
    			300,
    			-166
    		],
    		[
    			211,
    			319
    		],
    		[
    			367,
    			95
    		],
    		[
    			498,
    			65
    		],
    		[
    			849,
    			24
    		],
    		[
    			148,
    			-63
    		],
    		[
    			802,
    			100
    		],
    		[
    			601,
    			-38
    		],
    		[
    			602,
    			-37
    		],
    		[
    			742,
    			-47
    		],
    		[
    			597,
    			-75
    		],
    		[
    			508,
    			-161
    		],
    		[
    			-12,
    			-157
    		],
    		[
    			-678,
    			-257
    		],
    		[
    			-672,
    			-119
    		],
    		[
    			-251,
    			-133
    		],
    		[
    			605,
    			3
    		],
    		[
    			-656,
    			-358
    		],
    		[
    			-452,
    			-167
    		],
    		[
    			-476,
    			-483
    		],
    		[
    			-573,
    			-98
    		],
    		[
    			-177,
    			-120
    		],
    		[
    			-841,
    			-64
    		],
    		[
    			383,
    			-74
    		],
    		[
    			-192,
    			-105
    		],
    		[
    			230,
    			-292
    		],
    		[
    			-264,
    			-202
    		],
    		[
    			-429,
    			-167
    		],
    		[
    			-132,
    			-232
    		],
    		[
    			-388,
    			-176
    		],
    		[
    			39,
    			-134
    		],
    		[
    			475,
    			23
    		],
    		[
    			6,
    			-144
    		],
    		[
    			-742,
    			-355
    		],
    		[
    			-726,
    			163
    		],
    		[
    			-816,
    			-91
    		],
    		[
    			-414,
    			71
    		],
    		[
    			-525,
    			31
    		],
    		[
    			-35,
    			284
    		],
    		[
    			514,
    			133
    		],
    		[
    			-137,
    			427
    		],
    		[
    			170,
    			41
    		],
    		[
    			742,
    			-255
    		],
    		[
    			-379,
    			379
    		],
    		[
    			-450,
    			113
    		],
    		[
    			225,
    			229
    		],
    		[
    			492,
    			141
    		],
    		[
    			79,
    			206
    		],
    		[
    			-392,
    			231
    		],
    		[
    			-118,
    			304
    		],
    		[
    			759,
    			-26
    		],
    		[
    			220,
    			-64
    		],
    		[
    			433,
    			216
    		],
    		[
    			-625,
    			68
    		],
    		[
    			-972,
    			-38
    		],
    		[
    			-491,
    			201
    		],
    		[
    			-232,
    			239
    		],
    		[
    			-324,
    			173
    		],
    		[
    			-61,
    			202
    		]
    	],
    	[
    		[
    			29106,
    			90427
    		],
    		[
    			-180,
    			-174
    		],
    		[
    			-312,
    			-30
    		],
    		[
    			-69,
    			289
    		],
    		[
    			118,
    			331
    		],
    		[
    			255,
    			82
    		],
    		[
    			217,
    			-163
    		],
    		[
    			3,
    			-253
    		],
    		[
    			-32,
    			-82
    		]
    	],
    	[
    		[
    			23262,
    			91636
    		],
    		[
    			169,
    			-226
    		],
    		[
    			-173,
    			-207
    		],
    		[
    			-374,
    			179
    		],
    		[
    			-226,
    			-65
    		],
    		[
    			-380,
    			266
    		],
    		[
    			245,
    			183
    		],
    		[
    			194,
    			256
    		],
    		[
    			295,
    			-168
    		],
    		[
    			166,
    			-106
    		],
    		[
    			84,
    			-112
    		]
    	],
    	[
    		[
    			32078,
    			80046
    		],
    		[
    			96,
    			49
    		],
    		[
    			365,
    			-148
    		],
    		[
    			284,
    			-247
    		],
    		[
    			8,
    			-108
    		],
    		[
    			-135,
    			-11
    		],
    		[
    			-360,
    			186
    		],
    		[
    			-258,
    			279
    		]
    	],
    	[
    		[
    			32218,
    			78370
    		],
    		[
    			97,
    			-288
    		],
    		[
    			202,
    			-79
    		],
    		[
    			257,
    			16
    		],
    		[
    			-137,
    			-242
    		],
    		[
    			-102,
    			-38
    		],
    		[
    			-353,
    			250
    		],
    		[
    			-69,
    			198
    		],
    		[
    			105,
    			183
    		]
    	],
    	[
    		[
    			31350,
    			77248
    		],
    		[
    			48,
    			-194
    		],
    		[
    			-296,
    			-286
    		],
    		[
    			-286,
    			-204
    		],
    		[
    			-293,
    			-175
    		],
    		[
    			-147,
    			-351
    		],
    		[
    			-47,
    			-133
    		],
    		[
    			-3,
    			-313
    		],
    		[
    			92,
    			-313
    		],
    		[
    			115,
    			-15
    		],
    		[
    			-29,
    			216
    		],
    		[
    			83,
    			-131
    		],
    		[
    			-22,
    			-169
    		],
    		[
    			-188,
    			-96
    		],
    		[
    			-133,
    			11
    		],
    		[
    			-205,
    			-103
    		],
    		[
    			-121,
    			-29
    		],
    		[
    			-162,
    			-29
    		],
    		[
    			-231,
    			-171
    		],
    		[
    			408,
    			111
    		],
    		[
    			82,
    			-112
    		],
    		[
    			-389,
    			-177
    		],
    		[
    			-177,
    			-1
    		],
    		[
    			8,
    			72
    		],
    		[
    			-84,
    			-164
    		],
    		[
    			82,
    			-27
    		],
    		[
    			-60,
    			-424
    		],
    		[
    			-203,
    			-455
    		],
    		[
    			-20,
    			152
    		],
    		[
    			-61,
    			30
    		],
    		[
    			-91,
    			148
    		],
    		[
    			57,
    			-318
    		],
    		[
    			69,
    			-105
    		],
    		[
    			5,
    			-223
    		],
    		[
    			-89,
    			-230
    		],
    		[
    			-157,
    			-472
    		],
    		[
    			-25,
    			24
    		],
    		[
    			86,
    			402
    		],
    		[
    			-142,
    			225
    		],
    		[
    			-33,
    			491
    		],
    		[
    			-53,
    			-255
    		],
    		[
    			59,
    			-375
    		],
    		[
    			-183,
    			93
    		],
    		[
    			191,
    			-191
    		],
    		[
    			12,
    			-562
    		],
    		[
    			79,
    			-41
    		],
    		[
    			29,
    			-204
    		],
    		[
    			39,
    			-591
    		],
    		[
    			-176,
    			-439
    		],
    		[
    			-288,
    			-175
    		],
    		[
    			-182,
    			-346
    		],
    		[
    			-139,
    			-38
    		],
    		[
    			-141,
    			-217
    		],
    		[
    			-39,
    			-199
    		],
    		[
    			-305,
    			-383
    		],
    		[
    			-157,
    			-281
    		],
    		[
    			-131,
    			-351
    		],
    		[
    			-43,
    			-419
    		],
    		[
    			50,
    			-411
    		],
    		[
    			92,
    			-505
    		],
    		[
    			124,
    			-418
    		],
    		[
    			1,
    			-256
    		],
    		[
    			132,
    			-685
    		],
    		[
    			-9,
    			-398
    		],
    		[
    			-12,
    			-230
    		],
    		[
    			-69,
    			-361
    		],
    		[
    			-83,
    			-75
    		],
    		[
    			-137,
    			72
    		],
    		[
    			-44,
    			259
    		],
    		[
    			-105,
    			136
    		],
    		[
    			-148,
    			508
    		],
    		[
    			-129,
    			452
    		],
    		[
    			-42,
    			231
    		],
    		[
    			57,
    			393
    		],
    		[
    			-77,
    			325
    		],
    		[
    			-217,
    			494
    		],
    		[
    			-108,
    			90
    		],
    		[
    			-281,
    			-268
    		],
    		[
    			-49,
    			30
    		],
    		[
    			-135,
    			275
    		],
    		[
    			-174,
    			147
    		],
    		[
    			-314,
    			-75
    		],
    		[
    			-247,
    			66
    		],
    		[
    			-212,
    			-41
    		],
    		[
    			-114,
    			-92
    		],
    		[
    			50,
    			-157
    		],
    		[
    			-5,
    			-240
    		],
    		[
    			59,
    			-117
    		],
    		[
    			-53,
    			-77
    		],
    		[
    			-103,
    			87
    		],
    		[
    			-104,
    			-112
    		],
    		[
    			-202,
    			18
    		],
    		[
    			-207,
    			312
    		],
    		[
    			-242,
    			-73
    		],
    		[
    			-202,
    			137
    		],
    		[
    			-173,
    			-42
    		],
    		[
    			-234,
    			-138
    		],
    		[
    			-253,
    			-438
    		],
    		[
    			-276,
    			-255
    		],
    		[
    			-152,
    			-282
    		],
    		[
    			-63,
    			-266
    		],
    		[
    			-3,
    			-407
    		],
    		[
    			14,
    			-284
    		],
    		[
    			52,
    			-201
    		]
    	],
    	[
    		[
    			23016,
    			65864
    		],
    		[
    			-108,
    			-18
    		],
    		[
    			-197,
    			130
    		],
    		[
    			-217,
    			184
    		],
    		[
    			-78,
    			277
    		],
    		[
    			-61,
    			414
    		],
    		[
    			-164,
    			337
    		],
    		[
    			-96,
    			346
    		],
    		[
    			-139,
    			404
    		],
    		[
    			-196,
    			236
    		],
    		[
    			-227,
    			-11
    		],
    		[
    			-175,
    			-467
    		],
    		[
    			-230,
    			177
    		],
    		[
    			-144,
    			178
    		],
    		[
    			-69,
    			325
    		],
    		[
    			-92,
    			309
    		],
    		[
    			-165,
    			260
    		],
    		[
    			-142,
    			186
    		],
    		[
    			-102,
    			210
    		],
    		[
    			-481,
    			0
    		],
    		[
    			0,
    			-244
    		],
    		[
    			-221,
    			0
    		],
    		[
    			-552,
    			-4
    		],
    		[
    			-634,
    			416
    		],
    		[
    			-419,
    			287
    		],
    		[
    			26,
    			116
    		],
    		[
    			-353,
    			-64
    		],
    		[
    			-316,
    			-46
    		]
    	],
    	[
    		[
    			17464,
    			69802
    		],
    		[
    			-46,
    			302
    		],
    		[
    			-180,
    			340
    		],
    		[
    			-130,
    			71
    		],
    		[
    			-30,
    			169
    		],
    		[
    			-156,
    			30
    		],
    		[
    			-100,
    			159
    		],
    		[
    			-258,
    			59
    		],
    		[
    			-71,
    			95
    		],
    		[
    			-33,
    			324
    		],
    		[
    			-270,
    			594
    		],
    		[
    			-231,
    			821
    		],
    		[
    			10,
    			137
    		],
    		[
    			-123,
    			195
    		],
    		[
    			-215,
    			495
    		],
    		[
    			-38,
    			482
    		],
    		[
    			-148,
    			323
    		],
    		[
    			61,
    			489
    		],
    		[
    			-10,
    			507
    		],
    		[
    			-89,
    			453
    		],
    		[
    			109,
    			557
    		],
    		[
    			34,
    			536
    		],
    		[
    			33,
    			536
    		],
    		[
    			-50,
    			792
    		],
    		[
    			-88,
    			506
    		],
    		[
    			-80,
    			274
    		],
    		[
    			33,
    			115
    		],
    		[
    			402,
    			-200
    		],
    		[
    			148,
    			-558
    		],
    		[
    			69,
    			156
    		],
    		[
    			-45,
    			484
    		],
    		[
    			-94,
    			485
    		]
    	],
    	[
    		[
    			6833,
    			62443
    		],
    		[
    			49,
    			-51
    		],
    		[
    			45,
    			-79
    		],
    		[
    			71,
    			-207
    		],
    		[
    			-7,
    			-33
    		],
    		[
    			-108,
    			-126
    		],
    		[
    			-89,
    			-92
    		],
    		[
    			-41,
    			-99
    		],
    		[
    			-69,
    			84
    		],
    		[
    			8,
    			165
    		],
    		[
    			-46,
    			216
    		],
    		[
    			14,
    			65
    		],
    		[
    			48,
    			97
    		],
    		[
    			-19,
    			116
    		],
    		[
    			16,
    			55
    		],
    		[
    			21,
    			-11
    		],
    		[
    			107,
    			-100
    		]
    	],
    	[
    		[
    			6668,
    			62848
    		],
    		[
    			-23,
    			-71
    		],
    		[
    			-94,
    			-43
    		],
    		[
    			-47,
    			125
    		],
    		[
    			-32,
    			48
    		],
    		[
    			-3,
    			37
    		],
    		[
    			27,
    			50
    		],
    		[
    			99,
    			-56
    		],
    		[
    			73,
    			-90
    		]
    	],
    	[
    		[
    			6456,
    			63091
    		],
    		[
    			-9,
    			-63
    		],
    		[
    			-149,
    			17
    		],
    		[
    			21,
    			72
    		],
    		[
    			137,
    			-26
    		]
    	],
    	[
    		[
    			6104,
    			63411
    		],
    		[
    			23,
    			-38
    		],
    		[
    			80,
    			-196
    		],
    		[
    			-15,
    			-34
    		],
    		[
    			-19,
    			8
    		],
    		[
    			-97,
    			21
    		],
    		[
    			-35,
    			133
    		],
    		[
    			-11,
    			24
    		],
    		[
    			74,
    			82
    		]
    	],
    	[
    		[
    			5732,
    			63705
    		],
    		[
    			5,
    			-138
    		],
    		[
    			-33,
    			-58
    		],
    		[
    			-93,
    			107
    		],
    		[
    			14,
    			43
    		],
    		[
    			43,
    			58
    		],
    		[
    			64,
    			-12
    		]
    	],
    	[
    		[
    			3759,
    			86256
    		],
    		[
    			220,
    			-54
    		],
    		[
    			27,
    			-226
    		],
    		[
    			-171,
    			-92
    		],
    		[
    			-182,
    			110
    		],
    		[
    			-168,
    			161
    		],
    		[
    			274,
    			101
    		]
    	],
    	[
    		[
    			7436,
    			84829
    		],
    		[
    			185,
    			-40
    		],
    		[
    			117,
    			-183
    		],
    		[
    			-240,
    			-281
    		],
    		[
    			-277,
    			-225
    		],
    		[
    			-142,
    			152
    		],
    		[
    			-43,
    			277
    		],
    		[
    			252,
    			210
    		],
    		[
    			148,
    			90
    		]
    	],
    	[
    		[
    			13740,
    			82958
    		],
    		[
    			-153,
    			223
    		],
    		[
    			-245,
    			188
    		],
    		[
    			-78,
    			515
    		],
    		[
    			-358,
    			478
    		],
    		[
    			-150,
    			558
    		],
    		[
    			-267,
    			38
    		],
    		[
    			-441,
    			15
    		],
    		[
    			-326,
    			170
    		],
    		[
    			-574,
    			613
    		],
    		[
    			-266,
    			112
    		],
    		[
    			-486,
    			211
    		],
    		[
    			-385,
    			-51
    		],
    		[
    			-546,
    			272
    		],
    		[
    			-330,
    			252
    		],
    		[
    			-309,
    			-125
    		],
    		[
    			58,
    			-411
    		],
    		[
    			-154,
    			-38
    		],
    		[
    			-321,
    			-123
    		],
    		[
    			-245,
    			-199
    		],
    		[
    			-308,
    			-126
    		],
    		[
    			-39,
    			348
    		],
    		[
    			125,
    			580
    		],
    		[
    			295,
    			182
    		],
    		[
    			-76,
    			148
    		],
    		[
    			-354,
    			-329
    		],
    		[
    			-190,
    			-394
    		],
    		[
    			-400,
    			-420
    		],
    		[
    			203,
    			-287
    		],
    		[
    			-262,
    			-424
    		],
    		[
    			-299,
    			-248
    		],
    		[
    			-278,
    			-180
    		],
    		[
    			-69,
    			-261
    		],
    		[
    			-434,
    			-305
    		],
    		[
    			-87,
    			-278
    		],
    		[
    			-325,
    			-252
    		],
    		[
    			-191,
    			45
    		],
    		[
    			-259,
    			-165
    		],
    		[
    			-282,
    			-201
    		],
    		[
    			-231,
    			-197
    		],
    		[
    			-477,
    			-169
    		],
    		[
    			-43,
    			99
    		],
    		[
    			304,
    			276
    		],
    		[
    			271,
    			182
    		],
    		[
    			296,
    			324
    		],
    		[
    			345,
    			66
    		],
    		[
    			137,
    			243
    		],
    		[
    			385,
    			353
    		],
    		[
    			62,
    			119
    		],
    		[
    			205,
    			208
    		],
    		[
    			48,
    			448
    		],
    		[
    			141,
    			349
    		],
    		[
    			-320,
    			-179
    		],
    		[
    			-90,
    			102
    		],
    		[
    			-150,
    			-215
    		],
    		[
    			-181,
    			300
    		],
    		[
    			-75,
    			-212
    		],
    		[
    			-104,
    			294
    		],
    		[
    			-278,
    			-236
    		],
    		[
    			-170,
    			0
    		],
    		[
    			-24,
    			352
    		],
    		[
    			50,
    			216
    		],
    		[
    			-179,
    			211
    		],
    		[
    			-361,
    			-113
    		],
    		[
    			-235,
    			277
    		],
    		[
    			-190,
    			142
    		],
    		[
    			-1,
    			334
    		],
    		[
    			-214,
    			252
    		],
    		[
    			108,
    			340
    		],
    		[
    			226,
    			330
    		],
    		[
    			99,
    			303
    		],
    		[
    			225,
    			43
    		],
    		[
    			191,
    			-94
    		],
    		[
    			224,
    			285
    		],
    		[
    			201,
    			-51
    		],
    		[
    			212,
    			183
    		],
    		[
    			-52,
    			270
    		],
    		[
    			-155,
    			106
    		],
    		[
    			205,
    			228
    		],
    		[
    			-170,
    			-7
    		],
    		[
    			-295,
    			-128
    		],
    		[
    			-85,
    			-131
    		],
    		[
    			-219,
    			131
    		],
    		[
    			-392,
    			-67
    		],
    		[
    			-407,
    			142
    		],
    		[
    			-117,
    			238
    		],
    		[
    			-351,
    			343
    		],
    		[
    			390,
    			247
    		],
    		[
    			620,
    			289
    		],
    		[
    			228,
    			0
    		],
    		[
    			-38,
    			-296
    		],
    		[
    			586,
    			23
    		],
    		[
    			-225,
    			366
    		],
    		[
    			-342,
    			225
    		],
    		[
    			-197,
    			296
    		],
    		[
    			-267,
    			252
    		],
    		[
    			-381,
    			187
    		],
    		[
    			155,
    			309
    		],
    		[
    			493,
    			19
    		],
    		[
    			350,
    			270
    		],
    		[
    			66,
    			287
    		],
    		[
    			284,
    			281
    		],
    		[
    			271,
    			68
    		],
    		[
    			526,
    			262
    		],
    		[
    			256,
    			-40
    		],
    		[
    			427,
    			315
    		],
    		[
    			421,
    			-124
    		],
    		[
    			201,
    			-266
    		],
    		[
    			123,
    			114
    		],
    		[
    			469,
    			-35
    		],
    		[
    			-16,
    			-136
    		],
    		[
    			425,
    			-101
    		],
    		[
    			283,
    			59
    		],
    		[
    			585,
    			-186
    		],
    		[
    			534,
    			-56
    		],
    		[
    			214,
    			-77
    		],
    		[
    			370,
    			96
    		],
    		[
    			421,
    			-177
    		],
    		[
    			302,
    			-83
    		]
    	],
    	[
    		[
    			2297,
    			88264
    		],
    		[
    			171,
    			-113
    		],
    		[
    			173,
    			61
    		],
    		[
    			225,
    			-156
    		],
    		[
    			276,
    			-79
    		],
    		[
    			-23,
    			-64
    		],
    		[
    			-211,
    			-125
    		],
    		[
    			-211,
    			128
    		],
    		[
    			-106,
    			107
    		],
    		[
    			-245,
    			-34
    		],
    		[
    			-66,
    			52
    		],
    		[
    			17,
    			223
    		]
    	],
    	[
    		[
    			74266,
    			79657
    		],
    		[
    			-212,
    			-393
    		],
    		[
    			-230,
    			-56
    		],
    		[
    			-13,
    			-592
    		],
    		[
    			-155,
    			-267
    		],
    		[
    			-551,
    			194
    		],
    		[
    			-200,
    			-1058
    		],
    		[
    			-143,
    			-131
    		],
    		[
    			-550,
    			-236
    		],
    		[
    			250,
    			-1026
    		],
    		[
    			-190,
    			-154
    		],
    		[
    			22,
    			-337
    		]
    	],
    	[
    		[
    			72294,
    			75601
    		],
    		[
    			-171,
    			87
    		],
    		[
    			-140,
    			212
    		],
    		[
    			-412,
    			62
    		],
    		[
    			-461,
    			16
    		],
    		[
    			-100,
    			-65
    		],
    		[
    			-396,
    			248
    		],
    		[
    			-158,
    			-122
    		],
    		[
    			-43,
    			-349
    		],
    		[
    			-457,
    			204
    		],
    		[
    			-183,
    			-84
    		],
    		[
    			-62,
    			-259
    		]
    	],
    	[
    		[
    			69711,
    			75551
    		],
    		[
    			-159,
    			-109
    		],
    		[
    			-367,
    			-412
    		],
    		[
    			-121,
    			-422
    		],
    		[
    			-104,
    			-4
    		],
    		[
    			-76,
    			280
    		],
    		[
    			-353,
    			19
    		],
    		[
    			-57,
    			484
    		],
    		[
    			-135,
    			4
    		],
    		[
    			21,
    			593
    		],
    		[
    			-333,
    			431
    		],
    		[
    			-476,
    			-46
    		],
    		[
    			-326,
    			-86
    		],
    		[
    			-265,
    			533
    		],
    		[
    			-227,
    			223
    		],
    		[
    			-431,
    			423
    		],
    		[
    			-52,
    			51
    		],
    		[
    			-715,
    			-349
    		],
    		[
    			11,
    			-2178
    		]
    	],
    	[
    		[
    			65546,
    			74986
    		],
    		[
    			-142,
    			-29
    		],
    		[
    			-195,
    			463
    		],
    		[
    			-188,
    			166
    		],
    		[
    			-315,
    			-123
    		],
    		[
    			-123,
    			-197
    		]
    	],
    	[
    		[
    			64583,
    			75266
    		],
    		[
    			-15,
    			144
    		],
    		[
    			68,
    			246
    		],
    		[
    			-53,
    			206
    		],
    		[
    			-322,
    			202
    		],
    		[
    			-125,
    			530
    		],
    		[
    			-154,
    			150
    		],
    		[
    			-9,
    			192
    		],
    		[
    			270,
    			-56
    		],
    		[
    			11,
    			432
    		],
    		[
    			236,
    			96
    		],
    		[
    			243,
    			-88
    		],
    		[
    			50,
    			576
    		],
    		[
    			-50,
    			365
    		],
    		[
    			-278,
    			-28
    		],
    		[
    			-236,
    			144
    		],
    		[
    			-321,
    			-260
    		],
    		[
    			-259,
    			-124
    		]
    	],
    	[
    		[
    			63639,
    			77993
    		],
    		[
    			-142,
    			96
    		],
    		[
    			29,
    			304
    		],
    		[
    			-177,
    			395
    		],
    		[
    			-207,
    			-17
    		],
    		[
    			-235,
    			401
    		],
    		[
    			160,
    			448
    		],
    		[
    			-81,
    			120
    		],
    		[
    			222,
    			649
    		],
    		[
    			285,
    			-342
    		],
    		[
    			35,
    			431
    		],
    		[
    			573,
    			643
    		],
    		[
    			434,
    			15
    		],
    		[
    			612,
    			-409
    		],
    		[
    			329,
    			-239
    		],
    		[
    			295,
    			249
    		],
    		[
    			440,
    			12
    		],
    		[
    			356,
    			-306
    		],
    		[
    			80,
    			175
    		],
    		[
    			391,
    			-25
    		],
    		[
    			69,
    			280
    		],
    		[
    			-450,
    			406
    		],
    		[
    			267,
    			288
    		],
    		[
    			-52,
    			161
    		],
    		[
    			266,
    			153
    		],
    		[
    			-200,
    			405
    		],
    		[
    			127,
    			202
    		],
    		[
    			1039,
    			205
    		],
    		[
    			136,
    			146
    		],
    		[
    			695,
    			218
    		],
    		[
    			250,
    			245
    		],
    		[
    			499,
    			-127
    		],
    		[
    			88,
    			-612
    		],
    		[
    			290,
    			144
    		],
    		[
    			356,
    			-202
    		],
    		[
    			-23,
    			-322
    		],
    		[
    			267,
    			33
    		],
    		[
    			696,
    			558
    		],
    		[
    			-102,
    			-185
    		],
    		[
    			355,
    			-457
    		],
    		[
    			620,
    			-1500
    		],
    		[
    			148,
    			309
    		],
    		[
    			383,
    			-340
    		],
    		[
    			399,
    			151
    		],
    		[
    			154,
    			-106
    		],
    		[
    			133,
    			-341
    		],
    		[
    			194,
    			-115
    		],
    		[
    			119,
    			-251
    		],
    		[
    			358,
    			79
    		],
    		[
    			147,
    			-361
    		]
    	],
    	[
    		[
    			69711,
    			75551
    		],
    		[
    			83,
    			-58
    		],
    		[
    			-234,
    			-382
    		],
    		[
    			205,
    			-223
    		],
    		[
    			198,
    			147
    		],
    		[
    			329,
    			-311
    		],
    		[
    			-355,
    			-425
    		],
    		[
    			-212,
    			58
    		]
    	],
    	[
    		[
    			69725,
    			74357
    		],
    		[
    			-114,
    			-15
    		],
    		[
    			-40,
    			164
    		],
    		[
    			58,
    			274
    		],
    		[
    			-371,
    			-137
    		],
    		[
    			-89,
    			-380
    		],
    		[
    			-132,
    			-326
    		],
    		[
    			-232,
    			28
    		],
    		[
    			-72,
    			-261
    		],
    		[
    			204,
    			-140
    		],
    		[
    			60,
    			-440
    		],
    		[
    			-156,
    			-598
    		]
    	],
    	[
    		[
    			68841,
    			72526
    		],
    		[
    			-210,
    			124
    		],
    		[
    			-154,
    			4
    		]
    	],
    	[
    		[
    			68477,
    			72654
    		],
    		[
    			7,
    			362
    		],
    		[
    			-369,
    			253
    		],
    		[
    			-291,
    			289
    		],
    		[
    			-181,
    			278
    		],
    		[
    			-317,
    			408
    		],
    		[
    			-137,
    			609
    		],
    		[
    			-93,
    			108
    		],
    		[
    			-301,
    			-27
    		],
    		[
    			-106,
    			121
    		],
    		[
    			-30,
    			471
    		],
    		[
    			-374,
    			312
    		],
    		[
    			-234,
    			-343
    		],
    		[
    			-237,
    			-204
    		],
    		[
    			45,
    			-297
    		],
    		[
    			-313,
    			-8
    		]
    	],
    	[
    		[
    			89166,
    			49043
    		],
    		[
    			482,
    			-407
    		],
    		[
    			513,
    			-338
    		],
    		[
    			192,
    			-302
    		],
    		[
    			154,
    			-297
    		],
    		[
    			43,
    			-349
    		],
    		[
    			462,
    			-365
    		],
    		[
    			68,
    			-313
    		],
    		[
    			-256,
    			-64
    		],
    		[
    			62,
    			-393
    		],
    		[
    			248,
    			-388
    		],
    		[
    			180,
    			-627
    		],
    		[
    			159,
    			20
    		],
    		[
    			-11,
    			-262
    		],
    		[
    			215,
    			-100
    		],
    		[
    			-84,
    			-111
    		],
    		[
    			295,
    			-249
    		],
    		[
    			-30,
    			-171
    		],
    		[
    			-184,
    			-41
    		],
    		[
    			-69,
    			153
    		],
    		[
    			-238,
    			66
    		],
    		[
    			-281,
    			89
    		],
    		[
    			-216,
    			377
    		],
    		[
    			-158,
    			325
    		],
    		[
    			-144,
    			517
    		],
    		[
    			-362,
    			259
    		],
    		[
    			-235,
    			-169
    		],
    		[
    			-170,
    			-195
    		],
    		[
    			35,
    			-436
    		],
    		[
    			-218,
    			-203
    		],
    		[
    			-155,
    			99
    		],
    		[
    			-288,
    			25
    		]
    	],
    	[
    		[
    			89175,
    			45193
    		],
    		[
    			-4,
    			1925
    		],
    		[
    			-5,
    			1925
    		]
    	],
    	[
    		[
    			92399,
    			48417
    		],
    		[
    			106,
    			-189
    		],
    		[
    			33,
    			-307
    		],
    		[
    			-87,
    			-157
    		],
    		[
    			-52,
    			348
    		],
    		[
    			-65,
    			229
    		],
    		[
    			-126,
    			193
    		],
    		[
    			-158,
    			252
    		],
    		[
    			-200,
    			174
    		],
    		[
    			77,
    			143
    		],
    		[
    			150,
    			-166
    		],
    		[
    			94,
    			-130
    		],
    		[
    			117,
    			-142
    		],
    		[
    			111,
    			-248
    		]
    	],
    	[
    		[
    			92027,
    			47129
    		],
    		[
    			-152,
    			-144
    		],
    		[
    			-142,
    			-138
    		],
    		[
    			-148,
    			1
    		],
    		[
    			-228,
    			171
    		],
    		[
    			-158,
    			165
    		],
    		[
    			23,
    			183
    		],
    		[
    			249,
    			-86
    		],
    		[
    			152,
    			46
    		],
    		[
    			42,
    			283
    		],
    		[
    			40,
    			15
    		],
    		[
    			27,
    			-314
    		],
    		[
    			158,
    			45
    		],
    		[
    			78,
    			202
    		],
    		[
    			155,
    			211
    		],
    		[
    			-30,
    			348
    		],
    		[
    			166,
    			11
    		],
    		[
    			56,
    			-97
    		],
    		[
    			-5,
    			-327
    		],
    		[
    			-93,
    			-361
    		],
    		[
    			-146,
    			-48
    		],
    		[
    			-44,
    			-166
    		]
    	],
    	[
    		[
    			92988,
    			47425
    		],
    		[
    			84,
    			-134
    		],
    		[
    			135,
    			-375
    		],
    		[
    			131,
    			-200
    		],
    		[
    			-39,
    			-166
    		],
    		[
    			-78,
    			-59
    		],
    		[
    			-120,
    			227
    		],
    		[
    			-122,
    			375
    		],
    		[
    			-59,
    			450
    		],
    		[
    			38,
    			57
    		],
    		[
    			30,
    			-175
    		]
    	],
    	[
    		[
    			89175,
    			45193
    		],
    		[
    			-247,
    			485
    		],
    		[
    			-282,
    			118
    		],
    		[
    			-69,
    			-168
    		],
    		[
    			-352,
    			-18
    		],
    		[
    			118,
    			481
    		],
    		[
    			175,
    			164
    		],
    		[
    			-72,
    			642
    		],
    		[
    			-134,
    			496
    		],
    		[
    			-538,
    			500
    		],
    		[
    			-229,
    			50
    		],
    		[
    			-417,
    			546
    		],
    		[
    			-82,
    			-287
    		],
    		[
    			-107,
    			-52
    		],
    		[
    			-63,
    			216
    		],
    		[
    			-1,
    			257
    		],
    		[
    			-212,
    			290
    		],
    		[
    			299,
    			213
    		],
    		[
    			198,
    			-11
    		],
    		[
    			-23,
    			156
    		],
    		[
    			-407,
    			1
    		],
    		[
    			-110,
    			352
    		],
    		[
    			-248,
    			109
    		],
    		[
    			-117,
    			293
    		],
    		[
    			374,
    			143
    		],
    		[
    			142,
    			192
    		],
    		[
    			446,
    			-242
    		],
    		[
    			44,
    			-220
    		],
    		[
    			78,
    			-955
    		],
    		[
    			287,
    			-354
    		],
    		[
    			232,
    			627
    		],
    		[
    			319,
    			356
    		],
    		[
    			247,
    			1
    		],
    		[
    			238,
    			-206
    		],
    		[
    			206,
    			-212
    		],
    		[
    			298,
    			-113
    		]
    	],
    	[
    		[
    			84713,
    			45326
    		],
    		[
    			28,
    			-117
    		],
    		[
    			5,
    			-179
    		]
    	],
    	[
    		[
    			84746,
    			45030
    		],
    		[
    			-181,
    			-441
    		],
    		[
    			-238,
    			-130
    		],
    		[
    			-33,
    			71
    		],
    		[
    			25,
    			201
    		],
    		[
    			119,
    			360
    		],
    		[
    			275,
    			235
    		]
    	],
    	[
    		[
    			87280,
    			46506
    		],
    		[
    			-27,
    			445
    		],
    		[
    			49,
    			212
    		],
    		[
    			58,
    			200
    		],
    		[
    			63,
    			-173
    		],
    		[
    			0,
    			-282
    		],
    		[
    			-143,
    			-402
    		]
    	],
    	[
    		[
    			82744,
    			53024
    		],
    		[
    			-158,
    			-533
    		],
    		[
    			204,
    			-560
    		],
    		[
    			-48,
    			-272
    		],
    		[
    			312,
    			-546
    		],
    		[
    			-329,
    			-70
    		],
    		[
    			-93,
    			-403
    		],
    		[
    			12,
    			-535
    		],
    		[
    			-267,
    			-404
    		],
    		[
    			-7,
    			-589
    		],
    		[
    			-107,
    			-903
    		],
    		[
    			-41,
    			210
    		],
    		[
    			-316,
    			-266
    		],
    		[
    			-110,
    			361
    		],
    		[
    			-198,
    			34
    		],
    		[
    			-139,
    			189
    		],
    		[
    			-330,
    			-212
    		],
    		[
    			-101,
    			285
    		],
    		[
    			-182,
    			-32
    		],
    		[
    			-229,
    			68
    		],
    		[
    			-43,
    			793
    		],
    		[
    			-138,
    			164
    		],
    		[
    			-134,
    			505
    		],
    		[
    			-38,
    			517
    		],
    		[
    			32,
    			548
    		],
    		[
    			165,
    			392
    		]
    	],
    	[
    		[
    			80461,
    			51765
    		],
    		[
    			47,
    			-395
    		],
    		[
    			190,
    			-334
    		],
    		[
    			179,
    			121
    		],
    		[
    			177,
    			-43
    		],
    		[
    			162,
    			299
    		],
    		[
    			133,
    			52
    		],
    		[
    			263,
    			-166
    		],
    		[
    			226,
    			126
    		],
    		[
    			143,
    			822
    		],
    		[
    			107,
    			205
    		],
    		[
    			96,
    			672
    		],
    		[
    			319,
    			0
    		],
    		[
    			241,
    			-100
    		]
    	],
    	[
    		[
    			85936,
    			48924
    		],
    		[
    			305,
    			-172
    		],
    		[
    			101,
    			-452
    		],
    		[
    			-234,
    			244
    		],
    		[
    			-232,
    			49
    		],
    		[
    			-157,
    			-39
    		],
    		[
    			-192,
    			21
    		],
    		[
    			65,
    			325
    		],
    		[
    			344,
    			24
    		]
    	],
    	[
    		[
    			85242,
    			48340
    		],
    		[
    			-192,
    			108
    		],
    		[
    			-54,
    			254
    		],
    		[
    			281,
    			29
    		],
    		[
    			69,
    			-195
    		],
    		[
    			-104,
    			-196
    		]
    	],
    	[
    		[
    			85536,
    			51864
    		],
    		[
    			20,
    			-322
    		],
    		[
    			164,
    			-52
    		],
    		[
    			26,
    			-241
    		],
    		[
    			-15,
    			-517
    		],
    		[
    			-143,
    			58
    		],
    		[
    			-42,
    			-359
    		],
    		[
    			114,
    			-312
    		],
    		[
    			-78,
    			-71
    		],
    		[
    			-112,
    			374
    		],
    		[
    			-82,
    			755
    		],
    		[
    			56,
    			472
    		],
    		[
    			92,
    			215
    		]
    	],
    	[
    		[
    			84146,
    			51097
    		],
    		[
    			319,
    			25
    		],
    		[
    			275,
    			429
    		],
    		[
    			48,
    			-132
    		],
    		[
    			-223,
    			-587
    		],
    		[
    			-209,
    			-113
    		],
    		[
    			-267,
    			115
    		],
    		[
    			-463,
    			-29
    		],
    		[
    			-243,
    			-85
    		],
    		[
    			-39,
    			-447
    		],
    		[
    			248,
    			-526
    		],
    		[
    			150,
    			268
    		],
    		[
    			518,
    			201
    		],
    		[
    			-22,
    			-272
    		],
    		[
    			-121,
    			86
    		],
    		[
    			-121,
    			-347
    		],
    		[
    			-245,
    			-229
    		],
    		[
    			263,
    			-757
    		],
    		[
    			-50,
    			-203
    		],
    		[
    			249,
    			-682
    		],
    		[
    			-2,
    			-388
    		],
    		[
    			-148,
    			-173
    		],
    		[
    			-109,
    			207
    		],
    		[
    			134,
    			484
    		],
    		[
    			-273,
    			-229
    		],
    		[
    			-69,
    			164
    		],
    		[
    			36,
    			228
    		],
    		[
    			-200,
    			346
    		],
    		[
    			21,
    			576
    		],
    		[
    			-186,
    			-179
    		],
    		[
    			24,
    			-689
    		],
    		[
    			11,
    			-846
    		],
    		[
    			-176,
    			-85
    		],
    		[
    			-119,
    			173
    		],
    		[
    			79,
    			544
    		],
    		[
    			-43,
    			570
    		],
    		[
    			-117,
    			4
    		],
    		[
    			-86,
    			405
    		],
    		[
    			115,
    			387
    		],
    		[
    			40,
    			469
    		],
    		[
    			139,
    			891
    		],
    		[
    			58,
    			243
    		],
    		[
    			237,
    			439
    		],
    		[
    			217,
    			-174
    		],
    		[
    			350,
    			-82
    		]
    	],
    	[
    		[
    			83414,
    			44519
    		],
    		[
    			-368,
    			414
    		],
    		[
    			259,
    			116
    		],
    		[
    			146,
    			-180
    		],
    		[
    			97,
    			-180
    		],
    		[
    			-17,
    			-159
    		],
    		[
    			-117,
    			-11
    		]
    	],
    	[
    		[
    			83705,
    			45536
    		],
    		[
    			185,
    			45
    		],
    		[
    			249,
    			216
    		],
    		[
    			-41,
    			-328
    		],
    		[
    			-417,
    			-168
    		],
    		[
    			-370,
    			73
    		],
    		[
    			0,
    			216
    		],
    		[
    			220,
    			123
    		],
    		[
    			174,
    			-177
    		]
    	],
    	[
    		[
    			82849,
    			45639
    		],
    		[
    			172,
    			48
    		],
    		[
    			69,
    			-251
    		],
    		[
    			-321,
    			-119
    		],
    		[
    			-193,
    			-79
    		],
    		[
    			-149,
    			5
    		],
    		[
    			95,
    			340
    		],
    		[
    			153,
    			5
    		],
    		[
    			74,
    			209
    		],
    		[
    			100,
    			-158
    		]
    	],
    	[
    		[
    			80134,
    			46785
    		],
    		[
    			38,
    			-210
    		],
    		[
    			533,
    			-59
    		],
    		[
    			61,
    			244
    		],
    		[
    			515,
    			-284
    		],
    		[
    			101,
    			-383
    		],
    		[
    			417,
    			-108
    		],
    		[
    			341,
    			-351
    		],
    		[
    			-317,
    			-225
    		],
    		[
    			-306,
    			238
    		],
    		[
    			-251,
    			-16
    		],
    		[
    			-288,
    			44
    		],
    		[
    			-260,
    			106
    		],
    		[
    			-322,
    			225
    		],
    		[
    			-204,
    			59
    		],
    		[
    			-116,
    			-74
    		],
    		[
    			-506,
    			243
    		],
    		[
    			-48,
    			254
    		],
    		[
    			-255,
    			44
    		],
    		[
    			191,
    			564
    		],
    		[
    			337,
    			-35
    		],
    		[
    			224,
    			-231
    		],
    		[
    			115,
    			-45
    		]
    	],
    	[
    		[
    			78991,
    			49939
    		],
    		[
    			47,
    			-412
    		],
    		[
    			97,
    			-330
    		],
    		[
    			204,
    			-52
    		],
    		[
    			135,
    			-374
    		],
    		[
    			-70,
    			-735
    		],
    		[
    			-11,
    			-914
    		],
    		[
    			-308,
    			-12
    		],
    		[
    			-234,
    			494
    		],
    		[
    			-356,
    			482
    		],
    		[
    			-119,
    			358
    		],
    		[
    			-210,
    			481
    		],
    		[
    			-138,
    			443
    		],
    		[
    			-212,
    			827
    		],
    		[
    			-244,
    			493
    		],
    		[
    			-81,
    			508
    		],
    		[
    			-103,
    			461
    		],
    		[
    			-250,
    			372
    		],
    		[
    			-145,
    			506
    		],
    		[
    			-209,
    			330
    		],
    		[
    			-290,
    			652
    		],
    		[
    			-24,
    			300
    		],
    		[
    			178,
    			-24
    		],
    		[
    			430,
    			-114
    		],
    		[
    			246,
    			-577
    		],
    		[
    			215,
    			-401
    		],
    		[
    			153,
    			-246
    		],
    		[
    			263,
    			-635
    		],
    		[
    			283,
    			-9
    		],
    		[
    			233,
    			-405
    		],
    		[
    			161,
    			-495
    		],
    		[
    			211,
    			-270
    		],
    		[
    			-111,
    			-482
    		],
    		[
    			159,
    			-205
    		],
    		[
    			100,
    			-15
    		]
    	],
    	[
    		[
    			30935,
    			19481
    		],
    		[
    			106,
    			-274
    		],
    		[
    			139,
    			-443
    		],
    		[
    			361,
    			-355
    		],
    		[
    			389,
    			-147
    		],
    		[
    			-125,
    			-296
    		],
    		[
    			-264,
    			-29
    		],
    		[
    			-141,
    			208
    		]
    	],
    	[
    		[
    			31400,
    			18145
    		],
    		[
    			-168,
    			16
    		],
    		[
    			-297,
    			1
    		],
    		[
    			0,
    			1319
    		]
    	],
    	[
    		[
    			33993,
    			32727
    		],
    		[
    			-70,
    			-473
    		],
    		[
    			-74,
    			-607
    		],
    		[
    			3,
    			-588
    		],
    		[
    			-61,
    			-132
    		],
    		[
    			-21,
    			-382
    		]
    	],
    	[
    		[
    			33770,
    			30545
    		],
    		[
    			-19,
    			-308
    		],
    		[
    			353,
    			-506
    		],
    		[
    			-38,
    			-408
    		],
    		[
    			173,
    			-257
    		],
    		[
    			-14,
    			-289
    		],
    		[
    			-267,
    			-757
    		],
    		[
    			-412,
    			-317
    		],
    		[
    			-557,
    			-123
    		],
    		[
    			-305,
    			59
    		],
    		[
    			59,
    			-352
    		],
    		[
    			-57,
    			-442
    		],
    		[
    			51,
    			-298
    		],
    		[
    			-167,
    			-208
    		],
    		[
    			-284,
    			-82
    		],
    		[
    			-267,
    			216
    		],
    		[
    			-108,
    			-155
    		],
    		[
    			39,
    			-587
    		],
    		[
    			188,
    			-178
    		],
    		[
    			152,
    			186
    		],
    		[
    			82,
    			-307
    		],
    		[
    			-255,
    			-183
    		],
    		[
    			-223,
    			-367
    		],
    		[
    			-41,
    			-595
    		],
    		[
    			-66,
    			-316
    		],
    		[
    			-262,
    			-2
    		],
    		[
    			-218,
    			-302
    		],
    		[
    			-80,
    			-443
    		],
    		[
    			273,
    			-433
    		],
    		[
    			266,
    			-119
    		],
    		[
    			-96,
    			-531
    		],
    		[
    			-328,
    			-333
    		],
    		[
    			-180,
    			-692
    		],
    		[
    			-254,
    			-234
    		],
    		[
    			-113,
    			-276
    		],
    		[
    			89,
    			-614
    		],
    		[
    			185,
    			-342
    		],
    		[
    			-117,
    			30
    		]
    	],
    	[
    		[
    			30952,
    			19680
    		],
    		[
    			-257,
    			93
    		],
    		[
    			-672,
    			79
    		],
    		[
    			-115,
    			344
    		],
    		[
    			6,
    			443
    		],
    		[
    			-185,
    			-38
    		],
    		[
    			-98,
    			214
    		],
    		[
    			-24,
    			626
    		],
    		[
    			213,
    			260
    		],
    		[
    			88,
    			375
    		],
    		[
    			-33,
    			299
    		],
    		[
    			148,
    			504
    		],
    		[
    			101,
    			782
    		],
    		[
    			-30,
    			347
    		],
    		[
    			122,
    			112
    		],
    		[
    			-30,
    			223
    		],
    		[
    			-129,
    			118
    		],
    		[
    			92,
    			248
    		],
    		[
    			-126,
    			224
    		],
    		[
    			-65,
    			682
    		],
    		[
    			112,
    			120
    		],
    		[
    			-47,
    			720
    		],
    		[
    			65,
    			605
    		],
    		[
    			75,
    			527
    		],
    		[
    			166,
    			215
    		],
    		[
    			-84,
    			576
    		],
    		[
    			-1,
    			543
    		],
    		[
    			210,
    			386
    		],
    		[
    			-7,
    			494
    		],
    		[
    			159,
    			576
    		],
    		[
    			1,
    			544
    		],
    		[
    			-72,
    			108
    		],
    		[
    			-128,
    			1020
    		],
    		[
    			171,
    			607
    		],
    		[
    			-27,
    			572
    		],
    		[
    			100,
    			537
    		],
    		[
    			182,
    			555
    		],
    		[
    			196,
    			367
    		],
    		[
    			-83,
    			232
    		],
    		[
    			58,
    			190
    		],
    		[
    			-9,
    			985
    		],
    		[
    			302,
    			291
    		],
    		[
    			96,
    			614
    		],
    		[
    			-34,
    			148
    		]
    	],
    	[
    		[
    			31359,
    			37147
    		],
    		[
    			231,
    			534
    		],
    		[
    			364,
    			-144
    		],
    		[
    			163,
    			-427
    		],
    		[
    			109,
    			475
    		],
    		[
    			316,
    			-24
    		],
    		[
    			45,
    			-127
    		]
    	],
    	[
    		[
    			32587,
    			37434
    		],
    		[
    			511,
    			-964
    		],
    		[
    			227,
    			-89
    		],
    		[
    			339,
    			-437
    		],
    		[
    			286,
    			-231
    		],
    		[
    			40,
    			-261
    		],
    		[
    			-273,
    			-898
    		],
    		[
    			280,
    			-160
    		],
    		[
    			312,
    			-91
    		],
    		[
    			220,
    			95
    		],
    		[
    			252,
    			453
    		],
    		[
    			45,
    			521
    		]
    	],
    	[
    		[
    			34826,
    			35372
    		],
    		[
    			138,
    			114
    		],
    		[
    			139,
    			-341
    		],
    		[
    			-6,
    			-472
    		],
    		[
    			-234,
    			-326
    		],
    		[
    			-186,
    			-241
    		],
    		[
    			-314,
    			-573
    		],
    		[
    			-370,
    			-806
    		]
    	],
    	[
    		[
    			31400,
    			18145
    		],
    		[
    			-92,
    			-239
    		],
    		[
    			-238,
    			-183
    		],
    		[
    			-137,
    			19
    		],
    		[
    			-164,
    			48
    		],
    		[
    			-202,
    			177
    		],
    		[
    			-291,
    			86
    		],
    		[
    			-350,
    			330
    		],
    		[
    			-283,
    			317
    		],
    		[
    			-383,
    			662
    		],
    		[
    			229,
    			-124
    		],
    		[
    			390,
    			-395
    		],
    		[
    			369,
    			-212
    		],
    		[
    			143,
    			271
    		],
    		[
    			90,
    			405
    		],
    		[
    			256,
    			244
    		],
    		[
    			198,
    			-70
    		]
    	],
    	[
    		[
    			30669,
    			40193
    		],
    		[
    			136,
    			-402
    		],
    		[
    			37,
    			-426
    		],
    		[
    			146,
    			-250
    		],
    		[
    			-88,
    			-572
    		],
    		[
    			150,
    			-663
    		],
    		[
    			109,
    			-814
    		],
    		[
    			200,
    			81
    		]
    	],
    	[
    		[
    			30952,
    			19680
    		],
    		[
    			-247,
    			4
    		],
    		[
    			-134,
    			-145
    		],
    		[
    			-250,
    			-213
    		],
    		[
    			-45,
    			-552
    		],
    		[
    			-118,
    			-14
    		],
    		[
    			-313,
    			192
    		],
    		[
    			-318,
    			412
    		],
    		[
    			-346,
    			338
    		],
    		[
    			-87,
    			374
    		],
    		[
    			79,
    			346
    		],
    		[
    			-140,
    			393
    		],
    		[
    			-36,
    			1007
    		],
    		[
    			119,
    			568
    		],
    		[
    			293,
    			457
    		],
    		[
    			-422,
    			172
    		],
    		[
    			265,
    			522
    		],
    		[
    			94,
    			982
    		],
    		[
    			309,
    			-208
    		],
    		[
    			145,
    			1224
    		],
    		[
    			-186,
    			157
    		],
    		[
    			-87,
    			-738
    		],
    		[
    			-175,
    			83
    		],
    		[
    			87,
    			845
    		],
    		[
    			95,
    			1095
    		],
    		[
    			127,
    			404
    		],
    		[
    			-80,
    			576
    		],
    		[
    			-22,
    			666
    		],
    		[
    			117,
    			19
    		],
    		[
    			170,
    			954
    		],
    		[
    			192,
    			945
    		],
    		[
    			118,
    			881
    		],
    		[
    			-64,
    			885
    		],
    		[
    			83,
    			487
    		],
    		[
    			-34,
    			730
    		],
    		[
    			163,
    			721
    		],
    		[
    			50,
    			1143
    		],
    		[
    			89,
    			1227
    		],
    		[
    			87,
    			1321
    		],
    		[
    			-20,
    			967
    		],
    		[
    			-58,
    			832
    		]
    	],
    	[
    		[
    			30452,
    			39739
    		],
    		[
    			143,
    			151
    		],
    		[
    			74,
    			303
    		]
    	],
    	[
    		[
    			58538,
    			45652
    		],
    		[
    			-109,
    			60
    		],
    		[
    			-373,
    			-99
    		],
    		[
    			-75,
    			-71
    		],
    		[
    			-79,
    			-377
    		],
    		[
    			62,
    			-261
    		],
    		[
    			-49,
    			-699
    		],
    		[
    			-34,
    			-593
    		],
    		[
    			75,
    			-105
    		],
    		[
    			194,
    			-230
    		],
    		[
    			76,
    			107
    		],
    		[
    			23,
    			-637
    		],
    		[
    			-212,
    			5
    		],
    		[
    			-114,
    			325
    		],
    		[
    			-103,
    			252
    		],
    		[
    			-213,
    			82
    		],
    		[
    			-62,
    			310
    		],
    		[
    			-170,
    			-187
    		],
    		[
    			-222,
    			83
    		],
    		[
    			-93,
    			268
    		],
    		[
    			-176,
    			55
    		],
    		[
    			-131,
    			-15
    		],
    		[
    			-15,
    			184
    		],
    		[
    			-96,
    			15
    		]
    	],
    	[
    		[
    			56642,
    			44124
    		],
    		[
    			-127,
    			35
    		],
    		[
    			-172,
    			-89
    		],
    		[
    			-121,
    			15
    		],
    		[
    			-68,
    			-54
    		],
    		[
    			15,
    			703
    		],
    		[
    			-93,
    			219
    		],
    		[
    			-21,
    			363
    		],
    		[
    			41,
    			356
    		],
    		[
    			-56,
    			228
    		],
    		[
    			-5,
    			372
    		],
    		[
    			-337,
    			-5
    		],
    		[
    			24,
    			213
    		],
    		[
    			-142,
    			-2
    		],
    		[
    			-15,
    			-103
    		],
    		[
    			-172,
    			-23
    		],
    		[
    			-69,
    			-344
    		],
    		[
    			-42,
    			-148
    		],
    		[
    			-154,
    			83
    		],
    		[
    			-91,
    			-83
    		],
    		[
    			-184,
    			-47
    		],
    		[
    			-106,
    			309
    		],
    		[
    			-64,
    			191
    		],
    		[
    			-80,
    			354
    		],
    		[
    			-68,
    			440
    		],
    		[
    			-820,
    			8
    		],
    		[
    			-98,
    			-71
    		],
    		[
    			-80,
    			11
    		],
    		[
    			-115,
    			-79
    		]
    	],
    	[
    		[
    			53422,
    			46976
    		],
    		[
    			-39,
    			183
    		]
    	],
    	[
    		[
    			53383,
    			47159
    		],
    		[
    			71,
    			62
    		],
    		[
    			9,
    			258
    		],
    		[
    			45,
    			152
    		],
    		[
    			101,
    			124
    		]
    	],
    	[
    		[
    			53609,
    			47755
    		],
    		[
    			73,
    			-60
    		],
    		[
    			95,
    			226
    		],
    		[
    			152,
    			-6
    		],
    		[
    			17,
    			-167
    		],
    		[
    			104,
    			-105
    		],
    		[
    			164,
    			370
    		],
    		[
    			161,
    			289
    		],
    		[
    			71,
    			189
    		],
    		[
    			-10,
    			486
    		],
    		[
    			121,
    			574
    		],
    		[
    			127,
    			304
    		],
    		[
    			183,
    			285
    		],
    		[
    			32,
    			189
    		],
    		[
    			7,
    			216
    		],
    		[
    			45,
    			205
    		],
    		[
    			-14,
    			335
    		],
    		[
    			34,
    			524
    		],
    		[
    			55,
    			368
    		],
    		[
    			83,
    			316
    		],
    		[
    			16,
    			357
    		]
    	],
    	[
    		[
    			55125,
    			52650
    		],
    		[
    			25,
    			412
    		],
    		[
    			108,
    			300
    		],
    		[
    			149,
    			190
    		],
    		[
    			229,
    			-200
    		],
    		[
    			177,
    			-218
    		],
    		[
    			203,
    			-59
    		],
    		[
    			207,
    			-115
    		],
    		[
    			83,
    			357
    		],
    		[
    			38,
    			46
    		],
    		[
    			127,
    			-60
    		],
    		[
    			309,
    			295
    		],
    		[
    			110,
    			-125
    		],
    		[
    			90,
    			18
    		],
    		[
    			41,
    			143
    		],
    		[
    			104,
    			51
    		],
    		[
    			209,
    			-62
    		],
    		[
    			178,
    			-14
    		],
    		[
    			91,
    			63
    		]
    	],
    	[
    		[
    			57603,
    			53672
    		],
    		[
    			169,
    			-488
    		],
    		[
    			124,
    			-71
    		],
    		[
    			75,
    			99
    		],
    		[
    			128,
    			-39
    		],
    		[
    			155,
    			125
    		],
    		[
    			66,
    			-252
    		],
    		[
    			244,
    			-393
    		]
    	],
    	[
    		[
    			58564,
    			52653
    		],
    		[
    			-16,
    			-691
    		],
    		[
    			111,
    			-80
    		],
    		[
    			-89,
    			-210
    		],
    		[
    			-107,
    			-157
    		],
    		[
    			-106,
    			-308
    		],
    		[
    			-59,
    			-274
    		],
    		[
    			-15,
    			-475
    		],
    		[
    			-65,
    			-225
    		],
    		[
    			-2,
    			-446
    		]
    	],
    	[
    		[
    			58216,
    			49787
    		],
    		[
    			-80,
    			-165
    		],
    		[
    			-10,
    			-351
    		],
    		[
    			-38,
    			-46
    		],
    		[
    			-26,
    			-323
    		]
    	],
    	[
    		[
    			58062,
    			48902
    		],
    		[
    			70,
    			-268
    		],
    		[
    			17,
    			-713
    		]
    	],
    	[
    		[
    			61551,
    			49585
    		],
    		[
    			-165,
    			488
    		],
    		[
    			-3,
    			2152
    		],
    		[
    			243,
    			670
    		]
    	],
    	[
    		[
    			61626,
    			52895
    		],
    		[
    			76,
    			186
    		],
    		[
    			178,
    			11
    		],
    		[
    			247,
    			417
    		],
    		[
    			362,
    			26
    		],
    		[
    			785,
    			1773
    		]
    	],
    	[
    		[
    			63274,
    			55308
    		],
    		[
    			194,
    			493
    		],
    		[
    			125,
    			363
    		],
    		[
    			0,
    			308
    		],
    		[
    			0,
    			596
    		],
    		[
    			1,
    			244
    		],
    		[
    			2,
    			9
    		]
    	],
    	[
    		[
    			63596,
    			57321
    		],
    		[
    			89,
    			12
    		],
    		[
    			128,
    			88
    		],
    		[
    			147,
    			59
    		],
    		[
    			132,
    			202
    		],
    		[
    			105,
    			2
    		],
    		[
    			6,
    			-163
    		],
    		[
    			-25,
    			-344
    		],
    		[
    			1,
    			-310
    		],
    		[
    			-59,
    			-214
    		],
    		[
    			-78,
    			-639
    		],
    		[
    			-134,
    			-659
    		],
    		[
    			-172,
    			-755
    		],
    		[
    			-238,
    			-866
    		],
    		[
    			-237,
    			-661
    		],
    		[
    			-327,
    			-806
    		],
    		[
    			-278,
    			-479
    		],
    		[
    			-415,
    			-586
    		],
    		[
    			-259,
    			-450
    		],
    		[
    			-304,
    			-715
    		],
    		[
    			-64,
    			-312
    		],
    		[
    			-63,
    			-140
    		]
    	],
    	[
    		[
    			59417,
    			50018
    		],
    		[
    			-3,
    			627
    		],
    		[
    			80,
    			239
    		],
    		[
    			137,
    			391
    		],
    		[
    			101,
    			431
    		],
    		[
    			-123,
    			678
    		],
    		[
    			-32,
    			296
    		],
    		[
    			-132,
    			411
    		]
    	],
    	[
    		[
    			59445,
    			53091
    		],
    		[
    			171,
    			352
    		],
    		[
    			188,
    			390
    		]
    	],
    	[
    		[
    			59804,
    			53833
    		],
    		[
    			145,
    			-99
    		],
    		[
    			0,
    			-332
    		],
    		[
    			95,
    			-194
    		],
    		[
    			193,
    			0
    		],
    		[
    			352,
    			-502
    		],
    		[
    			87,
    			-6
    		],
    		[
    			65,
    			16
    		],
    		[
    			62,
    			-68
    		],
    		[
    			185,
    			-47
    		],
    		[
    			82,
    			247
    		],
    		[
    			254,
    			247
    		],
    		[
    			112,
    			-200
    		],
    		[
    			190,
    			0
    		]
    	],
    	[
    		[
    			61551,
    			49585
    		],
    		[
    			-195,
    			-236
    		],
    		[
    			-68,
    			-246
    		],
    		[
    			-104,
    			-44
    		],
    		[
    			-40,
    			-416
    		],
    		[
    			-89,
    			-238
    		],
    		[
    			-54,
    			-393
    		],
    		[
    			-112,
    			-195
    		]
    	],
    	[
    		[
    			56824,
    			55442
    		],
    		[
    			-212,
    			258
    		],
    		[
    			-96,
    			170
    		],
    		[
    			-18,
    			184
    		],
    		[
    			45,
    			246
    		],
    		[
    			-1,
    			241
    		],
    		[
    			-160,
    			369
    		],
    		[
    			-31,
    			253
    		]
    	],
    	[
    		[
    			56351,
    			57163
    		],
    		[
    			3,
    			143
    		],
    		[
    			-102,
    			174
    		],
    		[
    			-3,
    			343
    		],
    		[
    			-58,
    			228
    		],
    		[
    			-98,
    			-34
    		],
    		[
    			28,
    			217
    		],
    		[
    			72,
    			246
    		],
    		[
    			-32,
    			245
    		],
    		[
    			92,
    			181
    		],
    		[
    			-58,
    			138
    		],
    		[
    			73,
    			365
    		],
    		[
    			127,
    			435
    		],
    		[
    			240,
    			-41
    		],
    		[
    			-14,
    			2345
    		]
    	],
    	[
    		[
    			56621,
    			62148
    		],
    		[
    			3,
    			248
    		],
    		[
    			320,
    			2
    		],
    		[
    			0,
    			1180
    		]
    	],
    	[
    		[
    			56944,
    			63578
    		],
    		[
    			1117,
    			0
    		],
    		[
    			1077,
    			0
    		],
    		[
    			1102,
    			0
    		]
    	],
    	[
    		[
    			60240,
    			63578
    		],
    		[
    			90,
    			-580
    		],
    		[
    			-61,
    			-107
    		],
    		[
    			40,
    			-608
    		],
    		[
    			102,
    			-706
    		],
    		[
    			106,
    			-145
    		],
    		[
    			152,
    			-219
    		]
    	],
    	[
    		[
    			60669,
    			61213
    		],
    		[
    			-141,
    			-337
    		],
    		[
    			-204,
    			-97
    		],
    		[
    			-88,
    			-181
    		],
    		[
    			-27,
    			-393
    		],
    		[
    			-120,
    			-868
    		],
    		[
    			30,
    			-236
    		]
    	],
    	[
    		[
    			60119,
    			59101
    		],
    		[
    			-45,
    			-508
    		],
    		[
    			-112,
    			-582
    		],
    		[
    			-168,
    			-293
    		],
    		[
    			-119,
    			-451
    		],
    		[
    			-28,
    			-241
    		],
    		[
    			-132,
    			-166
    		],
    		[
    			-82,
    			-618
    		],
    		[
    			4,
    			-531
    		]
    	],
    	[
    		[
    			59437,
    			55711
    		],
    		[
    			-3,
    			460
    		],
    		[
    			-39,
    			12
    		],
    		[
    			5,
    			294
    		],
    		[
    			-33,
    			203
    		],
    		[
    			-143,
    			233
    		],
    		[
    			-34,
    			426
    		],
    		[
    			34,
    			436
    		],
    		[
    			-129,
    			41
    		],
    		[
    			-19,
    			-132
    		],
    		[
    			-167,
    			-30
    		],
    		[
    			67,
    			-173
    		],
    		[
    			23,
    			-355
    		],
    		[
    			-152,
    			-324
    		],
    		[
    			-138,
    			-426
    		],
    		[
    			-144,
    			-61
    		],
    		[
    			-233,
    			345
    		],
    		[
    			-105,
    			-122
    		],
    		[
    			-29,
    			-172
    		],
    		[
    			-143,
    			-112
    		],
    		[
    			-9,
    			-122
    		],
    		[
    			-277,
    			0
    		],
    		[
    			-38,
    			122
    		],
    		[
    			-200,
    			20
    		],
    		[
    			-100,
    			-101
    		],
    		[
    			-77,
    			51
    		],
    		[
    			-143,
    			344
    		],
    		[
    			-48,
    			163
    		],
    		[
    			-200,
    			-81
    		],
    		[
    			-76,
    			-274
    		],
    		[
    			-72,
    			-528
    		],
    		[
    			-95,
    			-111
    		],
    		[
    			-85,
    			-65
    		],
    		[
    			189,
    			-230
    		]
    	],
    	[
    		[
    			56351,
    			57163
    		],
    		[
    			-176,
    			-101
    		],
    		[
    			-141,
    			-239
    		],
    		[
    			-201,
    			-645
    		],
    		[
    			-261,
    			-273
    		],
    		[
    			-269,
    			36
    		],
    		[
    			-78,
    			-54
    		],
    		[
    			28,
    			-208
    		],
    		[
    			-145,
    			-207
    		],
    		[
    			-118,
    			-230
    		],
    		[
    			-350,
    			-226
    		],
    		[
    			-69,
    			134
    		],
    		[
    			-46,
    			11
    		],
    		[
    			-52,
    			-152
    		],
    		[
    			-229,
    			-44
    		]
    	],
    	[
    		[
    			54244,
    			54965
    		],
    		[
    			43,
    			160
    		],
    		[
    			-87,
    			407
    		],
    		[
    			-39,
    			245
    		],
    		[
    			-121,
    			100
    		],
    		[
    			-164,
    			345
    		],
    		[
    			60,
    			279
    		],
    		[
    			127,
    			-60
    		],
    		[
    			78,
    			42
    		],
    		[
    			155,
    			-6
    		],
    		[
    			-151,
    			537
    		],
    		[
    			10,
    			393
    		],
    		[
    			-18,
    			392
    		],
    		[
    			-111,
    			378
    		]
    	],
    	[
    		[
    			54026,
    			58177
    		],
    		[
    			28,
    			279
    		],
    		[
    			-178,
    			13
    		],
    		[
    			0,
    			380
    		],
    		[
    			-115,
    			219
    		],
    		[
    			120,
    			778
    		],
    		[
    			354,
    			557
    		],
    		[
    			15,
    			769
    		],
    		[
    			107,
    			1199
    		],
    		[
    			60,
    			254
    		],
    		[
    			-116,
    			203
    		],
    		[
    			-4,
    			188
    		],
    		[
    			-104,
    			153
    		],
    		[
    			-68,
    			919
    		]
    	],
    	[
    		[
    			54125,
    			64088
    		],
    		[
    			280,
    			323
    		],
    		[
    			1108,
    			-1132
    		],
    		[
    			1108,
    			-1131
    		]
    	],
    	[
    		[
    			30080,
    			62227
    		],
    		[
    			24,
    			-321
    		],
    		[
    			-21,
    			-228
    		],
    		[
    			-68,
    			-99
    		],
    		[
    			71,
    			-177
    		],
    		[
    			-5,
    			-161
    		]
    	],
    	[
    		[
    			30081,
    			61241
    		],
    		[
    			-185,
    			100
    		],
    		[
    			-131,
    			-41
    		],
    		[
    			-169,
    			43
    		],
    		[
    			-130,
    			-110
    		],
    		[
    			-149,
    			184
    		],
    		[
    			24,
    			190
    		],
    		[
    			256,
    			-82
    		],
    		[
    			210,
    			-47
    		],
    		[
    			100,
    			131
    		],
    		[
    			-127,
    			256
    		],
    		[
    			2,
    			226
    		],
    		[
    			-175,
    			92
    		],
    		[
    			62,
    			163
    		],
    		[
    			170,
    			-26
    		],
    		[
    			241,
    			-93
    		]
    	],
    	[
    		[
    			30080,
    			62227
    		],
    		[
    			34,
    			101
    		],
    		[
    			217,
    			-3
    		],
    		[
    			165,
    			-152
    		],
    		[
    			73,
    			15
    		],
    		[
    			50,
    			-209
    		],
    		[
    			152,
    			11
    		],
    		[
    			-9,
    			-176
    		],
    		[
    			124,
    			-21
    		],
    		[
    			136,
    			-217
    		],
    		[
    			-103,
    			-240
    		],
    		[
    			-132,
    			128
    		],
    		[
    			-127,
    			-25
    		],
    		[
    			-92,
    			28
    		],
    		[
    			-50,
    			-107
    		],
    		[
    			-106,
    			-37
    		],
    		[
    			-43,
    			144
    		],
    		[
    			-92,
    			-85
    		],
    		[
    			-111,
    			-405
    		],
    		[
    			-71,
    			94
    		],
    		[
    			-14,
    			170
    		]
    	],
    	[
    		[
    			76049,
    			98451
    		],
    		[
    			600,
    			133
    		],
    		[
    			540,
    			-297
    		],
    		[
    			640,
    			-572
    		],
    		[
    			-69,
    			-531
    		],
    		[
    			-606,
    			-73
    		],
    		[
    			-773,
    			170
    		],
    		[
    			-462,
    			226
    		],
    		[
    			-213,
    			423
    		],
    		[
    			-379,
    			117
    		],
    		[
    			722,
    			404
    		]
    	],
    	[
    		[
    			78565,
    			97421
    		],
    		[
    			704,
    			-336
    		],
    		[
    			-82,
    			-240
    		],
    		[
    			-1566,
    			-228
    		],
    		[
    			507,
    			776
    		],
    		[
    			229,
    			66
    		],
    		[
    			208,
    			-38
    		]
    	],
    	[
    		[
    			88563,
    			95563
    		],
    		[
    			734,
    			-26
    		],
    		[
    			1004,
    			-313
    		],
    		[
    			-219,
    			-439
    		],
    		[
    			-1023,
    			16
    		],
    		[
    			-461,
    			-139
    		],
    		[
    			-550,
    			384
    		],
    		[
    			149,
    			406
    		],
    		[
    			366,
    			111
    		]
    	],
    	[
    		[
    			91172,
    			95096
    		],
    		[
    			697,
    			-155
    		],
    		[
    			-321,
    			-234
    		],
    		[
    			-444,
    			53
    		],
    		[
    			-516,
    			233
    		],
    		[
    			66,
    			192
    		],
    		[
    			518,
    			-89
    		]
    	],
    	[
    		[
    			88850,
    			93928
    		],
    		[
    			263,
    			234
    		],
    		[
    			348,
    			54
    		],
    		[
    			394,
    			-226
    		],
    		[
    			34,
    			-155
    		],
    		[
    			-421,
    			-4
    		],
    		[
    			-569,
    			66
    		],
    		[
    			-49,
    			31
    		]
    	],
    	[
    		[
    			62457,
    			98194
    		],
    		[
    			542,
    			107
    		],
    		[
    			422,
    			8
    		],
    		[
    			57,
    			-160
    		],
    		[
    			159,
    			142
    		],
    		[
    			262,
    			97
    		],
    		[
    			412,
    			-129
    		],
    		[
    			-107,
    			-90
    		],
    		[
    			-373,
    			-78
    		],
    		[
    			-250,
    			-45
    		],
    		[
    			-39,
    			-97
    		],
    		[
    			-324,
    			-98
    		],
    		[
    			-301,
    			140
    		],
    		[
    			158,
    			185
    		],
    		[
    			-618,
    			18
    		]
    	],
    	[
    		[
    			56314,
    			82678
    		],
    		[
    			-511,
    			-9
    		],
    		[
    			-342,
    			67
    		]
    	],
    	[
    		[
    			55461,
    			82736
    		],
    		[
    			63,
    			260
    		],
    		[
    			383,
    			191
    		]
    	],
    	[
    		[
    			55907,
    			83187
    		],
    		[
    			291,
    			-103
    		],
    		[
    			123,
    			-94
    		],
    		[
    			-30,
    			-162
    		],
    		[
    			23,
    			-150
    		]
    	],
    	[
    		[
    			64863,
    			94153
    		],
    		[
    			665,
    			518
    		],
    		[
    			-75,
    			268
    		],
    		[
    			621,
    			312
    		],
    		[
    			917,
    			380
    		],
    		[
    			925,
    			110
    		],
    		[
    			475,
    			220
    		],
    		[
    			541,
    			76
    		],
    		[
    			193,
    			-233
    		],
    		[
    			-187,
    			-184
    		],
    		[
    			-984,
    			-293
    		],
    		[
    			-848,
    			-282
    		],
    		[
    			-863,
    			-562
    		],
    		[
    			-414,
    			-577
    		],
    		[
    			-435,
    			-568
    		],
    		[
    			56,
    			-491
    		],
    		[
    			531,
    			-484
    		],
    		[
    			-164,
    			-52
    		],
    		[
    			-907,
    			77
    		],
    		[
    			-74,
    			262
    		],
    		[
    			-503,
    			158
    		],
    		[
    			-40,
    			320
    		],
    		[
    			284,
    			126
    		],
    		[
    			-10,
    			323
    		],
    		[
    			551,
    			503
    		],
    		[
    			-255,
    			73
    		]
    	],
    	[
    		[
    			89698,
    			82309
    		],
    		[
    			96,
    			-569
    		],
    		[
    			-7,
    			-581
    		],
    		[
    			114,
    			-597
    		],
    		[
    			280,
    			-1046
    		],
    		[
    			-411,
    			195
    		],
    		[
    			-171,
    			-854
    		],
    		[
    			271,
    			-605
    		],
    		[
    			-8,
    			-413
    		],
    		[
    			-211,
    			356
    		],
    		[
    			-182,
    			-457
    		],
    		[
    			-51,
    			496
    		],
    		[
    			31,
    			575
    		],
    		[
    			-32,
    			638
    		],
    		[
    			64,
    			446
    		],
    		[
    			13,
    			790
    		],
    		[
    			-163,
    			581
    		],
    		[
    			24,
    			808
    		],
    		[
    			257,
    			271
    		],
    		[
    			-110,
    			274
    		],
    		[
    			123,
    			83
    		],
    		[
    			73,
    			-391
    		]
    	],
    	[
    		[
    			86327,
    			75524
    		],
    		[
    			-39,
    			104
    		]
    	],
    	[
    		[
    			86288,
    			75628
    		],
    		[
    			-2,
    			300
    		],
    		[
    			142,
    			16
    		],
    		[
    			40,
    			698
    		],
    		[
    			-73,
    			506
    		],
    		[
    			238,
    			208
    		],
    		[
    			338,
    			-104
    		],
    		[
    			186,
    			575
    		],
    		[
    			96,
    			647
    		],
    		[
    			107,
    			216
    		],
    		[
    			146,
    			532
    		],
    		[
    			-459,
    			-175
    		],
    		[
    			-240,
    			-233
    		],
    		[
    			-423,
    			1
    		],
    		[
    			-112,
    			555
    		],
    		[
    			-329,
    			420
    		],
    		[
    			-483,
    			189
    		],
    		[
    			-103,
    			579
    		],
    		[
    			-97,
    			363
    		],
    		[
    			-104,
    			254
    		],
    		[
    			-172,
    			596
    		],
    		[
    			-244,
    			217
    		],
    		[
    			-415,
    			176
    		],
    		[
    			-369,
    			-16
    		],
    		[
    			-345,
    			-106
    		],
    		[
    			-229,
    			-294
    		],
    		[
    			152,
    			-141
    		],
    		[
    			4,
    			-326
    		],
    		[
    			-155,
    			-189
    		],
    		[
    			-251,
    			-627
    		],
    		[
    			3,
    			-260
    		],
    		[
    			-392,
    			-373
    		],
    		[
    			-333,
    			223
    		]
    	],
    	[
    		[
    			82410,
    			80055
    		],
    		[
    			-331,
    			-49
    		],
    		[
    			-146,
    			198
    		],
    		[
    			-166,
    			63
    		],
    		[
    			-407,
    			-416
    		],
    		[
    			-366,
    			-98
    		],
    		[
    			-255,
    			-146
    		],
    		[
    			-350,
    			96
    		],
    		[
    			-258,
    			-6
    		],
    		[
    			-168,
    			302
    		],
    		[
    			-272,
    			284
    		],
    		[
    			-279,
    			78
    		],
    		[
    			-351,
    			-78
    		],
    		[
    			-263,
    			-109
    		],
    		[
    			-394,
    			248
    		],
    		[
    			-53,
    			443
    		],
    		[
    			-327,
    			152
    		],
    		[
    			-252,
    			69
    		],
    		[
    			-311,
    			244
    		],
    		[
    			-288,
    			-612
    		],
    		[
    			113,
    			-348
    		],
    		[
    			-270,
    			-411
    		],
    		[
    			-402,
    			148
    		],
    		[
    			-277,
    			22
    		],
    		[
    			-186,
    			276
    		],
    		[
    			-289,
    			8
    		],
    		[
    			-242,
    			182
    		],
    		[
    			-423,
    			-278
    		],
    		[
    			-530,
    			-509
    		],
    		[
    			-292,
    			-102
    		]
    	],
    	[
    		[
    			74375,
    			79706
    		],
    		[
    			-109,
    			-49
    		]
    	],
    	[
    		[
    			63639,
    			77993
    		],
    		[
    			-127,
    			-350
    		],
    		[
    			-269,
    			-97
    		],
    		[
    			-276,
    			-610
    		],
    		[
    			252,
    			-561
    		],
    		[
    			-27,
    			-398
    		],
    		[
    			303,
    			-696
    		]
    	],
    	[
    		[
    			63495,
    			75281
    		],
    		[
    			-166,
    			-238
    		],
    		[
    			-48,
    			-150
    		],
    		[
    			-122,
    			40
    		],
    		[
    			-191,
    			359
    		],
    		[
    			-78,
    			20
    		]
    	],
    	[
    		[
    			62890,
    			75312
    		],
    		[
    			-175,
    			137
    		],
    		[
    			-85,
    			242
    		],
    		[
    			-259,
    			124
    		],
    		[
    			-169,
    			-93
    		],
    		[
    			-48,
    			110
    		],
    		[
    			-378,
    			283
    		],
    		[
    			-409,
    			96
    		],
    		[
    			-235,
    			101
    		],
    		[
    			-34,
    			-70
    		]
    	],
    	[
    		[
    			61098,
    			76242
    		],
    		[
    			-354,
    			499
    		],
    		[
    			-317,
    			223
    		],
    		[
    			-240,
    			347
    		],
    		[
    			202,
    			95
    		],
    		[
    			231,
    			494
    		],
    		[
    			-156,
    			234
    		],
    		[
    			410,
    			241
    		],
    		[
    			-8,
    			129
    		],
    		[
    			-249,
    			-95
    		]
    	],
    	[
    		[
    			60617,
    			78409
    		],
    		[
    			9,
    			262
    		],
    		[
    			143,
    			165
    		],
    		[
    			269,
    			43
    		],
    		[
    			44,
    			197
    		],
    		[
    			-62,
    			326
    		],
    		[
    			113,
    			310
    		],
    		[
    			-3,
    			173
    		],
    		[
    			-410,
    			192
    		],
    		[
    			-162,
    			-6
    		],
    		[
    			-172,
    			277
    		],
    		[
    			-213,
    			-94
    		],
    		[
    			-352,
    			208
    		],
    		[
    			6,
    			116
    		],
    		[
    			-99,
    			256
    		],
    		[
    			-222,
    			29
    		],
    		[
    			-23,
    			183
    		],
    		[
    			70,
    			120
    		],
    		[
    			-178,
    			334
    		],
    		[
    			-288,
    			-57
    		],
    		[
    			-84,
    			30
    		],
    		[
    			-70,
    			-134
    		],
    		[
    			-104,
    			23
    		]
    	],
    	[
    		[
    			58829,
    			81362
    		],
    		[
    			-68,
    			379
    		],
    		[
    			-66,
    			196
    		],
    		[
    			54,
    			55
    		],
    		[
    			224,
    			-20
    		],
    		[
    			108,
    			129
    		],
    		[
    			-80,
    			157
    		],
    		[
    			-187,
    			104
    		],
    		[
    			16,
    			107
    		],
    		[
    			-113,
    			108
    		],
    		[
    			-174,
    			387
    		],
    		[
    			60,
    			159
    		],
    		[
    			-27,
    			277
    		],
    		[
    			-272,
    			141
    		],
    		[
    			-146,
    			-70
    		],
    		[
    			-39,
    			146
    		],
    		[
    			-293,
    			149
    		]
    	],
    	[
    		[
    			57826,
    			83766
    		],
    		[
    			-89,
    			348
    		],
    		[
    			-24,
    			287
    		],
    		[
    			-134,
    			136
    		]
    	],
    	[
    		[
    			57579,
    			84537
    		],
    		[
    			120,
    			187
    		],
    		[
    			-83,
    			551
    		],
    		[
    			198,
    			341
    		],
    		[
    			-42,
    			103
    		]
    	],
    	[
    		[
    			57772,
    			85719
    		],
    		[
    			316,
    			327
    		],
    		[
    			-291,
    			280
    		]
    	],
    	[
    		[
    			57797,
    			86326
    		],
    		[
    			594,
    			755
    		],
    		[
    			258,
    			341
    		],
    		[
    			105,
    			301
    		],
    		[
    			-411,
    			405
    		],
    		[
    			113,
    			385
    		],
    		[
    			-250,
    			440
    		],
    		[
    			187,
    			506
    		],
    		[
    			-323,
    			673
    		],
    		[
    			256,
    			445
    		],
    		[
    			-425,
    			394
    		],
    		[
    			41,
    			414
    		]
    	],
    	[
    		[
    			57942,
    			91385
    		],
    		[
    			224,
    			54
    		],
    		[
    			473,
    			237
    		]
    	],
    	[
    		[
    			58639,
    			91676
    		],
    		[
    			286,
    			206
    		],
    		[
    			456,
    			-358
    		],
    		[
    			761,
    			-140
    		],
    		[
    			1050,
    			-668
    		],
    		[
    			213,
    			-281
    		],
    		[
    			18,
    			-393
    		],
    		[
    			-308,
    			-311
    		],
    		[
    			-454,
    			-157
    		],
    		[
    			-1240,
    			449
    		],
    		[
    			-204,
    			-75
    		],
    		[
    			453,
    			-433
    		],
    		[
    			18,
    			-274
    		],
    		[
    			18,
    			-604
    		],
    		[
    			358,
    			-180
    		],
    		[
    			217,
    			-153
    		],
    		[
    			36,
    			286
    		],
    		[
    			-168,
    			254
    		],
    		[
    			177,
    			224
    		],
    		[
    			672,
    			-368
    		],
    		[
    			233,
    			144
    		],
    		[
    			-186,
    			433
    		],
    		[
    			647,
    			578
    		],
    		[
    			256,
    			-34
    		],
    		[
    			260,
    			-206
    		],
    		[
    			161,
    			406
    		],
    		[
    			-231,
    			352
    		],
    		[
    			136,
    			353
    		],
    		[
    			-204,
    			367
    		],
    		[
    			777,
    			-190
    		],
    		[
    			158,
    			-331
    		],
    		[
    			-351,
    			-73
    		],
    		[
    			1,
    			-328
    		],
    		[
    			219,
    			-203
    		],
    		[
    			429,
    			128
    		],
    		[
    			68,
    			377
    		],
    		[
    			580,
    			282
    		],
    		[
    			970,
    			507
    		],
    		[
    			209,
    			-29
    		],
    		[
    			-273,
    			-359
    		],
    		[
    			344,
    			-61
    		],
    		[
    			199,
    			202
    		],
    		[
    			521,
    			16
    		],
    		[
    			412,
    			245
    		],
    		[
    			317,
    			-356
    		],
    		[
    			315,
    			391
    		],
    		[
    			-291,
    			343
    		],
    		[
    			145,
    			195
    		],
    		[
    			820,
    			-179
    		],
    		[
    			385,
    			-185
    		],
    		[
    			1006,
    			-675
    		],
    		[
    			186,
    			309
    		],
    		[
    			-282,
    			313
    		],
    		[
    			-8,
    			125
    		],
    		[
    			-335,
    			58
    		],
    		[
    			92,
    			280
    		],
    		[
    			-149,
    			461
    		],
    		[
    			-8,
    			189
    		],
    		[
    			512,
    			535
    		],
    		[
    			183,
    			537
    		],
    		[
    			206,
    			116
    		],
    		[
    			736,
    			-156
    		],
    		[
    			57,
    			-328
    		],
    		[
    			-263,
    			-479
    		],
    		[
    			173,
    			-189
    		],
    		[
    			89,
    			-413
    		],
    		[
    			-63,
    			-809
    		],
    		[
    			307,
    			-362
    		],
    		[
    			-120,
    			-395
    		],
    		[
    			-544,
    			-839
    		],
    		[
    			318,
    			-87
    		],
    		[
    			110,
    			213
    		],
    		[
    			306,
    			151
    		],
    		[
    			74,
    			293
    		],
    		[
    			240,
    			281
    		],
    		[
    			-162,
    			336
    		],
    		[
    			130,
    			390
    		],
    		[
    			-304,
    			49
    		],
    		[
    			-67,
    			328
    		],
    		[
    			222,
    			593
    		],
    		[
    			-361,
    			482
    		],
    		[
    			497,
    			398
    		],
    		[
    			-64,
    			421
    		],
    		[
    			139,
    			13
    		],
    		[
    			145,
    			-328
    		],
    		[
    			-109,
    			-570
    		],
    		[
    			297,
    			-108
    		],
    		[
    			-127,
    			426
    		],
    		[
    			465,
    			233
    		],
    		[
    			577,
    			31
    		],
    		[
    			513,
    			-337
    		],
    		[
    			-247,
    			492
    		],
    		[
    			-28,
    			630
    		],
    		[
    			483,
    			119
    		],
    		[
    			669,
    			-26
    		],
    		[
    			602,
    			77
    		],
    		[
    			-226,
    			309
    		],
    		[
    			321,
    			388
    		],
    		[
    			319,
    			16
    		],
    		[
    			540,
    			293
    		],
    		[
    			734,
    			79
    		],
    		[
    			93,
    			162
    		],
    		[
    			729,
    			55
    		],
    		[
    			227,
    			-133
    		],
    		[
    			624,
    			314
    		],
    		[
    			510,
    			-10
    		],
    		[
    			77,
    			255
    		],
    		[
    			265,
    			252
    		],
    		[
    			656,
    			242
    		],
    		[
    			476,
    			-191
    		],
    		[
    			-378,
    			-146
    		],
    		[
    			629,
    			-90
    		],
    		[
    			75,
    			-292
    		],
    		[
    			254,
    			143
    		],
    		[
    			812,
    			-7
    		],
    		[
    			626,
    			-289
    		],
    		[
    			223,
    			-221
    		],
    		[
    			-69,
    			-307
    		],
    		[
    			-307,
    			-175
    		],
    		[
    			-730,
    			-328
    		],
    		[
    			-209,
    			-175
    		],
    		[
    			345,
    			-83
    		],
    		[
    			410,
    			-149
    		],
    		[
    			251,
    			112
    		],
    		[
    			141,
    			-379
    		],
    		[
    			122,
    			153
    		],
    		[
    			444,
    			93
    		],
    		[
    			892,
    			-97
    		],
    		[
    			67,
    			-276
    		],
    		[
    			1162,
    			-88
    		],
    		[
    			15,
    			451
    		],
    		[
    			590,
    			-104
    		],
    		[
    			443,
    			4
    		],
    		[
    			449,
    			-312
    		],
    		[
    			128,
    			-378
    		],
    		[
    			-165,
    			-247
    		],
    		[
    			349,
    			-465
    		],
    		[
    			437,
    			-240
    		],
    		[
    			268,
    			620
    		],
    		[
    			446,
    			-266
    		],
    		[
    			473,
    			159
    		],
    		[
    			538,
    			-182
    		],
    		[
    			204,
    			166
    		],
    		[
    			455,
    			-83
    		],
    		[
    			-201,
    			549
    		],
    		[
    			367,
    			256
    		],
    		[
    			2509,
    			-384
    		],
    		[
    			236,
    			-351
    		],
    		[
    			727,
    			-451
    		],
    		[
    			1122,
    			112
    		],
    		[
    			553,
    			-98
    		],
    		[
    			231,
    			-244
    		],
    		[
    			-33,
    			-432
    		],
    		[
    			342,
    			-168
    		],
    		[
    			372,
    			121
    		],
    		[
    			492,
    			15
    		],
    		[
    			525,
    			-116
    		],
    		[
    			526,
    			66
    		],
    		[
    			484,
    			-526
    		],
    		[
    			344,
    			189
    		],
    		[
    			-224,
    			378
    		],
    		[
    			123,
    			262
    		],
    		[
    			886,
    			-165
    		],
    		[
    			578,
    			36
    		],
    		[
    			799,
    			-282
    		],
    		[
    			-99610,
    			-258
    		],
    		[
    			681,
    			-451
    		],
    		[
    			728,
    			-588
    		],
    		[
    			-24,
    			-367
    		],
    		[
    			187,
    			-147
    		],
    		[
    			-64,
    			429
    		],
    		[
    			754,
    			-88
    		],
    		[
    			544,
    			-553
    		],
    		[
    			-276,
    			-257
    		],
    		[
    			-455,
    			-61
    		],
    		[
    			-7,
    			-578
    		],
    		[
    			-111,
    			-122
    		],
    		[
    			-260,
    			17
    		],
    		[
    			-212,
    			206
    		],
    		[
    			-369,
    			172
    		],
    		[
    			-62,
    			257
    		],
    		[
    			-283,
    			96
    		],
    		[
    			-315,
    			-76
    		],
    		[
    			-151,
    			207
    		],
    		[
    			60,
    			219
    		],
    		[
    			-333,
    			-140
    		],
    		[
    			126,
    			-278
    		],
    		[
    			-158,
    			-251
    		],
    		[
    			99997,
    			-3
    		],
    		[
    			-357,
    			-260
    		],
    		[
    			-360,
    			44
    		],
    		[
    			250,
    			-315
    		],
    		[
    			166,
    			-487
    		],
    		[
    			128,
    			-159
    		],
    		[
    			32,
    			-244
    		],
    		[
    			-71,
    			-157
    		],
    		[
    			-518,
    			129
    		],
    		[
    			-777,
    			-445
    		],
    		[
    			-247,
    			-69
    		],
    		[
    			-425,
    			-415
    		],
    		[
    			-403,
    			-362
    		],
    		[
    			-102,
    			-269
    		],
    		[
    			-397,
    			409
    		],
    		[
    			-724,
    			-464
    		],
    		[
    			-126,
    			219
    		],
    		[
    			-268,
    			-253
    		],
    		[
    			-371,
    			81
    		],
    		[
    			-90,
    			-388
    		],
    		[
    			-333,
    			-572
    		],
    		[
    			10,
    			-239
    		],
    		[
    			316,
    			-132
    		],
    		[
    			-37,
    			-860
    		],
    		[
    			-258,
    			-22
    		],
    		[
    			-119,
    			-494
    		],
    		[
    			116,
    			-255
    		],
    		[
    			-486,
    			-302
    		],
    		[
    			-96,
    			-674
    		],
    		[
    			-415,
    			-144
    		],
    		[
    			-83,
    			-600
    		],
    		[
    			-400,
    			-551
    		],
    		[
    			-103,
    			407
    		],
    		[
    			-119,
    			862
    		],
    		[
    			-155,
    			1313
    		],
    		[
    			134,
    			819
    		],
    		[
    			234,
    			353
    		],
    		[
    			14,
    			276
    		],
    		[
    			432,
    			132
    		],
    		[
    			496,
    			744
    		],
    		[
    			479,
    			608
    		],
    		[
    			499,
    			471
    		],
    		[
    			223,
    			833
    		],
    		[
    			-337,
    			-50
    		],
    		[
    			-167,
    			-487
    		],
    		[
    			-705,
    			-649
    		],
    		[
    			-227,
    			727
    		],
    		[
    			-717,
    			-201
    		],
    		[
    			-696,
    			-990
    		],
    		[
    			230,
    			-362
    		],
    		[
    			-620,
    			-154
    		],
    		[
    			-430,
    			-61
    		],
    		[
    			20,
    			427
    		],
    		[
    			-431,
    			90
    		],
    		[
    			-344,
    			-291
    		],
    		[
    			-850,
    			102
    		],
    		[
    			-914,
    			-175
    		],
    		[
    			-899,
    			-1153
    		],
    		[
    			-1065,
    			-1394
    		],
    		[
    			438,
    			-74
    		],
    		[
    			136,
    			-370
    		],
    		[
    			270,
    			-132
    		],
    		[
    			178,
    			295
    		],
    		[
    			305,
    			-38
    		],
    		[
    			401,
    			-650
    		],
    		[
    			9,
    			-503
    		],
    		[
    			-217,
    			-590
    		],
    		[
    			-23,
    			-705
    		],
    		[
    			-126,
    			-945
    		],
    		[
    			-418,
    			-855
    		],
    		[
    			-94,
    			-409
    		],
    		[
    			-377,
    			-688
    		],
    		[
    			-374,
    			-682
    		],
    		[
    			-179,
    			-349
    		],
    		[
    			-370,
    			-346
    		],
    		[
    			-175,
    			-8
    		],
    		[
    			-175,
    			287
    		],
    		[
    			-373,
    			-432
    		],
    		[
    			-43,
    			-197
    		]
    	],
    	[
    		[
    			0,
    			92833
    		],
    		[
    			36,
    			24
    		],
    		[
    			235,
    			-1
    		],
    		[
    			402,
    			-169
    		],
    		[
    			-24,
    			-81
    		],
    		[
    			-286,
    			-141
    		],
    		[
    			-363,
    			-36
    		],
    		[
    			99694,
    			-30
    		],
    		[
    			-49,
    			187
    		],
    		[
    			-99645,
    			247
    		]
    	],
    	[
    		[
    			59287,
    			77741
    		],
    		[
    			73,
    			146
    		],
    		[
    			198,
    			-127
    		],
    		[
    			89,
    			-23
    		],
    		[
    			36,
    			-117
    		],
    		[
    			42,
    			-18
    		]
    	],
    	[
    		[
    			59725,
    			77602
    		],
    		[
    			2,
    			-51
    		],
    		[
    			136,
    			-142
    		],
    		[
    			284,
    			35
    		],
    		[
    			-55,
    			-210
    		],
    		[
    			-304,
    			-103
    		],
    		[
    			-377,
    			-342
    		],
    		[
    			-154,
    			121
    		],
    		[
    			61,
    			277
    		],
    		[
    			-304,
    			173
    		],
    		[
    			50,
    			113
    		],
    		[
    			265,
    			197
    		],
    		[
    			-42,
    			71
    		]
    	],
    	[
    		[
    			28061,
    			66408
    		],
    		[
    			130,
    			47
    		],
    		[
    			184,
    			-18
    		],
    		[
    			8,
    			-153
    		],
    		[
    			-303,
    			-95
    		],
    		[
    			-19,
    			219
    		]
    	],
    	[
    		[
    			28391,
    			66555
    		],
    		[
    			220,
    			-265
    		],
    		[
    			-48,
    			-420
    		],
    		[
    			-51,
    			75
    		],
    		[
    			4,
    			309
    		],
    		[
    			-124,
    			234
    		],
    		[
    			-1,
    			67
    		]
    	],
    	[
    		[
    			28280,
    			65474
    		],
    		[
    			84,
    			-23
    		],
    		[
    			97,
    			-491
    		],
    		[
    			1,
    			-343
    		],
    		[
    			-68,
    			-29
    		],
    		[
    			-70,
    			340
    		],
    		[
    			-104,
    			171
    		],
    		[
    			60,
    			375
    		]
    	],
    	[
    		[
    			33000,
    			19946
    		],
    		[
    			333,
    			354
    		],
    		[
    			236,
    			-148
    		],
    		[
    			167,
    			237
    		],
    		[
    			222,
    			-266
    		],
    		[
    			-83,
    			-207
    		],
    		[
    			-375,
    			-177
    		],
    		[
    			-125,
    			207
    		],
    		[
    			-236,
    			-266
    		],
    		[
    			-139,
    			266
    		]
    	],
    	[
    		[
    			54206,
    			97653
    		],
    		[
    			105,
    			202
    		],
    		[
    			408,
    			20
    		],
    		[
    			350,
    			-206
    		],
    		[
    			915,
    			-440
    		],
    		[
    			-699,
    			-233
    		],
    		[
    			-155,
    			-435
    		],
    		[
    			-243,
    			-111
    		],
    		[
    			-132,
    			-490
    		],
    		[
    			-335,
    			-23
    		],
    		[
    			-598,
    			361
    		],
    		[
    			252,
    			210
    		],
    		[
    			-416,
    			170
    		],
    		[
    			-541,
    			499
    		],
    		[
    			-216,
    			463
    		],
    		[
    			757,
    			212
    		],
    		[
    			152,
    			-207
    		],
    		[
    			396,
    			8
    		]
    	],
    	[
    		[
    			57942,
    			91385
    		],
    		[
    			117,
    			414
    		],
    		[
    			-356,
    			235
    		],
    		[
    			-431,
    			-200
    		],
    		[
    			-137,
    			-433
    		],
    		[
    			-265,
    			-262
    		],
    		[
    			-298,
    			143
    		],
    		[
    			-362,
    			-29
    		],
    		[
    			-309,
    			312
    		],
    		[
    			-167,
    			-156
    		]
    	],
    	[
    		[
    			55734,
    			91409
    		],
    		[
    			-172,
    			-24
    		],
    		[
    			-41,
    			-389
    		],
    		[
    			-523,
    			95
    		],
    		[
    			-74,
    			-329
    		],
    		[
    			-267,
    			2
    		],
    		[
    			-183,
    			-421
    		],
    		[
    			-278,
    			-655
    		],
    		[
    			-431,
    			-831
    		],
    		[
    			101,
    			-202
    		],
    		[
    			-97,
    			-234
    		],
    		[
    			-275,
    			10
    		],
    		[
    			-180,
    			-554
    		],
    		[
    			17,
    			-784
    		],
    		[
    			177,
    			-300
    		],
    		[
    			-92,
    			-694
    		],
    		[
    			-231,
    			-405
    		],
    		[
    			-122,
    			-341
    		]
    	],
    	[
    		[
    			53063,
    			85353
    		],
    		[
    			-187,
    			363
    		],
    		[
    			-548,
    			-684
    		],
    		[
    			-371,
    			-138
    		],
    		[
    			-384,
    			301
    		],
    		[
    			-99,
    			635
    		],
    		[
    			-88,
    			1363
    		],
    		[
    			256,
    			381
    		],
    		[
    			733,
    			496
    		],
    		[
    			549,
    			609
    		],
    		[
    			508,
    			824
    		],
    		[
    			668,
    			1141
    		],
    		[
    			465,
    			444
    		],
    		[
    			763,
    			741
    		],
    		[
    			610,
    			259
    		],
    		[
    			457,
    			-31
    		],
    		[
    			423,
    			489
    		],
    		[
    			506,
    			-26
    		],
    		[
    			499,
    			118
    		],
    		[
    			869,
    			-433
    		],
    		[
    			-358,
    			-158
    		],
    		[
    			305,
    			-371
    		]
    	],
    	[
    		[
    			57613,
    			97879
    		],
    		[
    			-412,
    			-318
    		],
    		[
    			-806,
    			-70
    		],
    		[
    			-819,
    			98
    		],
    		[
    			-50,
    			163
    		],
    		[
    			-398,
    			11
    		],
    		[
    			-304,
    			271
    		],
    		[
    			858,
    			165
    		],
    		[
    			403,
    			-142
    		],
    		[
    			281,
    			177
    		],
    		[
    			702,
    			-148
    		],
    		[
    			545,
    			-207
    		]
    	],
    	[
    		[
    			56867,
    			96577
    		],
    		[
    			-620,
    			-241
    		],
    		[
    			-490,
    			137
    		],
    		[
    			191,
    			152
    		],
    		[
    			-167,
    			189
    		],
    		[
    			575,
    			119
    		],
    		[
    			110,
    			-222
    		],
    		[
    			401,
    			-134
    		]
    	],
    	[
    		[
    			37010,
    			99398
    		],
    		[
    			932,
    			353
    		],
    		[
    			975,
    			-27
    		],
    		[
    			354,
    			218
    		],
    		[
    			982,
    			57
    		],
    		[
    			2219,
    			-74
    		],
    		[
    			1737,
    			-469
    		],
    		[
    			-513,
    			-227
    		],
    		[
    			-1062,
    			-26
    		],
    		[
    			-1496,
    			-58
    		],
    		[
    			140,
    			-105
    		],
    		[
    			984,
    			65
    		],
    		[
    			836,
    			-204
    		],
    		[
    			540,
    			181
    		],
    		[
    			231,
    			-212
    		],
    		[
    			-305,
    			-344
    		],
    		[
    			707,
    			220
    		],
    		[
    			1348,
    			229
    		],
    		[
    			833,
    			-114
    		],
    		[
    			156,
    			-253
    		],
    		[
    			-1132,
    			-420
    		],
    		[
    			-157,
    			-136
    		],
    		[
    			-888,
    			-102
    		],
    		[
    			643,
    			-28
    		],
    		[
    			-324,
    			-431
    		],
    		[
    			-224,
    			-383
    		],
    		[
    			9,
    			-658
    		],
    		[
    			333,
    			-386
    		],
    		[
    			-434,
    			-24
    		],
    		[
    			-457,
    			-187
    		],
    		[
    			513,
    			-313
    		],
    		[
    			65,
    			-502
    		],
    		[
    			-297,
    			-55
    		],
    		[
    			360,
    			-508
    		],
    		[
    			-617,
    			-42
    		],
    		[
    			322,
    			-241
    		],
    		[
    			-91,
    			-208
    		],
    		[
    			-391,
    			-91
    		],
    		[
    			-388,
    			-2
    		],
    		[
    			348,
    			-400
    		],
    		[
    			4,
    			-263
    		],
    		[
    			-549,
    			244
    		],
    		[
    			-143,
    			-158
    		],
    		[
    			375,
    			-148
    		],
    		[
    			364,
    			-361
    		],
    		[
    			105,
    			-476
    		],
    		[
    			-495,
    			-114
    		],
    		[
    			-214,
    			228
    		],
    		[
    			-344,
    			340
    		],
    		[
    			95,
    			-401
    		],
    		[
    			-322,
    			-311
    		],
    		[
    			732,
    			-25
    		],
    		[
    			383,
    			-32
    		],
    		[
    			-745,
    			-515
    		],
    		[
    			-755,
    			-466
    		],
    		[
    			-813,
    			-204
    		],
    		[
    			-306,
    			-2
    		],
    		[
    			-288,
    			-228
    		],
    		[
    			-386,
    			-624
    		],
    		[
    			-597,
    			-414
    		],
    		[
    			-192,
    			-24
    		],
    		[
    			-370,
    			-145
    		],
    		[
    			-399,
    			-138
    		],
    		[
    			-238,
    			-365
    		],
    		[
    			-4,
    			-415
    		],
    		[
    			-141,
    			-388
    		],
    		[
    			-453,
    			-472
    		],
    		[
    			112,
    			-462
    		],
    		[
    			-125,
    			-488
    		],
    		[
    			-142,
    			-577
    		],
    		[
    			-391,
    			-36
    		],
    		[
    			-410,
    			482
    		],
    		[
    			-556,
    			3
    		],
    		[
    			-269,
    			324
    		],
    		[
    			-186,
    			577
    		],
    		[
    			-481,
    			735
    		],
    		[
    			-141,
    			385
    		],
    		[
    			-38,
    			530
    		],
    		[
    			-384,
    			546
    		],
    		[
    			100,
    			435
    		],
    		[
    			-186,
    			208
    		],
    		[
    			275,
    			691
    		],
    		[
    			418,
    			220
    		],
    		[
    			110,
    			247
    		],
    		[
    			58,
    			461
    		],
    		[
    			-318,
    			-209
    		],
    		[
    			-151,
    			-88
    		],
    		[
    			-249,
    			-84
    		],
    		[
    			-341,
    			193
    		],
    		[
    			-19,
    			401
    		],
    		[
    			109,
    			314
    		],
    		[
    			258,
    			9
    		],
    		[
    			567,
    			-157
    		],
    		[
    			-478,
    			375
    		],
    		[
    			-249,
    			202
    		],
    		[
    			-276,
    			-83
    		],
    		[
    			-232,
    			147
    		],
    		[
    			310,
    			550
    		],
    		[
    			-169,
    			220
    		],
    		[
    			-220,
    			409
    		],
    		[
    			-335,
    			626
    		],
    		[
    			-353,
    			230
    		],
    		[
    			3,
    			247
    		],
    		[
    			-745,
    			346
    		],
    		[
    			-590,
    			43
    		],
    		[
    			-743,
    			-24
    		],
    		[
    			-677,
    			-44
    		],
    		[
    			-323,
    			188
    		],
    		[
    			-482,
    			372
    		],
    		[
    			729,
    			186
    		],
    		[
    			559,
    			31
    		],
    		[
    			-1188,
    			154
    		],
    		[
    			-627,
    			241
    		],
    		[
    			39,
    			229
    		],
    		[
    			1051,
    			285
    		],
    		[
    			1018,
    			284
    		],
    		[
    			107,
    			214
    		],
    		[
    			-750,
    			213
    		],
    		[
    			243,
    			235
    		],
    		[
    			961,
    			413
    		],
    		[
    			404,
    			63
    		],
    		[
    			-115,
    			265
    		],
    		[
    			658,
    			156
    		],
    		[
    			854,
    			93
    		],
    		[
    			853,
    			5
    		],
    		[
    			303,
    			-184
    		],
    		[
    			737,
    			325
    		],
    		[
    			663,
    			-221
    		],
    		[
    			390,
    			-46
    		],
    		[
    			577,
    			-192
    		],
    		[
    			-660,
    			318
    		],
    		[
    			38,
    			253
    		]
    	],
    	[
    		[
    			69148,
    			21851
    		],
    		[
    			179,
    			-186
    		],
    		[
    			263,
    			-74
    		],
    		[
    			9,
    			-112
    		],
    		[
    			-77,
    			-269
    		],
    		[
    			-427,
    			-38
    		],
    		[
    			-7,
    			314
    		],
    		[
    			41,
    			244
    		],
    		[
    			19,
    			121
    		]
    	],
    	[
    		[
    			84713,
    			45326
    		],
    		[
    			32,
    			139
    		],
    		[
    			239,
    			133
    		],
    		[
    			194,
    			20
    		],
    		[
    			87,
    			74
    		],
    		[
    			105,
    			-74
    		],
    		[
    			-102,
    			-160
    		],
    		[
    			-289,
    			-258
    		],
    		[
    			-233,
    			-170
    		]
    	],
    	[
    		[
    			54540,
    			33696
    		],
    		[
    			133,
    			292
    		],
    		[
    			109,
    			-162
    		],
    		[
    			47,
    			-252
    		],
    		[
    			125,
    			-43
    		],
    		[
    			175,
    			-112
    		],
    		[
    			149,
    			43
    		],
    		[
    			248,
    			302
    		],
    		[
    			0,
    			2182
    		]
    	],
    	[
    		[
    			55526,
    			35946
    		],
    		[
    			75,
    			-88
    		],
    		[
    			165,
    			-562
    		],
    		[
    			-26,
    			-360
    		],
    		[
    			62,
    			-207
    		],
    		[
    			199,
    			60
    		],
    		[
    			139,
    			264
    		],
    		[
    			132,
    			177
    		],
    		[
    			68,
    			283
    		],
    		[
    			135,
    			137
    		],
    		[
    			117,
    			-71
    		],
    		[
    			133,
    			-166
    		],
    		[
    			226,
    			-29
    		],
    		[
    			178,
    			138
    		],
    		[
    			28,
    			184
    		],
    		[
    			48,
    			283
    		],
    		[
    			152,
    			47
    		],
    		[
    			83,
    			222
    		],
    		[
    			93,
    			393
    		],
    		[
    			249,
    			442
    		],
    		[
    			393,
    			435
    		]
    	],
    	[
    		[
    			58175,
    			37528
    		],
    		[
    			113,
    			-7
    		],
    		[
    			134,
    			-100
    		],
    		[
    			94,
    			71
    		],
    		[
    			148,
    			-59
    		]
    	],
    	[
    		[
    			58664,
    			37433
    		],
    		[
    			133,
    			-832
    		],
    		[
    			72,
    			-419
    		],
    		[
    			-49,
    			-659
    		],
    		[
    			23,
    			-212
    		]
    	],
    	[
    		[
    			58843,
    			35311
    		],
    		[
    			-140,
    			108
    		],
    		[
    			-80,
    			-42
    		],
    		[
    			-26,
    			-172
    		],
    		[
    			-76,
    			-222
    		],
    		[
    			2,
    			-204
    		],
    		[
    			166,
    			-320
    		],
    		[
    			163,
    			63
    		],
    		[
    			56,
    			263
    		]
    	],
    	[
    		[
    			58908,
    			34785
    		],
    		[
    			211,
    			-5
    		]
    	],
    	[
    		[
    			59119,
    			34780
    		],
    		[
    			-70,
    			-430
    		],
    		[
    			-32,
    			-491
    		],
    		[
    			-72,
    			-267
    		],
    		[
    			-190,
    			-298
    		],
    		[
    			-54,
    			-86
    		],
    		[
    			-118,
    			-300
    		],
    		[
    			-77,
    			-303
    		],
    		[
    			-158,
    			-424
    		],
    		[
    			-314,
    			-609
    		],
    		[
    			-196,
    			-355
    		],
    		[
    			-210,
    			-269
    		],
    		[
    			-290,
    			-229
    		],
    		[
    			-141,
    			-31
    		],
    		[
    			-36,
    			-164
    		],
    		[
    			-169,
    			88
    		],
    		[
    			-138,
    			-113
    		],
    		[
    			-301,
    			114
    		],
    		[
    			-168,
    			-72
    		],
    		[
    			-115,
    			31
    		],
    		[
    			-286,
    			-233
    		],
    		[
    			-238,
    			-94
    		],
    		[
    			-171,
    			-223
    		],
    		[
    			-127,
    			-14
    		],
    		[
    			-117,
    			210
    		],
    		[
    			-94,
    			11
    		],
    		[
    			-120,
    			264
    		],
    		[
    			-13,
    			-82
    		],
    		[
    			-37,
    			159
    		],
    		[
    			2,
    			346
    		],
    		[
    			-90,
    			396
    		],
    		[
    			89,
    			108
    		],
    		[
    			-7,
    			453
    		],
    		[
    			-182,
    			553
    		],
    		[
    			-139,
    			501
    		],
    		[
    			-1,
    			1
    		],
    		[
    			-199,
    			768
    		]
    	],
    	[
    		[
    			58049,
    			33472
    		],
    		[
    			-121,
    			182
    		],
    		[
    			-130,
    			-120
    		],
    		[
    			-151,
    			-232
    		],
    		[
    			-148,
    			-374
    		],
    		[
    			209,
    			-454
    		],
    		[
    			99,
    			59
    		],
    		[
    			51,
    			188
    		],
    		[
    			155,
    			93
    		],
    		[
    			47,
    			192
    		],
    		[
    			85,
    			288
    		],
    		[
    			-96,
    			178
    		]
    	],
    	[
    		[
    			23016,
    			65864
    		],
    		[
    			-107,
    			-518
    		],
    		[
    			-49,
    			-426
    		],
    		[
    			-20,
    			-791
    		],
    		[
    			-27,
    			-289
    		],
    		[
    			48,
    			-322
    		],
    		[
    			86,
    			-288
    		],
    		[
    			56,
    			-458
    		],
    		[
    			184,
    			-440
    		],
    		[
    			65,
    			-337
    		],
    		[
    			109,
    			-291
    		],
    		[
    			295,
    			-157
    		],
    		[
    			114,
    			-247
    		],
    		[
    			244,
    			165
    		],
    		[
    			212,
    			60
    		],
    		[
    			208,
    			106
    		],
    		[
    			175,
    			101
    		],
    		[
    			176,
    			241
    		],
    		[
    			67,
    			345
    		],
    		[
    			22,
    			496
    		],
    		[
    			48,
    			173
    		],
    		[
    			188,
    			155
    		],
    		[
    			294,
    			137
    		],
    		[
    			246,
    			-21
    		],
    		[
    			169,
    			50
    		],
    		[
    			66,
    			-125
    		],
    		[
    			-9,
    			-285
    		],
    		[
    			-149,
    			-351
    		],
    		[
    			-66,
    			-360
    		],
    		[
    			51,
    			-103
    		],
    		[
    			-42,
    			-255
    		],
    		[
    			-69,
    			-461
    		],
    		[
    			-71,
    			152
    		],
    		[
    			-58,
    			-10
    		]
    	],
    	[
    		[
    			25472,
    			61510
    		],
    		[
    			-53,
    			-8
    		],
    		[
    			-99,
    			-357
    		],
    		[
    			-51,
    			70
    		],
    		[
    			-33,
    			-27
    		],
    		[
    			2,
    			-87
    		]
    	],
    	[
    		[
    			25238,
    			61101
    		],
    		[
    			-257,
    			7
    		],
    		[
    			-259,
    			-1
    		],
    		[
    			-1,
    			-333
    		],
    		[
    			-125,
    			-1
    		],
    		[
    			103,
    			-198
    		],
    		[
    			103,
    			-136
    		],
    		[
    			31,
    			-128
    		],
    		[
    			45,
    			-36
    		],
    		[
    			-7,
    			-201
    		],
    		[
    			-357,
    			-2
    		],
    		[
    			-133,
    			-481
    		],
    		[
    			39,
    			-111
    		],
    		[
    			-32,
    			-138
    		],
    		[
    			-7,
    			-172
    		]
    	],
    	[
    		[
    			24381,
    			59170
    		],
    		[
    			-314,
    			636
    		],
    		[
    			-144,
    			191
    		],
    		[
    			-226,
    			155
    		],
    		[
    			-156,
    			-43
    		],
    		[
    			-223,
    			-223
    		],
    		[
    			-140,
    			-58
    		],
    		[
    			-196,
    			156
    		],
    		[
    			-208,
    			112
    		],
    		[
    			-260,
    			271
    		],
    		[
    			-208,
    			83
    		],
    		[
    			-314,
    			275
    		],
    		[
    			-233,
    			282
    		],
    		[
    			-70,
    			158
    		],
    		[
    			-155,
    			35
    		],
    		[
    			-284,
    			187
    		],
    		[
    			-116,
    			270
    		],
    		[
    			-299,
    			335
    		],
    		[
    			-139,
    			373
    		],
    		[
    			-66,
    			288
    		],
    		[
    			93,
    			57
    		],
    		[
    			-29,
    			169
    		],
    		[
    			64,
    			153
    		],
    		[
    			1,
    			204
    		],
    		[
    			-93,
    			266
    		],
    		[
    			-25,
    			235
    		],
    		[
    			-94,
    			298
    		],
    		[
    			-244,
    			587
    		],
    		[
    			-280,
    			462
    		],
    		[
    			-135,
    			368
    		],
    		[
    			-238,
    			241
    		],
    		[
    			-51,
    			145
    		],
    		[
    			42,
    			365
    		],
    		[
    			-142,
    			138
    		],
    		[
    			-164,
    			287
    		],
    		[
    			-69,
    			412
    		],
    		[
    			-149,
    			48
    		],
    		[
    			-162,
    			311
    		],
    		[
    			-130,
    			288
    		],
    		[
    			-12,
    			184
    		],
    		[
    			-149,
    			446
    		],
    		[
    			-99,
    			452
    		],
    		[
    			5,
    			227
    		],
    		[
    			-201,
    			234
    		],
    		[
    			-93,
    			-25
    		],
    		[
    			-159,
    			163
    		],
    		[
    			-44,
    			-240
    		],
    		[
    			46,
    			-284
    		],
    		[
    			27,
    			-444
    		],
    		[
    			95,
    			-243
    		],
    		[
    			206,
    			-407
    		],
    		[
    			46,
    			-139
    		],
    		[
    			42,
    			-42
    		],
    		[
    			37,
    			-203
    		],
    		[
    			49,
    			8
    		],
    		[
    			56,
    			-381
    		],
    		[
    			85,
    			-150
    		],
    		[
    			59,
    			-210
    		],
    		[
    			174,
    			-300
    		],
    		[
    			92,
    			-550
    		],
    		[
    			83,
    			-259
    		],
    		[
    			77,
    			-277
    		],
    		[
    			15,
    			-311
    		],
    		[
    			134,
    			-20
    		],
    		[
    			112,
    			-268
    		],
    		[
    			100,
    			-264
    		],
    		[
    			-6,
    			-106
    		],
    		[
    			-117,
    			-217
    		],
    		[
    			-49,
    			3
    		],
    		[
    			-74,
    			359
    		],
    		[
    			-181,
    			337
    		],
    		[
    			-201,
    			286
    		],
    		[
    			-142,
    			150
    		],
    		[
    			9,
    			432
    		],
    		[
    			-42,
    			320
    		],
    		[
    			-132,
    			183
    		],
    		[
    			-191,
    			264
    		],
    		[
    			-37,
    			-76
    		],
    		[
    			-70,
    			154
    		],
    		[
    			-171,
    			143
    		],
    		[
    			-164,
    			343
    		],
    		[
    			20,
    			44
    		],
    		[
    			115,
    			-33
    		],
    		[
    			103,
    			221
    		],
    		[
    			10,
    			266
    		],
    		[
    			-214,
    			422
    		],
    		[
    			-163,
    			163
    		],
    		[
    			-102,
    			369
    		],
    		[
    			-103,
    			388
    		],
    		[
    			-129,
    			472
    		],
    		[
    			-113,
    			531
    		]
    	],
    	[
    		[
    			33993,
    			32727
    		],
    		[
    			180,
    			63
    		],
    		[
    			279,
    			-457
    		],
    		[
    			103,
    			18
    		],
    		[
    			286,
    			-379
    		],
    		[
    			218,
    			-327
    		],
    		[
    			160,
    			-402
    		],
    		[
    			-122,
    			-280
    		],
    		[
    			77,
    			-334
    		]
    	],
    	[
    		[
    			35174,
    			30629
    		],
    		[
    			-121,
    			-372
    		],
    		[
    			-313,
    			-328
    		],
    		[
    			-205,
    			118
    		],
    		[
    			-151,
    			-63
    		],
    		[
    			-256,
    			253
    		],
    		[
    			-189,
    			-19
    		],
    		[
    			-169,
    			327
    		]
    	],
    	[
    		[
    			34826,
    			35372
    		],
    		[
    			54,
    			341
    		],
    		[
    			38,
    			350
    		],
    		[
    			0,
    			325
    		],
    		[
    			-100,
    			107
    		],
    		[
    			-104,
    			-96
    		],
    		[
    			-103,
    			26
    		],
    		[
    			-33,
    			228
    		],
    		[
    			-26,
    			541
    		],
    		[
    			-52,
    			177
    		],
    		[
    			-187,
    			160
    		],
    		[
    			-114,
    			-116
    		],
    		[
    			-293,
    			113
    		],
    		[
    			18,
    			802
    		],
    		[
    			-82,
    			329
    		]
    	],
    	[
    		[
    			33842,
    			38659
    		],
    		[
    			87,
    			122
    		],
    		[
    			-27,
    			337
    		],
    		[
    			77,
    			259
    		],
    		[
    			49,
    			465
    		],
    		[
    			-66,
    			367
    		],
    		[
    			-151,
    			166
    		],
    		[
    			-30,
    			233
    		],
    		[
    			41,
    			342
    		],
    		[
    			-533,
    			24
    		],
    		[
    			-107,
    			688
    		],
    		[
    			81,
    			10
    		],
    		[
    			-3,
    			255
    		],
    		[
    			-55,
    			172
    		],
    		[
    			-12,
    			342
    		],
    		[
    			-161,
    			175
    		],
    		[
    			-175,
    			-6
    		],
    		[
    			-115,
    			172
    		],
    		[
    			-188,
    			117
    		],
    		[
    			-109,
    			220
    		],
    		[
    			-311,
    			98
    		],
    		[
    			-302,
    			529
    		],
    		[
    			23,
    			396
    		],
    		[
    			-34,
    			227
    		],
    		[
    			29,
    			443
    		],
    		[
    			-363,
    			-100
    		],
    		[
    			-147,
    			-222
    		],
    		[
    			-243,
    			-239
    		],
    		[
    			-62,
    			-179
    		],
    		[
    			-143,
    			-13
    		],
    		[
    			-206,
    			50
    		]
    	],
    	[
    		[
    			30686,
    			44109
    		],
    		[
    			-157,
    			-102
    		],
    		[
    			-126,
    			68
    		],
    		[
    			18,
    			898
    		],
    		[
    			-228,
    			-348
    		],
    		[
    			-245,
    			15
    		],
    		[
    			-105,
    			315
    		],
    		[
    			-184,
    			34
    		],
    		[
    			59,
    			254
    		],
    		[
    			-155,
    			359
    		],
    		[
    			-115,
    			532
    		],
    		[
    			73,
    			108
    		],
    		[
    			0,
    			250
    		],
    		[
    			168,
    			171
    		],
    		[
    			-28,
    			319
    		],
    		[
    			71,
    			206
    		],
    		[
    			20,
    			275
    		],
    		[
    			318,
    			402
    		],
    		[
    			227,
    			114
    		],
    		[
    			37,
    			89
    		],
    		[
    			251,
    			-28
    		]
    	],
    	[
    		[
    			30585,
    			48040
    		],
    		[
    			125,
    			1620
    		],
    		[
    			6,
    			256
    		],
    		[
    			-43,
    			339
    		],
    		[
    			-123,
    			215
    		],
    		[
    			1,
    			430
    		],
    		[
    			156,
    			97
    		],
    		[
    			56,
    			-61
    		],
    		[
    			9,
    			226
    		],
    		[
    			-162,
    			61
    		],
    		[
    			-4,
    			370
    		],
    		[
    			541,
    			-13
    		],
    		[
    			92,
    			203
    		],
    		[
    			77,
    			-187
    		],
    		[
    			55,
    			-349
    		],
    		[
    			52,
    			73
    		]
    	],
    	[
    		[
    			31423,
    			51320
    		],
    		[
    			153,
    			-312
    		],
    		[
    			216,
    			38
    		],
    		[
    			54,
    			181
    		],
    		[
    			206,
    			138
    		],
    		[
    			115,
    			97
    		],
    		[
    			32,
    			250
    		],
    		[
    			198,
    			168
    		],
    		[
    			-15,
    			124
    		],
    		[
    			-235,
    			51
    		],
    		[
    			-39,
    			372
    		],
    		[
    			12,
    			396
    		],
    		[
    			-125,
    			153
    		],
    		[
    			52,
    			55
    		],
    		[
    			206,
    			-76
    		],
    		[
    			221,
    			-148
    		],
    		[
    			80,
    			140
    		],
    		[
    			200,
    			92
    		],
    		[
    			310,
    			221
    		],
    		[
    			102,
    			225
    		],
    		[
    			-37,
    			167
    		]
    	],
    	[
    		[
    			33129,
    			53652
    		],
    		[
    			145,
    			26
    		],
    		[
    			64,
    			-136
    		],
    		[
    			-36,
    			-259
    		],
    		[
    			96,
    			-90
    		],
    		[
    			63,
    			-274
    		],
    		[
    			-77,
    			-209
    		],
    		[
    			-44,
    			-502
    		],
    		[
    			71,
    			-299
    		],
    		[
    			20,
    			-274
    		],
    		[
    			171,
    			-277
    		],
    		[
    			137,
    			-29
    		],
    		[
    			30,
    			116
    		],
    		[
    			88,
    			25
    		],
    		[
    			126,
    			104
    		],
    		[
    			90,
    			157
    		],
    		[
    			154,
    			-50
    		],
    		[
    			67,
    			21
    		]
    	],
    	[
    		[
    			34294,
    			51702
    		],
    		[
    			151,
    			-48
    		],
    		[
    			25,
    			120
    		],
    		[
    			-46,
    			118
    		],
    		[
    			28,
    			171
    		],
    		[
    			112,
    			-53
    		],
    		[
    			131,
    			61
    		],
    		[
    			159,
    			-125
    		]
    	],
    	[
    		[
    			34854,
    			51946
    		],
    		[
    			121,
    			-122
    		],
    		[
    			86,
    			160
    		],
    		[
    			62,
    			-25
    		],
    		[
    			38,
    			-166
    		],
    		[
    			133,
    			42
    		],
    		[
    			107,
    			224
    		],
    		[
    			85,
    			436
    		],
    		[
    			164,
    			540
    		]
    	],
    	[
    		[
    			35650,
    			53035
    		],
    		[
    			95,
    			28
    		],
    		[
    			69,
    			-327
    		],
    		[
    			155,
    			-1033
    		],
    		[
    			149,
    			-97
    		],
    		[
    			7,
    			-408
    		],
    		[
    			-208,
    			-487
    		],
    		[
    			86,
    			-178
    		],
    		[
    			491,
    			-92
    		],
    		[
    			10,
    			-593
    		],
    		[
    			211,
    			388
    		],
    		[
    			349,
    			-212
    		],
    		[
    			462,
    			-361
    		],
    		[
    			135,
    			-346
    		],
    		[
    			-45,
    			-327
    		],
    		[
    			323,
    			182
    		],
    		[
    			540,
    			-313
    		],
    		[
    			415,
    			23
    		],
    		[
    			411,
    			-489
    		],
    		[
    			355,
    			-662
    		],
    		[
    			214,
    			-170
    		],
    		[
    			237,
    			-24
    		],
    		[
    			101,
    			-186
    		],
    		[
    			94,
    			-752
    		],
    		[
    			46,
    			-358
    		],
    		[
    			-110,
    			-977
    		],
    		[
    			-142,
    			-385
    		],
    		[
    			-391,
    			-822
    		],
    		[
    			-177,
    			-668
    		],
    		[
    			-206,
    			-513
    		],
    		[
    			-69,
    			-11
    		],
    		[
    			-78,
    			-435
    		],
    		[
    			20,
    			-1107
    		],
    		[
    			-77,
    			-910
    		],
    		[
    			-30,
    			-390
    		],
    		[
    			-88,
    			-233
    		],
    		[
    			-49,
    			-790
    		],
    		[
    			-282,
    			-771
    		],
    		[
    			-47,
    			-610
    		],
    		[
    			-225,
    			-256
    		],
    		[
    			-65,
    			-355
    		],
    		[
    			-302,
    			2
    		],
    		[
    			-437,
    			-227
    		],
    		[
    			-195,
    			-263
    		],
    		[
    			-311,
    			-173
    		],
    		[
    			-327,
    			-470
    		],
    		[
    			-235,
    			-586
    		],
    		[
    			-41,
    			-441
    		],
    		[
    			46,
    			-326
    		],
    		[
    			-51,
    			-597
    		],
    		[
    			-63,
    			-289
    		],
    		[
    			-195,
    			-325
    		],
    		[
    			-308,
    			-1040
    		],
    		[
    			-244,
    			-468
    		],
    		[
    			-189,
    			-277
    		],
    		[
    			-127,
    			-562
    		],
    		[
    			-183,
    			-337
    		]
    	],
    	[
    		[
    			33842,
    			38659
    		],
    		[
    			-4,
    			182
    		],
    		[
    			-259,
    			302
    		],
    		[
    			-258,
    			9
    		],
    		[
    			-484,
    			-172
    		],
    		[
    			-133,
    			-520
    		],
    		[
    			-7,
    			-318
    		],
    		[
    			-110,
    			-708
    		]
    	],
    	[
    		[
    			30669,
    			40193
    		],
    		[
    			175,
    			638
    		],
    		[
    			-119,
    			496
    		],
    		[
    			63,
    			199
    		],
    		[
    			-49,
    			219
    		],
    		[
    			108,
    			295
    		],
    		[
    			6,
    			503
    		],
    		[
    			13,
    			415
    		],
    		[
    			60,
    			200
    		],
    		[
    			-240,
    			951
    		]
    	],
    	[
    		[
    			30452,
    			39739
    		],
    		[
    			-279,
    			340
    		],
    		[
    			-24,
    			242
    		],
    		[
    			-551,
    			593
    		],
    		[
    			-498,
    			646
    		],
    		[
    			-214,
    			365
    		],
    		[
    			-115,
    			488
    		],
    		[
    			46,
    			170
    		],
    		[
    			-236,
    			775
    		],
    		[
    			-274,
    			1090
    		],
    		[
    			-262,
    			1177
    		],
    		[
    			-114,
    			269
    		],
    		[
    			-87,
    			435
    		],
    		[
    			-216,
    			386
    		],
    		[
    			-198,
    			239
    		],
    		[
    			90,
    			264
    		],
    		[
    			-134,
    			563
    		],
    		[
    			86,
    			414
    		],
    		[
    			221,
    			373
    		]
    	],
    	[
    		[
    			27693,
    			48568
    		],
    		[
    			33,
    			-246
    		],
    		[
    			-79,
    			-141
    		],
    		[
    			8,
    			-216
    		],
    		[
    			114,
    			47
    		],
    		[
    			113,
    			-64
    		],
    		[
    			116,
    			-298
    		],
    		[
    			157,
    			243
    		],
    		[
    			53,
    			398
    		],
    		[
    			170,
    			514
    		],
    		[
    			334,
    			233
    		],
    		[
    			303,
    			619
    		],
    		[
    			86,
    			384
    		],
    		[
    			-38,
    			449
    		]
    	],
    	[
    		[
    			29063,
    			50490
    		],
    		[
    			74,
    			56
    		],
    		[
    			184,
    			-280
    		],
    		[
    			89,
    			-279
    		],
    		[
    			129,
    			-152
    		],
    		[
    			163,
    			-620
    		],
    		[
    			207,
    			-74
    		],
    		[
    			153,
    			157
    		],
    		[
    			101,
    			-103
    		],
    		[
    			166,
    			51
    		],
    		[
    			213,
    			-276
    		],
    		[
    			-179,
    			-602
    		],
    		[
    			83,
    			-14
    		],
    		[
    			139,
    			-314
    		]
    	],
    	[
    		[
    			29063,
    			50490
    		],
    		[
    			-119,
    			140
    		],
    		[
    			-137,
    			195
    		],
    		[
    			-79,
    			-94
    		],
    		[
    			-235,
    			82
    		],
    		[
    			-68,
    			255
    		],
    		[
    			-52,
    			-10
    		],
    		[
    			-278,
    			338
    		]
    	],
    	[
    		[
    			28095,
    			51396
    		],
    		[
    			-37,
    			183
    		],
    		[
    			103,
    			44
    		],
    		[
    			-12,
    			296
    		],
    		[
    			65,
    			214
    		],
    		[
    			138,
    			40
    		],
    		[
    			117,
    			371
    		],
    		[
    			106,
    			310
    		],
    		[
    			-102,
    			141
    		],
    		[
    			52,
    			343
    		],
    		[
    			-62,
    			540
    		],
    		[
    			59,
    			155
    		],
    		[
    			-44,
    			500
    		],
    		[
    			-112,
    			315
    		]
    	],
    	[
    		[
    			28366,
    			54848
    		],
    		[
    			36,
    			287
    		],
    		[
    			89,
    			-43
    		],
    		[
    			52,
    			176
    		],
    		[
    			-64,
    			348
    		],
    		[
    			34,
    			86
    		]
    	],
    	[
    		[
    			28513,
    			55702
    		],
    		[
    			143,
    			-18
    		],
    		[
    			209,
    			412
    		],
    		[
    			114,
    			63
    		],
    		[
    			3,
    			195
    		],
    		[
    			51,
    			500
    		],
    		[
    			159,
    			274
    		],
    		[
    			175,
    			11
    		],
    		[
    			22,
    			123
    		],
    		[
    			218,
    			-49
    		],
    		[
    			218,
    			298
    		],
    		[
    			109,
    			132
    		],
    		[
    			134,
    			285
    		],
    		[
    			98,
    			-36
    		],
    		[
    			73,
    			-156
    		],
    		[
    			-54,
    			-199
    		]
    	],
    	[
    		[
    			30185,
    			57537
    		],
    		[
    			-178,
    			-99
    		],
    		[
    			-71,
    			-295
    		],
    		[
    			-107,
    			-169
    		],
    		[
    			-81,
    			-220
    		],
    		[
    			-34,
    			-422
    		],
    		[
    			-77,
    			-345
    		],
    		[
    			144,
    			-40
    		],
    		[
    			35,
    			-271
    		],
    		[
    			62,
    			-130
    		],
    		[
    			21,
    			-238
    		],
    		[
    			-33,
    			-219
    		],
    		[
    			10,
    			-123
    		],
    		[
    			69,
    			-49
    		],
    		[
    			66,
    			-207
    		],
    		[
    			357,
    			57
    		],
    		[
    			161,
    			-75
    		],
    		[
    			196,
    			-508
    		],
    		[
    			112,
    			63
    		],
    		[
    			200,
    			-32
    		],
    		[
    			158,
    			68
    		],
    		[
    			99,
    			-102
    		],
    		[
    			-50,
    			-318
    		],
    		[
    			-62,
    			-199
    		],
    		[
    			-22,
    			-423
    		],
    		[
    			56,
    			-393
    		],
    		[
    			79,
    			-175
    		],
    		[
    			9,
    			-133
    		],
    		[
    			-140,
    			-294
    		],
    		[
    			100,
    			-130
    		],
    		[
    			74,
    			-207
    		],
    		[
    			85,
    			-589
    		]
    	],
    	[
    		[
    			28366,
    			54848
    		],
    		[
    			-93,
    			170
    		],
    		[
    			-59,
    			319
    		],
    		[
    			68,
    			158
    		],
    		[
    			-70,
    			40
    		],
    		[
    			-52,
    			196
    		],
    		[
    			-138,
    			164
    		],
    		[
    			-122,
    			-38
    		],
    		[
    			-56,
    			-205
    		],
    		[
    			-112,
    			-149
    		],
    		[
    			-61,
    			-20
    		],
    		[
    			-27,
    			-123
    		],
    		[
    			132,
    			-321
    		],
    		[
    			-75,
    			-76
    		],
    		[
    			-40,
    			-87
    		],
    		[
    			-130,
    			-30
    		],
    		[
    			-48,
    			353
    		],
    		[
    			-36,
    			-101
    		],
    		[
    			-92,
    			35
    		],
    		[
    			-56,
    			238
    		],
    		[
    			-114,
    			39
    		],
    		[
    			-72,
    			69
    		],
    		[
    			-119,
    			-1
    		],
    		[
    			-8,
    			-128
    		],
    		[
    			-32,
    			89
    		]
    	],
    	[
    		[
    			26954,
    			55439
    		],
    		[
    			14,
    			117
    		],
    		[
    			23,
    			120
    		],
    		[
    			-10,
    			107
    		],
    		[
    			41,
    			70
    		],
    		[
    			-58,
    			88
    		],
    		[
    			-1,
    			238
    		],
    		[
    			107,
    			53
    		]
    	],
    	[
    		[
    			27070,
    			56232
    		],
    		[
    			100,
    			-212
    		],
    		[
    			-6,
    			-126
    		],
    		[
    			111,
    			-26
    		],
    		[
    			26,
    			48
    		],
    		[
    			77,
    			-145
    		],
    		[
    			136,
    			42
    		],
    		[
    			119,
    			150
    		],
    		[
    			168,
    			119
    		],
    		[
    			95,
    			176
    		],
    		[
    			153,
    			-34
    		],
    		[
    			-10,
    			-58
    		],
    		[
    			155,
    			-21
    		],
    		[
    			124,
    			-102
    		],
    		[
    			90,
    			-177
    		],
    		[
    			105,
    			-164
    		]
    	],
    	[
    		[
    			26954,
    			55439
    		],
    		[
    			-151,
    			131
    		],
    		[
    			-56,
    			124
    		],
    		[
    			32,
    			103
    		],
    		[
    			-11,
    			130
    		],
    		[
    			-77,
    			142
    		],
    		[
    			-109,
    			116
    		],
    		[
    			-95,
    			76
    		],
    		[
    			-19,
    			173
    		],
    		[
    			-73,
    			105
    		],
    		[
    			18,
    			-172
    		],
    		[
    			-55,
    			-141
    		],
    		[
    			-64,
    			164
    		],
    		[
    			-89,
    			58
    		],
    		[
    			-38,
    			120
    		],
    		[
    			2,
    			179
    		],
    		[
    			36,
    			187
    		],
    		[
    			-78,
    			83
    		],
    		[
    			64,
    			114
    		]
    	],
    	[
    		[
    			26191,
    			57131
    		],
    		[
    			42,
    			76
    		],
    		[
    			183,
    			-156
    		],
    		[
    			63,
    			77
    		],
    		[
    			89,
    			-50
    		],
    		[
    			46,
    			-121
    		],
    		[
    			82,
    			-40
    		],
    		[
    			66,
    			126
    		]
    	],
    	[
    		[
    			26762,
    			57043
    		],
    		[
    			70,
    			-321
    		],
    		[
    			108,
    			-238
    		],
    		[
    			130,
    			-252
    		]
    	],
    	[
    		[
    			26191,
    			57131
    		],
    		[
    			-96,
    			186
    		],
    		[
    			-130,
    			238
    		],
    		[
    			-61,
    			200
    		],
    		[
    			-117,
    			185
    		],
    		[
    			-140,
    			267
    		],
    		[
    			31,
    			91
    		],
    		[
    			46,
    			-88
    		],
    		[
    			21,
    			41
    		]
    	],
    	[
    		[
    			25745,
    			58251
    		],
    		[
    			86,
    			25
    		],
    		[
    			35,
    			135
    		],
    		[
    			41,
    			5
    		],
    		[
    			-6,
    			290
    		],
    		[
    			65,
    			14
    		],
    		[
    			58,
    			-4
    		],
    		[
    			60,
    			158
    		],
    		[
    			82,
    			-120
    		],
    		[
    			29,
    			74
    		],
    		[
    			51,
    			70
    		],
    		[
    			97,
    			163
    		],
    		[
    			4,
    			121
    		],
    		[
    			27,
    			-5
    		],
    		[
    			36,
    			141
    		],
    		[
    			29,
    			17
    		],
    		[
    			47,
    			-90
    		],
    		[
    			56,
    			-27
    		],
    		[
    			61,
    			76
    		],
    		[
    			70,
    			0
    		],
    		[
    			97,
    			77
    		],
    		[
    			38,
    			81
    		],
    		[
    			95,
    			-12
    		]
    	],
    	[
    		[
    			26903,
    			59440
    		],
    		[
    			-24,
    			-57
    		],
    		[
    			-14,
    			-132
    		],
    		[
    			29,
    			-216
    		],
    		[
    			-64,
    			-202
    		],
    		[
    			-30,
    			-237
    		],
    		[
    			-9,
    			-261
    		],
    		[
    			15,
    			-152
    		],
    		[
    			7,
    			-266
    		],
    		[
    			-43,
    			-58
    		],
    		[
    			-26,
    			-253
    		],
    		[
    			19,
    			-156
    		],
    		[
    			-56,
    			-151
    		],
    		[
    			12,
    			-159
    		],
    		[
    			43,
    			-97
    		]
    	],
    	[
    		[
    			25745,
    			58251
    		],
    		[
    			-48,
    			185
    		],
    		[
    			-84,
    			51
    		]
    	],
    	[
    		[
    			25613,
    			58487
    		],
    		[
    			19,
    			237
    		],
    		[
    			-38,
    			64
    		],
    		[
    			-57,
    			42
    		],
    		[
    			-122,
    			-70
    		],
    		[
    			-10,
    			79
    		],
    		[
    			-84,
    			95
    		],
    		[
    			-60,
    			118
    		],
    		[
    			-82,
    			50
    		]
    	],
    	[
    		[
    			25179,
    			59102
    		],
    		[
    			58,
    			150
    		],
    		[
    			-22,
    			116
    		],
    		[
    			20,
    			113
    		],
    		[
    			131,
    			166
    		],
    		[
    			127,
    			225
    		]
    	],
    	[
    		[
    			25493,
    			59872
    		],
    		[
    			29,
    			-23
    		],
    		[
    			61,
    			104
    		],
    		[
    			79,
    			8
    		],
    		[
    			26,
    			-48
    		],
    		[
    			43,
    			29
    		],
    		[
    			129,
    			-53
    		],
    		[
    			128,
    			15
    		],
    		[
    			90,
    			66
    		],
    		[
    			32,
    			66
    		],
    		[
    			89,
    			-31
    		],
    		[
    			66,
    			-40
    		],
    		[
    			73,
    			14
    		],
    		[
    			55,
    			51
    		],
    		[
    			127,
    			-82
    		],
    		[
    			44,
    			-13
    		],
    		[
    			85,
    			-110
    		],
    		[
    			80,
    			-132
    		],
    		[
    			101,
    			-91
    		],
    		[
    			73,
    			-162
    		]
    	],
    	[
    		[
    			25613,
    			58487
    		],
    		[
    			-31,
    			-139
    		],
    		[
    			-161,
    			9
    		],
    		[
    			-100,
    			57
    		],
    		[
    			-115,
    			117
    		],
    		[
    			-154,
    			37
    		],
    		[
    			-79,
    			127
    		]
    	],
    	[
    		[
    			24973,
    			58695
    		],
    		[
    			9,
    			86
    		],
    		[
    			95,
    			149
    		],
    		[
    			52,
    			66
    		],
    		[
    			-15,
    			69
    		],
    		[
    			65,
    			37
    		]
    	],
    	[
    		[
    			25238,
    			61101
    		],
    		[
    			-2,
    			-468
    		],
    		[
    			-22,
    			-667
    		],
    		[
    			83,
    			0
    		]
    	],
    	[
    		[
    			25297,
    			59966
    		],
    		[
    			90,
    			-107
    		],
    		[
    			24,
    			88
    		],
    		[
    			82,
    			-75
    		]
    	],
    	[
    		[
    			24973,
    			58695
    		],
    		[
    			-142,
    			103
    		],
    		[
    			-174,
    			11
    		],
    		[
    			-127,
    			117
    		],
    		[
    			-149,
    			244
    		]
    	],
    	[
    		[
    			25472,
    			61510
    		],
    		[
    			1,
    			-87
    		],
    		[
    			53,
    			-3
    		],
    		[
    			-5,
    			-160
    		],
    		[
    			-45,
    			-256
    		],
    		[
    			24,
    			-91
    		],
    		[
    			-29,
    			-212
    		],
    		[
    			18,
    			-56
    		],
    		[
    			-32,
    			-299
    		],
    		[
    			-55,
    			-156
    		],
    		[
    			-50,
    			-19
    		],
    		[
    			-55,
    			-205
    		]
    	],
    	[
    		[
    			30185,
    			57537
    		],
    		[
    			-8,
    			-139
    		],
    		[
    			-163,
    			-69
    		],
    		[
    			91,
    			-268
    		],
    		[
    			-3,
    			-309
    		],
    		[
    			-123,
    			-344
    		],
    		[
    			105,
    			-468
    		],
    		[
    			120,
    			38
    		],
    		[
    			62,
    			427
    		],
    		[
    			-86,
    			208
    		],
    		[
    			-14,
    			447
    		],
    		[
    			346,
    			241
    		],
    		[
    			-38,
    			278
    		],
    		[
    			97,
    			186
    		],
    		[
    			100,
    			-415
    		],
    		[
    			195,
    			-9
    		],
    		[
    			180,
    			-330
    		],
    		[
    			11,
    			-195
    		],
    		[
    			249,
    			-6
    		],
    		[
    			297,
    			61
    		],
    		[
    			159,
    			-264
    		],
    		[
    			213,
    			-74
    		],
    		[
    			155,
    			185
    		],
    		[
    			4,
    			149
    		],
    		[
    			344,
    			35
    		],
    		[
    			333,
    			9
    		],
    		[
    			-236,
    			-175
    		],
    		[
    			95,
    			-279
    		],
    		[
    			222,
    			-44
    		],
    		[
    			210,
    			-291
    		],
    		[
    			45,
    			-473
    		],
    		[
    			144,
    			13
    		],
    		[
    			109,
    			-139
    		]
    	],
    	[
    		[
    			33400,
    			55523
    		],
    		[
    			-220,
    			-347
    		],
    		[
    			-24,
    			-215
    		],
    		[
    			95,
    			-220
    		],
    		[
    			-69,
    			-110
    		],
    		[
    			-171,
    			-95
    		],
    		[
    			5,
    			-273
    		],
    		[
    			-75,
    			-163
    		],
    		[
    			188,
    			-448
    		]
    	],
    	[
    		[
    			33400,
    			55523
    		],
    		[
    			183,
    			-217
    		],
    		[
    			171,
    			-385
    		],
    		[
    			8,
    			-304
    		],
    		[
    			105,
    			-14
    		],
    		[
    			149,
    			-289
    		],
    		[
    			109,
    			-205
    		]
    	],
    	[
    		[
    			34125,
    			54109
    		],
    		[
    			-44,
    			-532
    		],
    		[
    			-169,
    			-154
    		],
    		[
    			15,
    			-139
    		],
    		[
    			-51,
    			-305
    		],
    		[
    			123,
    			-429
    		],
    		[
    			89,
    			-1
    		],
    		[
    			37,
    			-333
    		],
    		[
    			169,
    			-514
    		]
    	],
    	[
    		[
    			34125,
    			54109
    		],
    		[
    			333,
    			-119
    		],
    		[
    			30,
    			107
    		],
    		[
    			225,
    			43
    		],
    		[
    			298,
    			-159
    		]
    	],
    	[
    		[
    			35011,
    			53981
    		],
    		[
    			-144,
    			-508
    		],
    		[
    			22,
    			-404
    		],
    		[
    			109,
    			-351
    		],
    		[
    			-49,
    			-254
    		],
    		[
    			-24,
    			-270
    		],
    		[
    			-71,
    			-248
    		]
    	],
    	[
    		[
    			35011,
    			53981
    		],
    		[
    			95,
    			-65
    		],
    		[
    			204,
    			-140
    		],
    		[
    			294,
    			-499
    		],
    		[
    			46,
    			-242
    		]
    	],
    	[
    		[
    			51718,
    			79804
    		],
    		[
    			131,
    			-155
    		],
    		[
    			400,
    			-109
    		],
    		[
    			-140,
    			-404
    		],
    		[
    			-35,
    			-421
    		]
    	],
    	[
    		[
    			52074,
    			78715
    		],
    		[
    			-77,
    			-101
    		],
    		[
    			-126,
    			54
    		],
    		[
    			9,
    			-150
    		],
    		[
    			-203,
    			-332
    		],
    		[
    			-5,
    			-267
    		],
    		[
    			133,
    			92
    		],
    		[
    			95,
    			-259
    		]
    	],
    	[
    		[
    			51900,
    			77752
    		],
    		[
    			-11,
    			-167
    		],
    		[
    			82,
    			-222
    		],
    		[
    			-97,
    			-180
    		],
    		[
    			72,
    			-457
    		],
    		[
    			151,
    			-75
    		],
    		[
    			-32,
    			-256
    		]
    	],
    	[
    		[
    			52065,
    			76395
    		],
    		[
    			-252,
    			-334
    		],
    		[
    			-548,
    			160
    		],
    		[
    			-404,
    			-192
    		],
    		[
    			-32,
    			-355
    		]
    	],
    	[
    		[
    			50829,
    			75674
    		],
    		[
    			-322,
    			-77
    		],
    		[
    			-313,
    			267
    		],
    		[
    			-101,
    			-127
    		],
    		[
    			-511,
    			268
    		],
    		[
    			-111,
    			230
    		]
    	],
    	[
    		[
    			49471,
    			76235
    		],
    		[
    			144,
    			354
    		],
    		[
    			53,
    			1177
    		],
    		[
    			-287,
    			620
    		],
    		[
    			-205,
    			299
    		],
    		[
    			-424,
    			227
    		],
    		[
    			-28,
    			431
    		],
    		[
    			360,
    			129
    		],
    		[
    			466,
    			-152
    		],
    		[
    			-88,
    			669
    		],
    		[
    			263,
    			-254
    		],
    		[
    			646,
    			461
    		],
    		[
    			84,
    			484
    		],
    		[
    			243,
    			119
    		]
    	],
    	[
    		[
    			50698,
    			80799
    		],
    		[
    			40,
    			-207
    		],
    		[
    			129,
    			-10
    		],
    		[
    			129,
    			-237
    		],
    		[
    			194,
    			-279
    		],
    		[
    			143,
    			46
    		],
    		[
    			243,
    			-269
    		]
    	],
    	[
    		[
    			51576,
    			79843
    		],
    		[
    			62,
    			-52
    		],
    		[
    			80,
    			13
    		]
    	],
    	[
    		[
    			52429,
    			75765
    		],
    		[
    			179,
    			226
    		],
    		[
    			47,
    			-507
    		],
    		[
    			-92,
    			-456
    		],
    		[
    			-126,
    			120
    		],
    		[
    			-64,
    			398
    		],
    		[
    			56,
    			219
    		]
    	],
    	[
    		[
    			27693,
    			48568
    		],
    		[
    			148,
    			442
    		],
    		[
    			-60,
    			258
    		],
    		[
    			-106,
    			-275
    		],
    		[
    			-166,
    			259
    		],
    		[
    			56,
    			167
    		],
    		[
    			-47,
    			536
    		],
    		[
    			97,
    			89
    		],
    		[
    			52,
    			368
    		],
    		[
    			105,
    			381
    		],
    		[
    			-20,
    			241
    		],
    		[
    			153,
    			126
    		],
    		[
    			190,
    			236
    		]
    	],
    	[
    		[
    			31588,
    			61519
    		],
    		[
    			142,
    			-52
    		],
    		[
    			50,
    			-118
    		],
    		[
    			-71,
    			-149
    		],
    		[
    			-209,
    			4
    		],
    		[
    			-163,
    			-21
    		],
    		[
    			-16,
    			253
    		],
    		[
    			40,
    			86
    		],
    		[
    			227,
    			-3
    		]
    	],
    	[
    		[
    			28453,
    			61504
    		],
    		[
    			187,
    			-53
    		],
    		[
    			147,
    			-142
    		],
    		[
    			46,
    			-161
    		],
    		[
    			-195,
    			-11
    		],
    		[
    			-84,
    			-99
    		],
    		[
    			-156,
    			95
    		],
    		[
    			-159,
    			215
    		],
    		[
    			34,
    			135
    		],
    		[
    			116,
    			41
    		],
    		[
    			64,
    			-20
    		]
    	],
    	[
    		[
    			27147,
    			64280
    		],
    		[
    			240,
    			-42
    		],
    		[
    			219,
    			-7
    		],
    		[
    			261,
    			-201
    		],
    		[
    			110,
    			-216
    		],
    		[
    			260,
    			66
    		],
    		[
    			98,
    			-138
    		],
    		[
    			235,
    			-366
    		],
    		[
    			173,
    			-267
    		],
    		[
    			92,
    			8
    		],
    		[
    			165,
    			-120
    		],
    		[
    			-20,
    			-167
    		],
    		[
    			205,
    			-24
    		],
    		[
    			210,
    			-242
    		],
    		[
    			-33,
    			-138
    		],
    		[
    			-185,
    			-75
    		],
    		[
    			-187,
    			-29
    		],
    		[
    			-191,
    			46
    		],
    		[
    			-398,
    			-57
    		],
    		[
    			186,
    			329
    		],
    		[
    			-113,
    			154
    		],
    		[
    			-179,
    			39
    		],
    		[
    			-96,
    			171
    		],
    		[
    			-66,
    			336
    		],
    		[
    			-157,
    			-23
    		],
    		[
    			-259,
    			159
    		],
    		[
    			-83,
    			124
    		],
    		[
    			-362,
    			91
    		],
    		[
    			-97,
    			115
    		],
    		[
    			104,
    			148
    		],
    		[
    			-273,
    			30
    		],
    		[
    			-199,
    			-307
    		],
    		[
    			-115,
    			-8
    		],
    		[
    			-40,
    			-144
    		],
    		[
    			-138,
    			-65
    		],
    		[
    			-118,
    			56
    		],
    		[
    			146,
    			183
    		],
    		[
    			60,
    			213
    		],
    		[
    			126,
    			131
    		],
    		[
    			142,
    			116
    		],
    		[
    			210,
    			56
    		],
    		[
    			67,
    			65
    		]
    	],
    	[
    		[
    			58175,
    			37528
    		],
    		[
    			-177,
    			267
    		],
    		[
    			-215,
    			90
    		],
    		[
    			-82,
    			375
    		],
    		[
    			0,
    			208
    		],
    		[
    			-119,
    			64
    		],
    		[
    			-315,
    			649
    		],
    		[
    			-87,
    			342
    		],
    		[
    			-56,
    			105
    		],
    		[
    			-107,
    			473
    		]
    	],
    	[
    		[
    			57017,
    			40101
    		],
    		[
    			311,
    			-65
    		],
    		[
    			90,
    			-68
    		],
    		[
    			94,
    			13
    		],
    		[
    			154,
    			383
    		],
    		[
    			241,
    			486
    		],
    		[
    			100,
    			46
    		],
    		[
    			33,
    			205
    		],
    		[
    			159,
    			235
    		],
    		[
    			210,
    			81
    		]
    	],
    	[
    		[
    			58409,
    			41417
    		],
    		[
    			18,
    			-220
    		],
    		[
    			232,
    			12
    		],
    		[
    			128,
    			-125
    		],
    		[
    			60,
    			-146
    		],
    		[
    			132,
    			-43
    		],
    		[
    			145,
    			-190
    		],
    		[
    			0,
    			-748
    		],
    		[
    			-54,
    			-409
    		],
    		[
    			-12,
    			-442
    		],
    		[
    			45,
    			-175
    		],
    		[
    			-31,
    			-348
    		],
    		[
    			-42,
    			-53
    		],
    		[
    			-74,
    			-426
    		],
    		[
    			-292,
    			-671
    		]
    	],
    	[
    		[
    			55526,
    			35946
    		],
    		[
    			0,
    			1725
    		],
    		[
    			274,
    			20
    		],
    		[
    			8,
    			2105
    		],
    		[
    			207,
    			19
    		],
    		[
    			428,
    			207
    		],
    		[
    			106,
    			-243
    		],
    		[
    			177,
    			231
    		],
    		[
    			85,
    			2
    		],
    		[
    			156,
    			133
    		]
    	],
    	[
    		[
    			56967,
    			40145
    		],
    		[
    			50,
    			-44
    		]
    	],
    	[
    		[
    			54540,
    			33696
    		],
    		[
    			-207,
    			446
    		],
    		[
    			-108,
    			432
    		],
    		[
    			-62,
    			575
    		],
    		[
    			-68,
    			428
    		],
    		[
    			-93,
    			910
    		],
    		[
    			-7,
    			707
    		],
    		[
    			-35,
    			322
    		],
    		[
    			-108,
    			243
    		],
    		[
    			-144,
    			489
    		],
    		[
    			-146,
    			708
    		],
    		[
    			-60,
    			371
    		],
    		[
    			-226,
    			577
    		],
    		[
    			-17,
    			453
    		]
    	],
    	[
    		[
    			53259,
    			40357
    		],
    		[
    			134,
    			113
    		],
    		[
    			166,
    			100
    		],
    		[
    			180,
    			-17
    		],
    		[
    			166,
    			-267
    		],
    		[
    			42,
    			41
    		],
    		[
    			1126,
    			26
    		],
    		[
    			192,
    			-284
    		],
    		[
    			673,
    			-83
    		],
    		[
    			510,
    			241
    		]
    	],
    	[
    		[
    			56448,
    			40227
    		],
    		[
    			228,
    			134
    		],
    		[
    			180,
    			-34
    		],
    		[
    			109,
    			-133
    		],
    		[
    			2,
    			-49
    		]
    	],
    	[
    		[
    			45357,
    			58612
    		],
    		[
    			-115,
    			460
    		],
    		[
    			-138,
    			210
    		],
    		[
    			122,
    			112
    		],
    		[
    			134,
    			415
    		],
    		[
    			66,
    			304
    		]
    	],
    	[
    		[
    			45426,
    			60113
    		],
    		[
    			96,
    			189
    		],
    		[
    			138,
    			-51
    		],
    		[
    			135,
    			129
    		],
    		[
    			155,
    			6
    		],
    		[
    			133,
    			-173
    		],
    		[
    			184,
    			-157
    		],
    		[
    			168,
    			-435
    		],
    		[
    			184,
    			-405
    		]
    	],
    	[
    		[
    			46619,
    			59216
    		],
    		[
    			13,
    			-368
    		],
    		[
    			54,
    			-338
    		],
    		[
    			104,
    			-166
    		],
    		[
    			24,
    			-229
    		],
    		[
    			-13,
    			-184
    		]
    	],
    	[
    		[
    			46801,
    			57931
    		],
    		[
    			-40,
    			-33
    		],
    		[
    			-151,
    			47
    		],
    		[
    			-21,
    			-66
    		],
    		[
    			-61,
    			-13
    		],
    		[
    			-200,
    			144
    		],
    		[
    			-134,
    			6
    		]
    	],
    	[
    		[
    			46194,
    			58016
    		],
    		[
    			-513,
    			25
    		],
    		[
    			-75,
    			-67
    		],
    		[
    			-92,
    			19
    		],
    		[
    			-147,
    			-96
    		]
    	],
    	[
    		[
    			45367,
    			57897
    		],
    		[
    			-46,
    			453
    		]
    	],
    	[
    		[
    			45321,
    			58350
    		],
    		[
    			253,
    			-13
    		],
    		[
    			67,
    			83
    		],
    		[
    			50,
    			5
    		],
    		[
    			103,
    			136
    		],
    		[
    			119,
    			-124
    		],
    		[
    			121,
    			-11
    		],
    		[
    			120,
    			133
    		],
    		[
    			-56,
    			170
    		],
    		[
    			-92,
    			-99
    		],
    		[
    			-86,
    			3
    		],
    		[
    			-110,
    			145
    		],
    		[
    			-88,
    			-9
    		],
    		[
    			-63,
    			-140
    		],
    		[
    			-302,
    			-17
    		]
    	],
    	[
    		[
    			46619,
    			59216
    		],
    		[
    			93,
    			107
    		],
    		[
    			47,
    			348
    		],
    		[
    			88,
    			14
    		],
    		[
    			194,
    			-165
    		],
    		[
    			157,
    			117
    		],
    		[
    			107,
    			-39
    		],
    		[
    			42,
    			131
    		],
    		[
    			1114,
    			9
    		],
    		[
    			62,
    			414
    		],
    		[
    			-48,
    			73
    		],
    		[
    			-134,
    			2550
    		],
    		[
    			-134,
    			2550
    		],
    		[
    			425,
    			10
    		]
    	],
    	[
    		[
    			48632,
    			65335
    		],
    		[
    			937,
    			-1289
    		],
    		[
    			937,
    			-1289
    		],
    		[
    			66,
    			-277
    		],
    		[
    			173,
    			-169
    		],
    		[
    			129,
    			-96
    		],
    		[
    			3,
    			-376
    		],
    		[
    			308,
    			58
    		]
    	],
    	[
    		[
    			51185,
    			61897
    		],
    		[
    			1,
    			-1361
    		],
    		[
    			-152,
    			-394
    		],
    		[
    			-24,
    			-364
    		],
    		[
    			-247,
    			-94
    		],
    		[
    			-379,
    			-51
    		],
    		[
    			-102,
    			-210
    		],
    		[
    			-178,
    			-23
    		]
    	],
    	[
    		[
    			50104,
    			59400
    		],
    		[
    			-178,
    			-3
    		],
    		[
    			-70,
    			114
    		],
    		[
    			-153,
    			-84
    		],
    		[
    			-259,
    			-246
    		],
    		[
    			-53,
    			-184
    		],
    		[
    			-216,
    			-265
    		],
    		[
    			-38,
    			-152
    		],
    		[
    			-116,
    			-120
    		],
    		[
    			-134,
    			79
    		],
    		[
    			-76,
    			-144
    		],
    		[
    			-41,
    			-405
    		],
    		[
    			-221,
    			-490
    		],
    		[
    			7,
    			-200
    		],
    		[
    			-76,
    			-250
    		],
    		[
    			18,
    			-343
    		]
    	],
    	[
    		[
    			48498,
    			56707
    		],
    		[
    			-114,
    			-88
    		],
    		[
    			-65,
    			-74
    		],
    		[
    			-43,
    			253
    		],
    		[
    			-80,
    			-67
    		],
    		[
    			-48,
    			11
    		],
    		[
    			-51,
    			-172
    		],
    		[
    			-215,
    			5
    		],
    		[
    			-77,
    			89
    		],
    		[
    			-36,
    			-54
    		]
    	],
    	[
    		[
    			47769,
    			56610
    		],
    		[
    			-85,
    			170
    		],
    		[
    			15,
    			176
    		],
    		[
    			-35,
    			69
    		],
    		[
    			-59,
    			-58
    		],
    		[
    			11,
    			192
    		],
    		[
    			57,
    			152
    		],
    		[
    			-114,
    			248
    		],
    		[
    			-33,
    			163
    		],
    		[
    			-62,
    			130
    		],
    		[
    			-55,
    			15
    		],
    		[
    			-67,
    			-83
    		],
    		[
    			-90,
    			-79
    		],
    		[
    			-76,
    			-128
    		],
    		[
    			-119,
    			48
    		],
    		[
    			-77,
    			150
    		],
    		[
    			-46,
    			19
    		],
    		[
    			-73,
    			-78
    		],
    		[
    			-44,
    			-1
    		],
    		[
    			-16,
    			216
    		]
    	],
    	[
    		[
    			47587,
    			66766
    		],
    		[
    			1045,
    			-1431
    		]
    	],
    	[
    		[
    			45426,
    			60113
    		],
    		[
    			-24,
    			318
    		],
    		[
    			78,
    			291
    		],
    		[
    			34,
    			557
    		],
    		[
    			-30,
    			583
    		],
    		[
    			-34,
    			294
    		],
    		[
    			28,
    			295
    		],
    		[
    			-72,
    			281
    		],
    		[
    			-146,
    			255
    		]
    	],
    	[
    		[
    			50747,
    			54278
    		],
    		[
    			-229,
    			-69
    		]
    	],
    	[
    		[
    			50518,
    			54209
    		],
    		[
    			-69,
    			407
    		],
    		[
    			13,
    			1357
    		],
    		[
    			-56,
    			122
    		],
    		[
    			-11,
    			290
    		],
    		[
    			-96,
    			207
    		],
    		[
    			-85,
    			174
    		],
    		[
    			35,
    			311
    		]
    	],
    	[
    		[
    			50249,
    			57077
    		],
    		[
    			96,
    			67
    		],
    		[
    			56,
    			258
    		],
    		[
    			136,
    			56
    		],
    		[
    			61,
    			176
    		]
    	],
    	[
    		[
    			50598,
    			57634
    		],
    		[
    			93,
    			173
    		],
    		[
    			100,
    			2
    		],
    		[
    			212,
    			-340
    		]
    	],
    	[
    		[
    			51003,
    			57469
    		],
    		[
    			-11,
    			-197
    		],
    		[
    			62,
    			-350
    		],
    		[
    			-54,
    			-238
    		],
    		[
    			29,
    			-159
    		],
    		[
    			-135,
    			-366
    		],
    		[
    			-86,
    			-181
    		],
    		[
    			-52,
    			-372
    		],
    		[
    			7,
    			-376
    		],
    		[
    			-16,
    			-952
    		]
    	],
    	[
    		[
    			54026,
    			58177
    		],
    		[
    			-78,
    			-34
    		],
    		[
    			-9,
    			-188
    		]
    	],
    	[
    		[
    			53939,
    			57955
    		],
    		[
    			-52,
    			-13
    		],
    		[
    			-188,
    			647
    		],
    		[
    			-65,
    			24
    		],
    		[
    			-217,
    			-331
    		],
    		[
    			-215,
    			173
    		],
    		[
    			-150,
    			34
    		],
    		[
    			-80,
    			-83
    		],
    		[
    			-163,
    			18
    		],
    		[
    			-164,
    			-252
    		],
    		[
    			-141,
    			-14
    		],
    		[
    			-337,
    			305
    		],
    		[
    			-131,
    			-145
    		],
    		[
    			-142,
    			10
    		],
    		[
    			-104,
    			223
    		],
    		[
    			-279,
    			221
    		],
    		[
    			-298,
    			-70
    		],
    		[
    			-72,
    			-128
    		],
    		[
    			-39,
    			-340
    		],
    		[
    			-80,
    			-238
    		],
    		[
    			-19,
    			-527
    		]
    	],
    	[
    		[
    			50598,
    			57634
    		],
    		[
    			6,
    			405
    		],
    		[
    			-320,
    			134
    		],
    		[
    			-9,
    			286
    		],
    		[
    			-156,
    			386
    		],
    		[
    			-37,
    			269
    		],
    		[
    			22,
    			286
    		]
    	],
    	[
    		[
    			51185,
    			61897
    		],
    		[
    			392,
    			263
    		],
    		[
    			804,
    			1161
    		],
    		[
    			952,
    			1126
    		]
    	],
    	[
    		[
    			53333,
    			64447
    		],
    		[
    			439,
    			-255
    		],
    		[
    			156,
    			-324
    		],
    		[
    			197,
    			220
    		]
    	],
    	[
    		[
    			53939,
    			57955
    		],
    		[
    			110,
    			-235
    		],
    		[
    			-31,
    			-107
    		],
    		[
    			-14,
    			-196
    		],
    		[
    			-234,
    			-457
    		],
    		[
    			-74,
    			-377
    		],
    		[
    			-39,
    			-307
    		],
    		[
    			-59,
    			-132
    		],
    		[
    			-56,
    			-414
    		],
    		[
    			-148,
    			-243
    		],
    		[
    			-43,
    			-299
    		],
    		[
    			-63,
    			-238
    		],
    		[
    			-26,
    			-246
    		],
    		[
    			-191,
    			-199
    		],
    		[
    			-156,
    			243
    		],
    		[
    			-105,
    			-10
    		],
    		[
    			-165,
    			-345
    		],
    		[
    			-81,
    			-6
    		],
    		[
    			-132,
    			-570
    		],
    		[
    			-71,
    			-418
    		]
    	],
    	[
    		[
    			52361,
    			53399
    		],
    		[
    			-289,
    			-213
    		],
    		[
    			-105,
    			31
    		],
    		[
    			-107,
    			-132
    		],
    		[
    			-222,
    			13
    		],
    		[
    			-149,
    			370
    		],
    		[
    			-91,
    			427
    		],
    		[
    			-197,
    			389
    		],
    		[
    			-209,
    			-7
    		],
    		[
    			-245,
    			1
    		]
    	],
    	[
    		[
    			54244,
    			54965
    		],
    		[
    			-140,
    			-599
    		],
    		[
    			-67,
    			-107
    		],
    		[
    			-21,
    			-458
    		],
    		[
    			28,
    			-249
    		],
    		[
    			-23,
    			-176
    		],
    		[
    			132,
    			-309
    		],
    		[
    			23,
    			-212
    		],
    		[
    			103,
    			-305
    		],
    		[
    			127,
    			-190
    		],
    		[
    			12,
    			-269
    		],
    		[
    			29,
    			-172
    		]
    	],
    	[
    		[
    			54447,
    			51919
    		],
    		[
    			-20,
    			-319
    		],
    		[
    			-220,
    			140
    		],
    		[
    			-225,
    			156
    		],
    		[
    			-350,
    			23
    		]
    	],
    	[
    		[
    			53632,
    			51919
    		],
    		[
    			-35,
    			32
    		],
    		[
    			-164,
    			-76
    		],
    		[
    			-169,
    			79
    		],
    		[
    			-132,
    			-38
    		]
    	],
    	[
    		[
    			53132,
    			51916
    		],
    		[
    			-452,
    			13
    		]
    	],
    	[
    		[
    			52680,
    			51929
    		],
    		[
    			40,
    			466
    		],
    		[
    			-108,
    			391
    		],
    		[
    			-127,
    			100
    		],
    		[
    			-56,
    			265
    		],
    		[
    			-72,
    			85
    		],
    		[
    			4,
    			163
    		]
    	],
    	[
    		[
    			50518,
    			54209
    		],
    		[
    			-224,
    			-126
    		]
    	],
    	[
    		[
    			50294,
    			54083
    		],
    		[
    			-62,
    			207
    		],
    		[
    			-74,
    			375
    		],
    		[
    			-22,
    			294
    		],
    		[
    			61,
    			532
    		],
    		[
    			-69,
    			215
    		],
    		[
    			-27,
    			466
    		],
    		[
    			1,
    			429
    		],
    		[
    			-116,
    			305
    		],
    		[
    			20,
    			184
    		]
    	],
    	[
    		[
    			50006,
    			57090
    		],
    		[
    			243,
    			-13
    		]
    	],
    	[
    		[
    			50294,
    			54083
    		],
    		[
    			-436,
    			-346
    		],
    		[
    			-154,
    			-203
    		],
    		[
    			-250,
    			-171
    		],
    		[
    			-248,
    			168
    		]
    	],
    	[
    		[
    			49206,
    			53531
    		],
    		[
    			13,
    			233
    		],
    		[
    			-121,
    			509
    		],
    		[
    			73,
    			667
    		],
    		[
    			117,
    			496
    		],
    		[
    			-74,
    			841
    		]
    	],
    	[
    		[
    			49214,
    			56277
    		],
    		[
    			-38,
    			444
    		],
    		[
    			7,
    			336
    		],
    		[
    			482,
    			27
    		],
    		[
    			123,
    			-43
    		],
    		[
    			90,
    			96
    		],
    		[
    			128,
    			-47
    		]
    	],
    	[
    		[
    			48498,
    			56707
    		],
    		[
    			125,
    			-129
    		],
    		[
    			49,
    			-195
    		],
    		[
    			125,
    			-125
    		],
    		[
    			97,
    			149
    		],
    		[
    			130,
    			22
    		],
    		[
    			190,
    			-152
    		]
    	],
    	[
    		[
    			49206,
    			53531
    		],
    		[
    			-126,
    			-7
    		],
    		[
    			-194,
    			116
    		],
    		[
    			-178,
    			-7
    		],
    		[
    			-329,
    			-103
    		],
    		[
    			-193,
    			-170
    		],
    		[
    			-275,
    			-217
    		],
    		[
    			-54,
    			15
    		]
    	],
    	[
    		[
    			47857,
    			53158
    		],
    		[
    			22,
    			487
    		],
    		[
    			26,
    			74
    		],
    		[
    			-8,
    			233
    		],
    		[
    			-118,
    			247
    		],
    		[
    			-88,
    			40
    		],
    		[
    			-81,
    			162
    		],
    		[
    			60,
    			262
    		],
    		[
    			-28,
    			286
    		],
    		[
    			13,
    			172
    		]
    	],
    	[
    		[
    			47655,
    			55121
    		],
    		[
    			44,
    			0
    		],
    		[
    			17,
    			258
    		],
    		[
    			-22,
    			114
    		],
    		[
    			27,
    			82
    		],
    		[
    			103,
    			71
    		],
    		[
    			-69,
    			473
    		],
    		[
    			-64,
    			245
    		],
    		[
    			23,
    			200
    		],
    		[
    			55,
    			46
    		]
    	],
    	[
    		[
    			47655,
    			55121
    		],
    		[
    			-78,
    			15
    		],
    		[
    			-57,
    			-238
    		],
    		[
    			-78,
    			3
    		],
    		[
    			-55,
    			126
    		],
    		[
    			19,
    			237
    		],
    		[
    			-116,
    			362
    		],
    		[
    			-73,
    			-67
    		],
    		[
    			-59,
    			-13
    		]
    	],
    	[
    		[
    			47158,
    			55546
    		],
    		[
    			-77,
    			-34
    		],
    		[
    			3,
    			217
    		],
    		[
    			-44,
    			155
    		],
    		[
    			9,
    			171
    		],
    		[
    			-60,
    			249
    		],
    		[
    			-78,
    			211
    		],
    		[
    			-222,
    			1
    		],
    		[
    			-65,
    			-112
    		],
    		[
    			-76,
    			-13
    		],
    		[
    			-48,
    			-128
    		],
    		[
    			-32,
    			-163
    		],
    		[
    			-148,
    			-260
    		]
    	],
    	[
    		[
    			46320,
    			55840
    		],
    		[
    			-122,
    			349
    		],
    		[
    			-108,
    			232
    		],
    		[
    			-71,
    			76
    		],
    		[
    			-69,
    			118
    		],
    		[
    			-32,
    			261
    		],
    		[
    			-41,
    			130
    		],
    		[
    			-80,
    			97
    		]
    	],
    	[
    		[
    			45797,
    			57103
    		],
    		[
    			123,
    			288
    		],
    		[
    			84,
    			-11
    		],
    		[
    			73,
    			99
    		],
    		[
    			61,
    			1
    		],
    		[
    			44,
    			78
    		],
    		[
    			-24,
    			196
    		],
    		[
    			31,
    			62
    		],
    		[
    			5,
    			200
    		]
    	],
    	[
    		[
    			45797,
    			57103
    		],
    		[
    			-149,
    			247
    		],
    		[
    			-117,
    			39
    		],
    		[
    			-63,
    			166
    		],
    		[
    			1,
    			90
    		],
    		[
    			-84,
    			125
    		],
    		[
    			-18,
    			127
    		]
    	],
    	[
    		[
    			47857,
    			53158
    		],
    		[
    			-73,
    			-5
    		],
    		[
    			-286,
    			282
    		],
    		[
    			-252,
    			449
    		],
    		[
    			-237,
    			324
    		],
    		[
    			-187,
    			381
    		]
    	],
    	[
    		[
    			46822,
    			54589
    		],
    		[
    			66,
    			189
    		],
    		[
    			15,
    			172
    		],
    		[
    			126,
    			320
    		],
    		[
    			129,
    			276
    		]
    	],
    	[
    		[
    			46822,
    			54589
    		],
    		[
    			-75,
    			44
    		],
    		[
    			-200,
    			238
    		],
    		[
    			-144,
    			316
    		],
    		[
    			-49,
    			216
    		],
    		[
    			-34,
    			437
    		]
    	],
    	[
    		[
    			55125,
    			52650
    		],
    		[
    			-178,
    			33
    		],
    		[
    			-188,
    			99
    		],
    		[
    			-166,
    			-313
    		],
    		[
    			-146,
    			-550
    		]
    	],
    	[
    		[
    			56824,
    			55442
    		],
    		[
    			152,
    			-239
    		],
    		[
    			2,
    			-192
    		],
    		[
    			187,
    			-308
    		],
    		[
    			116,
    			-255
    		],
    		[
    			70,
    			-355
    		],
    		[
    			208,
    			-234
    		],
    		[
    			44,
    			-187
    		]
    	],
    	[
    		[
    			53609,
    			47755
    		],
    		[
    			-104,
    			203
    		],
    		[
    			-84,
    			-100
    		],
    		[
    			-112,
    			-255
    		]
    	],
    	[
    		[
    			53309,
    			47603
    		],
    		[
    			-228,
    			626
    		]
    	],
    	[
    		[
    			53081,
    			48229
    		],
    		[
    			212,
    			326
    		],
    		[
    			-105,
    			391
    		],
    		[
    			95,
    			148
    		],
    		[
    			187,
    			73
    		],
    		[
    			23,
    			261
    		],
    		[
    			148,
    			-283
    		],
    		[
    			245,
    			-25
    		],
    		[
    			85,
    			279
    		],
    		[
    			36,
    			393
    		],
    		[
    			-31,
    			461
    		],
    		[
    			-131,
    			350
    		],
    		[
    			120,
    			684
    		],
    		[
    			-69,
    			117
    		],
    		[
    			-207,
    			-48
    		],
    		[
    			-78,
    			305
    		],
    		[
    			21,
    			258
    		]
    	],
    	[
    		[
    			53081,
    			48229
    		],
    		[
    			-285,
    			596
    		],
    		[
    			-184,
    			488
    		],
    		[
    			-169,
    			610
    		],
    		[
    			9,
    			196
    		],
    		[
    			61,
    			189
    		],
    		[
    			67,
    			430
    		],
    		[
    			56,
    			438
    		]
    	],
    	[
    		[
    			52636,
    			51176
    		],
    		[
    			94,
    			35
    		],
    		[
    			404,
    			-6
    		],
    		[
    			-2,
    			711
    		]
    	],
    	[
    		[
    			52636,
    			51176
    		],
    		[
    			-52,
    			90
    		],
    		[
    			96,
    			663
    		]
    	],
    	[
    		[
    			59099,
    			45126
    		],
    		[
    			131,
    			-264
    		],
    		[
    			71,
    			-501
    		],
    		[
    			-47,
    			-160
    		],
    		[
    			-56,
    			-479
    		],
    		[
    			53,
    			-490
    		],
    		[
    			-87,
    			-205
    		],
    		[
    			-85,
    			-549
    		],
    		[
    			147,
    			-153
    		]
    	],
    	[
    		[
    			59226,
    			42325
    		],
    		[
    			-843,
    			-487
    		],
    		[
    			26,
    			-421
    		]
    	],
    	[
    		[
    			56448,
    			40227
    		],
    		[
    			-181,
    			369
    		],
    		[
    			-188,
    			483
    		],
    		[
    			13,
    			1880
    		],
    		[
    			579,
    			-7
    		],
    		[
    			-24,
    			203
    		],
    		[
    			41,
    			222
    		],
    		[
    			-49,
    			277
    		],
    		[
    			32,
    			286
    		],
    		[
    			-29,
    			184
    		]
    	],
    	[
    		[
    			59599,
    			43773
    		],
    		[
    			-77,
    			-449
    		],
    		[
    			77,
    			-768
    		],
    		[
    			97,
    			9
    		],
    		[
    			100,
    			-191
    		],
    		[
    			116,
    			-427
    		],
    		[
    			24,
    			-760
    		],
    		[
    			-120,
    			-124
    		],
    		[
    			-85,
    			-410
    		],
    		[
    			-181,
    			365
    		],
    		[
    			-21,
    			417
    		],
    		[
    			59,
    			274
    		],
    		[
    			-16,
    			237
    		],
    		[
    			-110,
    			149
    		],
    		[
    			-77,
    			-54
    		],
    		[
    			-159,
    			284
    		]
    	],
    	[
    		[
    			61198,
    			44484
    		],
    		[
    			45,
    			-265
    		],
    		[
    			-11,
    			-588
    		],
    		[
    			34,
    			-519
    		],
    		[
    			11,
    			-923
    		],
    		[
    			49,
    			-290
    		],
    		[
    			-83,
    			-422
    		],
    		[
    			-108,
    			-410
    		],
    		[
    			-177,
    			-366
    		],
    		[
    			-254,
    			-225
    		],
    		[
    			-313,
    			-287
    		],
    		[
    			-313,
    			-634
    		],
    		[
    			-107,
    			-108
    		],
    		[
    			-194,
    			-420
    		],
    		[
    			-115,
    			-136
    		],
    		[
    			-23,
    			-421
    		],
    		[
    			132,
    			-448
    		],
    		[
    			54,
    			-346
    		],
    		[
    			4,
    			-177
    		],
    		[
    			49,
    			29
    		],
    		[
    			-8,
    			-579
    		],
    		[
    			-45,
    			-275
    		],
    		[
    			65,
    			-101
    		],
    		[
    			-41,
    			-245
    		],
    		[
    			-116,
    			-211
    		],
    		[
    			-229,
    			-199
    		],
    		[
    			-334,
    			-320
    		],
    		[
    			-122,
    			-219
    		],
    		[
    			24,
    			-248
    		],
    		[
    			71,
    			-40
    		],
    		[
    			-24,
    			-311
    		]
    	],
    	[
    		[
    			58908,
    			34785
    		],
    		[
    			-24,
    			261
    		],
    		[
    			-41,
    			265
    		]
    	],
    	[
    		[
    			53383,
    			47159
    		],
    		[
    			-74,
    			444
    		]
    	],
    	[
    		[
    			53259,
    			40357
    		],
    		[
    			-26,
    			372
    		],
    		[
    			38,
    			519
    		],
    		[
    			96,
    			541
    		],
    		[
    			15,
    			254
    		],
    		[
    			90,
    			532
    		],
    		[
    			66,
    			243
    		],
    		[
    			159,
    			386
    		],
    		[
    			90,
    			263
    		],
    		[
    			29,
    			438
    		],
    		[
    			-15,
    			335
    		],
    		[
    			-83,
    			211
    		],
    		[
    			-74,
    			358
    		],
    		[
    			-68,
    			355
    		],
    		[
    			15,
    			122
    		],
    		[
    			85,
    			235
    		],
    		[
    			-84,
    			570
    		],
    		[
    			-57,
    			396
    		],
    		[
    			-139,
    			374
    		],
    		[
    			26,
    			115
    		]
    	],
    	[
    		[
    			58062,
    			48902
    		],
    		[
    			169,
    			-46
    		],
    		[
    			85,
    			336
    		],
    		[
    			147,
    			-38
    		]
    	],
    	[
    		[
    			59922,
    			69905
    		],
    		[
    			-49,
    			-186
    		]
    	],
    	[
    		[
    			59873,
    			69719
    		],
    		[
    			-100,
    			82
    		],
    		[
    			-58,
    			-394
    		],
    		[
    			69,
    			-66
    		],
    		[
    			-71,
    			-81
    		],
    		[
    			-12,
    			-156
    		],
    		[
    			131,
    			80
    		]
    	],
    	[
    		[
    			59832,
    			69184
    		],
    		[
    			7,
    			-230
    		],
    		[
    			-139,
    			-944
    		]
    	],
    	[
    		[
    			59700,
    			68010
    		],
    		[
    			-27,
    			153
    		],
    		[
    			-155,
    			862
    		]
    	],
    	[
    		[
    			59518,
    			69025
    		],
    		[
    			80,
    			194
    		],
    		[
    			-19,
    			34
    		],
    		[
    			74,
    			276
    		],
    		[
    			56,
    			446
    		],
    		[
    			40,
    			149
    		],
    		[
    			8,
    			6
    		]
    	],
    	[
    		[
    			59757,
    			70130
    		],
    		[
    			93,
    			-1
    		],
    		[
    			25,
    			104
    		],
    		[
    			75,
    			8
    		]
    	],
    	[
    		[
    			59950,
    			70241
    		],
    		[
    			4,
    			-242
    		],
    		[
    			-38,
    			-90
    		],
    		[
    			6,
    			-4
    		]
    	],
    	[
    		[
    			59757,
    			70130
    		],
    		[
    			99,
    			482
    		],
    		[
    			138,
    			416
    		],
    		[
    			5,
    			21
    		]
    	],
    	[
    		[
    			59999,
    			71049
    		],
    		[
    			125,
    			-31
    		],
    		[
    			45,
    			-231
    		],
    		[
    			-151,
    			-223
    		],
    		[
    			-68,
    			-323
    		]
    	],
    	[
    		[
    			63761,
    			43212
    		],
    		[
    			74,
    			-251
    		],
    		[
    			69,
    			-390
    		],
    		[
    			45,
    			-711
    		],
    		[
    			72,
    			-276
    		],
    		[
    			-28,
    			-284
    		],
    		[
    			-49,
    			-174
    		],
    		[
    			-94,
    			347
    		],
    		[
    			-53,
    			-175
    		],
    		[
    			53,
    			-438
    		],
    		[
    			-24,
    			-250
    		],
    		[
    			-77,
    			-137
    		],
    		[
    			-18,
    			-500
    		],
    		[
    			-109,
    			-689
    		],
    		[
    			-137,
    			-814
    		],
    		[
    			-172,
    			-1120
    		],
    		[
    			-106,
    			-821
    		],
    		[
    			-125,
    			-685
    		],
    		[
    			-226,
    			-140
    		],
    		[
    			-243,
    			-250
    		],
    		[
    			-160,
    			151
    		],
    		[
    			-220,
    			211
    		],
    		[
    			-77,
    			312
    		],
    		[
    			-18,
    			524
    		],
    		[
    			-98,
    			471
    		],
    		[
    			-26,
    			425
    		],
    		[
    			50,
    			426
    		],
    		[
    			128,
    			102
    		],
    		[
    			1,
    			197
    		],
    		[
    			133,
    			447
    		],
    		[
    			25,
    			377
    		],
    		[
    			-65,
    			280
    		],
    		[
    			-52,
    			372
    		],
    		[
    			-23,
    			544
    		],
    		[
    			97,
    			331
    		],
    		[
    			38,
    			375
    		],
    		[
    			138,
    			22
    		],
    		[
    			155,
    			121
    		],
    		[
    			103,
    			107
    		],
    		[
    			122,
    			7
    		],
    		[
    			158,
    			337
    		],
    		[
    			229,
    			364
    		],
    		[
    			83,
    			297
    		],
    		[
    			-38,
    			253
    		],
    		[
    			118,
    			-71
    		],
    		[
    			153,
    			410
    		],
    		[
    			6,
    			356
    		],
    		[
    			92,
    			264
    		],
    		[
    			96,
    			-254
    		]
    	],
    	[
    		[
    			59873,
    			69719
    		],
    		[
    			0,
    			-362
    		],
    		[
    			-41,
    			-173
    		]
    	],
    	[
    		[
    			45321,
    			58350
    		],
    		[
    			36,
    			262
    		]
    	],
    	[
    		[
    			52633,
    			68486
    		],
    		[
    			-118,
    			1061
    		],
    		[
    			-171,
    			238
    		],
    		[
    			-3,
    			143
    		],
    		[
    			-227,
    			352
    		],
    		[
    			-24,
    			445
    		],
    		[
    			171,
    			330
    		],
    		[
    			65,
    			487
    		],
    		[
    			-44,
    			563
    		],
    		[
    			57,
    			303
    		]
    	],
    	[
    		[
    			52339,
    			72408
    		],
    		[
    			302,
    			239
    		],
    		[
    			195,
    			-71
    		],
    		[
    			-9,
    			-299
    		],
    		[
    			236,
    			217
    		],
    		[
    			20,
    			-113
    		],
    		[
    			-139,
    			-290
    		],
    		[
    			-2,
    			-273
    		],
    		[
    			96,
    			-147
    		],
    		[
    			-36,
    			-511
    		],
    		[
    			-183,
    			-297
    		],
    		[
    			53,
    			-322
    		],
    		[
    			143,
    			-10
    		],
    		[
    			70,
    			-281
    		],
    		[
    			106,
    			-92
    		]
    	],
    	[
    		[
    			53191,
    			70158
    		],
    		[
    			-16,
    			-454
    		],
    		[
    			-135,
    			-170
    		],
    		[
    			-86,
    			-189
    		],
    		[
    			-191,
    			-228
    		],
    		[
    			30,
    			-244
    		],
    		[
    			-24,
    			-250
    		],
    		[
    			-136,
    			-137
    		]
    	],
    	[
    		[
    			47592,
    			66920
    		],
    		[
    			-2,
    			700
    		],
    		[
    			449,
    			436
    		],
    		[
    			277,
    			90
    		],
    		[
    			227,
    			159
    		],
    		[
    			107,
    			295
    		],
    		[
    			324,
    			234
    		],
    		[
    			12,
    			438
    		],
    		[
    			161,
    			51
    		],
    		[
    			126,
    			219
    		],
    		[
    			363,
    			99
    		],
    		[
    			51,
    			230
    		],
    		[
    			-73,
    			125
    		],
    		[
    			-96,
    			624
    		],
    		[
    			-17,
    			359
    		],
    		[
    			-104,
    			379
    		]
    	],
    	[
    		[
    			49397,
    			71358
    		],
    		[
    			267,
    			323
    		],
    		[
    			300,
    			102
    		],
    		[
    			175,
    			244
    		],
    		[
    			268,
    			180
    		],
    		[
    			471,
    			105
    		],
    		[
    			459,
    			48
    		],
    		[
    			140,
    			-87
    		],
    		[
    			262,
    			232
    		],
    		[
    			297,
    			5
    		],
    		[
    			113,
    			-137
    		],
    		[
    			190,
    			35
    		]
    	],
    	[
    		[
    			52633,
    			68486
    		],
    		[
    			90,
    			-522
    		],
    		[
    			15,
    			-274
    		],
    		[
    			-49,
    			-482
    		],
    		[
    			21,
    			-270
    		],
    		[
    			-36,
    			-323
    		],
    		[
    			24,
    			-371
    		],
    		[
    			-110,
    			-247
    		],
    		[
    			164,
    			-431
    		],
    		[
    			11,
    			-253
    		],
    		[
    			99,
    			-330
    		],
    		[
    			130,
    			109
    		],
    		[
    			219,
    			-275
    		],
    		[
    			122,
    			-370
    		]
    	],
    	[
    		[
    			59922,
    			69905
    		],
    		[
    			309,
    			-234
    		],
    		[
    			544,
    			630
    		]
    	],
    	[
    		[
    			60775,
    			70301
    		],
    		[
    			112,
    			-720
    		]
    	],
    	[
    		[
    			60887,
    			69581
    		],
    		[
    			-53,
    			-89
    		],
    		[
    			-556,
    			-296
    		],
    		[
    			277,
    			-591
    		],
    		[
    			-92,
    			-101
    		],
    		[
    			-46,
    			-197
    		],
    		[
    			-212,
    			-82
    		],
    		[
    			-66,
    			-213
    		],
    		[
    			-120,
    			-182
    		],
    		[
    			-310,
    			94
    		]
    	],
    	[
    		[
    			59709,
    			67924
    		],
    		[
    			-9,
    			86
    		]
    	],
    	[
    		[
    			64327,
    			64904
    		],
    		[
    			49,
    			29
    		],
    		[
    			11,
    			-162
    		],
    		[
    			217,
    			93
    		],
    		[
    			230,
    			-15
    		],
    		[
    			168,
    			-18
    		],
    		[
    			190,
    			400
    		],
    		[
    			207,
    			379
    		],
    		[
    			176,
    			364
    		]
    	],
    	[
    		[
    			65575,
    			65974
    		],
    		[
    			52,
    			-202
    		]
    	],
    	[
    		[
    			65627,
    			65772
    		],
    		[
    			38,
    			-466
    		]
    	],
    	[
    		[
    			65665,
    			65306
    		],
    		[
    			-142,
    			-3
    		],
    		[
    			-23,
    			-384
    		],
    		[
    			50,
    			-82
    		],
    		[
    			-126,
    			-117
    		],
    		[
    			-1,
    			-241
    		],
    		[
    			-81,
    			-245
    		],
    		[
    			-7,
    			-238
    		]
    	],
    	[
    		[
    			65335,
    			63996
    		],
    		[
    			-56,
    			-125
    		],
    		[
    			-835,
    			298
    		],
    		[
    			-106,
    			599
    		],
    		[
    			-11,
    			136
    		]
    	],
    	[
    		[
    			64113,
    			65205
    		],
    		[
    			-18,
    			430
    		],
    		[
    			75,
    			310
    		],
    		[
    			76,
    			64
    		],
    		[
    			84,
    			-185
    		],
    		[
    			5,
    			-346
    		],
    		[
    			-61,
    			-348
    		]
    	],
    	[
    		[
    			64274,
    			65130
    		],
    		[
    			-77,
    			-42
    		],
    		[
    			-84,
    			117
    		]
    	],
    	[
    		[
    			63326,
    			68290
    		],
    		[
    			58,
    			-261
    		],
    		[
    			-25,
    			-135
    		],
    		[
    			89,
    			-445
    		]
    	],
    	[
    		[
    			63448,
    			67449
    		],
    		[
    			-196,
    			-16
    		],
    		[
    			-69,
    			282
    		],
    		[
    			-248,
    			57
    		]
    	],
    	[
    		[
    			62935,
    			67772
    		],
    		[
    			204,
    			567
    		],
    		[
    			187,
    			-49
    		]
    	],
    	[
    		[
    			60775,
    			70301
    		],
    		[
    			615,
    			614
    		],
    		[
    			105,
    			715
    		],
    		[
    			-26,
    			431
    		],
    		[
    			152,
    			146
    		],
    		[
    			142,
    			369
    		]
    	],
    	[
    		[
    			61763,
    			72576
    		],
    		[
    			119,
    			92
    		],
    		[
    			324,
    			-77
    		],
    		[
    			97,
    			-150
    		],
    		[
    			133,
    			100
    		]
    	],
    	[
    		[
    			62436,
    			72541
    		],
    		[
    			180,
    			-705
    		],
    		[
    			182,
    			-177
    		],
    		[
    			21,
    			-345
    		],
    		[
    			-139,
    			-204
    		],
    		[
    			-65,
    			-461
    		],
    		[
    			193,
    			-562
    		],
    		[
    			340,
    			-324
    		],
    		[
    			143,
    			-449
    		],
    		[
    			-46,
    			-428
    		],
    		[
    			89,
    			0
    		],
    		[
    			3,
    			-314
    		],
    		[
    			153,
    			-311
    		]
    	],
    	[
    		[
    			63490,
    			68261
    		],
    		[
    			-164,
    			29
    		]
    	],
    	[
    		[
    			62935,
    			67772
    		],
    		[
    			-516,
    			47
    		],
    		[
    			-784,
    			1188
    		],
    		[
    			-413,
    			414
    		],
    		[
    			-335,
    			160
    		]
    	],
    	[
    		[
    			65665,
    			65306
    		],
    		[
    			125,
    			-404
    		],
    		[
    			155,
    			-214
    		],
    		[
    			203,
    			-78
    		],
    		[
    			165,
    			-107
    		],
    		[
    			125,
    			-339
    		],
    		[
    			75,
    			-196
    		],
    		[
    			100,
    			-75
    		],
    		[
    			-1,
    			-132
    		],
    		[
    			-101,
    			-352
    		],
    		[
    			-44,
    			-166
    		],
    		[
    			-117,
    			-189
    		],
    		[
    			-104,
    			-404
    		],
    		[
    			-126,
    			31
    		],
    		[
    			-58,
    			-141
    		],
    		[
    			-44,
    			-300
    		],
    		[
    			34,
    			-395
    		],
    		[
    			-26,
    			-72
    		],
    		[
    			-128,
    			2
    		],
    		[
    			-174,
    			-221
    		],
    		[
    			-27,
    			-288
    		],
    		[
    			-63,
    			-125
    		],
    		[
    			-173,
    			5
    		],
    		[
    			-109,
    			-149
    		],
    		[
    			1,
    			-238
    		],
    		[
    			-134,
    			-165
    		],
    		[
    			-153,
    			56
    		],
    		[
    			-186,
    			-199
    		],
    		[
    			-128,
    			-34
    		]
    	],
    	[
    		[
    			64752,
    			60417
    		],
    		[
    			-91,
    			413
    		],
    		[
    			-217,
    			975
    		]
    	],
    	[
    		[
    			64444,
    			61805
    		],
    		[
    			833,
    			591
    		],
    		[
    			185,
    			1182
    		],
    		[
    			-127,
    			418
    		]
    	],
    	[
    		[
    			65575,
    			65974
    		],
    		[
    			80,
    			201
    		],
    		[
    			35,
    			-51
    		],
    		[
    			-26,
    			-244
    		],
    		[
    			-37,
    			-108
    		]
    	],
    	[
    		[
    			96448,
    			41190
    		],
    		[
    			175,
    			-339
    		],
    		[
    			-92,
    			-78
    		],
    		[
    			-93,
    			259
    		],
    		[
    			10,
    			158
    		]
    	],
    	[
    		[
    			96330,
    			41322
    		],
    		[
    			-39,
    			163
    		],
    		[
    			-6,
    			453
    		],
    		[
    			133,
    			-182
    		],
    		[
    			45,
    			-476
    		],
    		[
    			-75,
    			74
    		],
    		[
    			-58,
    			-32
    		]
    	],
    	[
    		[
    			78495,
    			57780
    		],
    		[
    			-66,
    			713
    		],
    		[
    			178,
    			492
    		],
    		[
    			359,
    			112
    		],
    		[
    			261,
    			-84
    		]
    	],
    	[
    		[
    			79227,
    			59013
    		],
    		[
    			229,
    			-232
    		],
    		[
    			126,
    			407
    		],
    		[
    			246,
    			-217
    		]
    	],
    	[
    		[
    			79828,
    			58971
    		],
    		[
    			64,
    			-394
    		],
    		[
    			-34,
    			-708
    		],
    		[
    			-467,
    			-455
    		],
    		[
    			122,
    			-358
    		],
    		[
    			-292,
    			-43
    		],
    		[
    			-240,
    			-238
    		]
    	],
    	[
    		[
    			78981,
    			56775
    		],
    		[
    			-233,
    			87
    		],
    		[
    			-112,
    			307
    		],
    		[
    			-141,
    			611
    		]
    	],
    	[
    		[
    			78495,
    			57780
    		],
    		[
    			-249,
    			271
    		],
    		[
    			-238,
    			-11
    		],
    		[
    			41,
    			464
    		],
    		[
    			-245,
    			-3
    		],
    		[
    			-22,
    			-650
    		],
    		[
    			-150,
    			-863
    		],
    		[
    			-90,
    			-522
    		],
    		[
    			19,
    			-428
    		],
    		[
    			181,
    			-18
    		],
    		[
    			113,
    			-539
    		],
    		[
    			50,
    			-512
    		],
    		[
    			155,
    			-338
    		],
    		[
    			168,
    			-69
    		],
    		[
    			144,
    			-306
    		]
    	],
    	[
    		[
    			78372,
    			54256
    		],
    		[
    			-91,
    			-243
    		],
    		[
    			-183,
    			-71
    		],
    		[
    			-22,
    			304
    		],
    		[
    			-227,
    			258
    		],
    		[
    			-48,
    			-105
    		]
    	],
    	[
    		[
    			77801,
    			54399
    		],
    		[
    			-110,
    			227
    		],
    		[
    			-47,
    			292
    		],
    		[
    			-148,
    			334
    		],
    		[
    			-135,
    			280
    		],
    		[
    			-45,
    			-347
    		],
    		[
    			-53,
    			328
    		],
    		[
    			30,
    			369
    		],
    		[
    			82,
    			566
    		]
    	],
    	[
    		[
    			77375,
    			56448
    		],
    		[
    			135,
    			607
    		],
    		[
    			152,
    			551
    		],
    		[
    			-108,
    			539
    		],
    		[
    			4,
    			274
    		],
    		[
    			-32,
    			330
    		],
    		[
    			-185,
    			470
    		],
    		[
    			-66,
    			296
    		],
    		[
    			96,
    			109
    		],
    		[
    			101,
    			514
    		],
    		[
    			-113,
    			390
    		],
    		[
    			-177,
    			431
    		],
    		[
    			-134,
    			519
    		],
    		[
    			117,
    			107
    		],
    		[
    			127,
    			639
    		],
    		[
    			196,
    			26
    		],
    		[
    			162,
    			256
    		],
    		[
    			159,
    			137
    		]
    	],
    	[
    		[
    			77809,
    			62643
    		],
    		[
    			120,
    			-182
    		],
    		[
    			16,
    			-355
    		],
    		[
    			188,
    			-27
    		],
    		[
    			-68,
    			-623
    		],
    		[
    			6,
    			-530
    		],
    		[
    			293,
    			353
    		],
    		[
    			83,
    			-104
    		],
    		[
    			163,
    			17
    		],
    		[
    			56,
    			205
    		],
    		[
    			210,
    			-40
    		],
    		[
    			211,
    			-480
    		],
    		[
    			18,
    			-583
    		],
    		[
    			224,
    			-515
    		],
    		[
    			-12,
    			-500
    		],
    		[
    			-90,
    			-266
    		]
    	],
    	[
    		[
    			77809,
    			62643
    		],
    		[
    			59,
    			218
    		],
    		[
    			237,
    			384
    		]
    	],
    	[
    		[
    			78105,
    			63245
    		],
    		[
    			25,
    			-139
    		],
    		[
    			148,
    			-16
    		],
    		[
    			-42,
    			676
    		],
    		[
    			144,
    			86
    		]
    	],
    	[
    		[
    			78380,
    			63852
    		],
    		[
    			162,
    			-466
    		],
    		[
    			125,
    			-537
    		],
    		[
    			342,
    			-5
    		],
    		[
    			108,
    			-515
    		],
    		[
    			-178,
    			-155
    		],
    		[
    			-80,
    			-212
    		],
    		[
    			333,
    			-353
    		],
    		[
    			231,
    			-699
    		],
    		[
    			175,
    			-520
    		],
    		[
    			210,
    			-411
    		],
    		[
    			70,
    			-418
    		],
    		[
    			-50,
    			-590
    		]
    	],
    	[
    		[
    			77375,
    			56448
    		],
    		[
    			-27,
    			439
    		],
    		[
    			86,
    			452
    		],
    		[
    			-94,
    			350
    		],
    		[
    			23,
    			644
    		],
    		[
    			-113,
    			306
    		],
    		[
    			-90,
    			707
    		],
    		[
    			-50,
    			746
    		],
    		[
    			-121,
    			490
    		],
    		[
    			-183,
    			-297
    		],
    		[
    			-315,
    			-421
    		],
    		[
    			-156,
    			53
    		],
    		[
    			-172,
    			138
    		],
    		[
    			96,
    			732
    		],
    		[
    			-58,
    			554
    		],
    		[
    			-218,
    			681
    		],
    		[
    			34,
    			213
    		],
    		[
    			-163,
    			76
    		],
    		[
    			-197,
    			481
    		]
    	],
    	[
    		[
    			75657,
    			62792
    		],
    		[
    			-18,
    			476
    		],
    		[
    			97,
    			-90
    		],
    		[
    			6,
    			424
    		]
    	],
    	[
    		[
    			75742,
    			63602
    		],
    		[
    			137,
    			140
    		],
    		[
    			-30,
    			251
    		],
    		[
    			63,
    			201
    		],
    		[
    			11,
    			612
    		],
    		[
    			217,
    			-135
    		],
    		[
    			124,
    			487
    		],
    		[
    			14,
    			288
    		],
    		[
    			153,
    			496
    		],
    		[
    			-8,
    			338
    		],
    		[
    			359,
    			408
    		],
    		[
    			199,
    			-107
    		],
    		[
    			-23,
    			364
    		],
    		[
    			97,
    			108
    		],
    		[
    			-20,
    			224
    		]
    	],
    	[
    		[
    			77035,
    			67277
    		],
    		[
    			162,
    			44
    		],
    		[
    			93,
    			-348
    		],
    		[
    			121,
    			-141
    		],
    		[
    			8,
    			-452
    		],
    		[
    			-11,
    			-487
    		],
    		[
    			-263,
    			-493
    		],
    		[
    			-33,
    			-701
    		],
    		[
    			293,
    			98
    		],
    		[
    			66,
    			-544
    		],
    		[
    			176,
    			-115
    		],
    		[
    			-81,
    			-490
    		],
    		[
    			206,
    			-222
    		],
    		[
    			121,
    			-109
    		],
    		[
    			203,
    			172
    		],
    		[
    			9,
    			-244
    		]
    	],
    	[
    		[
    			78380,
    			63852
    		],
    		[
    			149,
    			145
    		],
    		[
    			221,
    			-3
    		],
    		[
    			271,
    			68
    		],
    		[
    			236,
    			315
    		],
    		[
    			134,
    			-222
    		],
    		[
    			254,
    			-108
    		],
    		[
    			-44,
    			-340
    		],
    		[
    			132,
    			-240
    		],
    		[
    			280,
    			-154
    		]
    	],
    	[
    		[
    			80013,
    			63313
    		],
    		[
    			-371,
    			-505
    		],
    		[
    			-231,
    			-558
    		],
    		[
    			-61,
    			-410
    		],
    		[
    			212,
    			-623
    		],
    		[
    			260,
    			-772
    		],
    		[
    			252,
    			-365
    		],
    		[
    			169,
    			-475
    		],
    		[
    			127,
    			-1093
    		],
    		[
    			-37,
    			-1039
    		],
    		[
    			-232,
    			-389
    		],
    		[
    			-318,
    			-381
    		],
    		[
    			-227,
    			-492
    		],
    		[
    			-346,
    			-550
    		],
    		[
    			-101,
    			378
    		],
    		[
    			78,
    			401
    		],
    		[
    			-206,
    			335
    		]
    	],
    	[
    		[
    			86327,
    			75524
    		],
    		[
    			0,
    			0
    		]
    	],
    	[
    		[
    			86327,
    			75524
    		],
    		[
    			-106,
    			36
    		],
    		[
    			-120,
    			-200
    		],
    		[
    			-83,
    			-202
    		],
    		[
    			10,
    			-424
    		],
    		[
    			-143,
    			-130
    		],
    		[
    			-50,
    			-105
    		],
    		[
    			-104,
    			-174
    		],
    		[
    			-185,
    			-97
    		],
    		[
    			-121,
    			-159
    		],
    		[
    			-9,
    			-256
    		],
    		[
    			-32,
    			-65
    		],
    		[
    			111,
    			-96
    		],
    		[
    			157,
    			-259
    		]
    	],
    	[
    		[
    			85652,
    			73393
    		],
    		[
    			-40,
    			-143
    		],
    		[
    			-118,
    			-39
    		],
    		[
    			-197,
    			-29
    		],
    		[
    			-108,
    			-266
    		],
    		[
    			-124,
    			21
    		],
    		[
    			-17,
    			-54
    		]
    	],
    	[
    		[
    			85048,
    			72883
    		],
    		[
    			-135,
    			112
    		],
    		[
    			-34,
    			-111
    		],
    		[
    			-81,
    			-49
    		],
    		[
    			-10,
    			112
    		],
    		[
    			-72,
    			54
    		],
    		[
    			-75,
    			94
    		],
    		[
    			76,
    			260
    		],
    		[
    			66,
    			69
    		],
    		[
    			-25,
    			108
    		],
    		[
    			71,
    			319
    		],
    		[
    			-18,
    			96
    		],
    		[
    			-163,
    			65
    		],
    		[
    			-131,
    			158
    		]
    	],
    	[
    		[
    			84517,
    			74170
    		],
    		[
    			227,
    			379
    		],
    		[
    			306,
    			318
    		],
    		[
    			191,
    			419
    		],
    		[
    			131,
    			-185
    		],
    		[
    			241,
    			-22
    		],
    		[
    			-44,
    			312
    		],
    		[
    			429,
    			254
    		],
    		[
    			111,
    			331
    		],
    		[
    			179,
    			-348
    		]
    	],
    	[
    		[
    			85652,
    			73393
    		],
    		[
    			240,
    			-697
    		],
    		[
    			68,
    			-383
    		],
    		[
    			3,
    			-681
    		],
    		[
    			-105,
    			-325
    		],
    		[
    			-252,
    			-113
    		],
    		[
    			-222,
    			-245
    		],
    		[
    			-250,
    			-51
    		],
    		[
    			-31,
    			322
    		],
    		[
    			51,
    			443
    		],
    		[
    			-122,
    			615
    		],
    		[
    			206,
    			99
    		],
    		[
    			-190,
    			506
    		]
    	],
    	[
    		[
    			82410,
    			80055
    		],
    		[
    			-135,
    			-446
    		],
    		[
    			-197,
    			-590
    		],
    		[
    			72,
    			-241
    		],
    		[
    			157,
    			74
    		],
    		[
    			274,
    			-92
    		],
    		[
    			214,
    			219
    		],
    		[
    			223,
    			-189
    		],
    		[
    			251,
    			-413
    		],
    		[
    			-30,
    			-210
    		],
    		[
    			-219,
    			66
    		],
    		[
    			-404,
    			-78
    		],
    		[
    			-195,
    			-168
    		],
    		[
    			-204,
    			-391
    		],
    		[
    			-423,
    			-229
    		],
    		[
    			-277,
    			-313
    		],
    		[
    			-286,
    			120
    		],
    		[
    			-156,
    			53
    		],
    		[
    			-146,
    			-381
    		],
    		[
    			89,
    			-227
    		],
    		[
    			45,
    			-195
    		],
    		[
    			-194,
    			-199
    		],
    		[
    			-200,
    			-316
    		],
    		[
    			-324,
    			-208
    		],
    		[
    			-417,
    			-22
    		],
    		[
    			-448,
    			-205
    		],
    		[
    			-324,
    			-318
    		],
    		[
    			-123,
    			184
    		],
    		[
    			-336,
    			-1
    		],
    		[
    			-411,
    			359
    		],
    		[
    			-274,
    			88
    		],
    		[
    			-369,
    			-82
    		],
    		[
    			-574,
    			133
    		],
    		[
    			-306,
    			-14
    		],
    		[
    			-163,
    			351
    		],
    		[
    			-127,
    			544
    		],
    		[
    			-171,
    			66
    		],
    		[
    			-336,
    			368
    		],
    		[
    			-374,
    			83
    		],
    		[
    			-330,
    			101
    		],
    		[
    			-100,
    			256
    		],
    		[
    			107,
    			690
    		],
    		[
    			-192,
    			476
    		],
    		[
    			-396,
    			222
    		],
    		[
    			-233,
    			313
    		],
    		[
    			-73,
    			413
    		]
    	],
    	[
    		[
    			75742,
    			63602
    		],
    		[
    			-147,
    			937
    		],
    		[
    			-76,
    			-2
    		],
    		[
    			-46,
    			-377
    		],
    		[
    			-152,
    			306
    		],
    		[
    			86,
    			336
    		],
    		[
    			124,
    			34
    		],
    		[
    			128,
    			500
    		],
    		[
    			-160,
    			101
    		],
    		[
    			-257,
    			-8
    		],
    		[
    			-265,
    			81
    		],
    		[
    			-24,
    			410
    		],
    		[
    			-133,
    			30
    		],
    		[
    			-220,
    			255
    		],
    		[
    			-98,
    			-401
    		],
    		[
    			200,
    			-313
    		],
    		[
    			-173,
    			-220
    		],
    		[
    			-62,
    			-215
    		],
    		[
    			171,
    			-159
    		],
    		[
    			-47,
    			-356
    		],
    		[
    			96,
    			-444
    		],
    		[
    			43,
    			-486
    		]
    	],
    	[
    		[
    			74730,
    			63611
    		],
    		[
    			-39,
    			-216
    		],
    		[
    			-189,
    			7
    		],
    		[
    			-343,
    			-122
    		],
    		[
    			16,
    			-445
    		],
    		[
    			-148,
    			-349
    		],
    		[
    			-400,
    			-398
    		],
    		[
    			-311,
    			-695
    		],
    		[
    			-209,
    			-373
    		],
    		[
    			-276,
    			-387
    		],
    		[
    			-1,
    			-271
    		],
    		[
    			-138,
    			-146
    		],
    		[
    			-251,
    			-212
    		],
    		[
    			-129,
    			-31
    		],
    		[
    			-84,
    			-450
    		],
    		[
    			58,
    			-769
    		],
    		[
    			15,
    			-490
    		],
    		[
    			-118,
    			-561
    		],
    		[
    			-1,
    			-1004
    		],
    		[
    			-144,
    			-29
    		],
    		[
    			-126,
    			-450
    		],
    		[
    			84,
    			-195
    		],
    		[
    			-253,
    			-168
    		],
    		[
    			-93,
    			-401
    		],
    		[
    			-112,
    			-170
    		],
    		[
    			-263,
    			552
    		],
    		[
    			-128,
    			827
    		],
    		[
    			-107,
    			596
    		],
    		[
    			-97,
    			279
    		],
    		[
    			-148,
    			568
    		],
    		[
    			-69,
    			739
    		],
    		[
    			-48,
    			369
    		],
    		[
    			-253,
    			811
    		],
    		[
    			-115,
    			1145
    		],
    		[
    			-83,
    			756
    		],
    		[
    			1,
    			716
    		],
    		[
    			-54,
    			553
    		],
    		[
    			-404,
    			-353
    		],
    		[
    			-196,
    			70
    		],
    		[
    			-362,
    			716
    		],
    		[
    			133,
    			214
    		],
    		[
    			-82,
    			232
    		],
    		[
    			-326,
    			501
    		]
    	],
    	[
    		[
    			68937,
    			64577
    		],
    		[
    			185,
    			395
    		],
    		[
    			612,
    			-2
    		],
    		[
    			-56,
    			507
    		],
    		[
    			-156,
    			300
    		],
    		[
    			-31,
    			455
    		],
    		[
    			-182,
    			265
    		],
    		[
    			306,
    			619
    		],
    		[
    			323,
    			-45
    		],
    		[
    			290,
    			620
    		],
    		[
    			174,
    			599
    		],
    		[
    			270,
    			593
    		],
    		[
    			-4,
    			421
    		],
    		[
    			236,
    			342
    		],
    		[
    			-224,
    			292
    		],
    		[
    			-96,
    			400
    		],
    		[
    			-99,
    			517
    		],
    		[
    			137,
    			255
    		],
    		[
    			421,
    			-144
    		],
    		[
    			310,
    			88
    		],
    		[
    			268,
    			496
    		]
    	],
    	[
    		[
    			71621,
    			71550
    		],
    		[
    			298,
    			-692
    		],
    		[
    			-28,
    			-482
    		],
    		[
    			111,
    			-303
    		],
    		[
    			-9,
    			-301
    		],
    		[
    			-200,
    			79
    		],
    		[
    			78,
    			-651
    		],
    		[
    			273,
    			-374
    		],
    		[
    			386,
    			-413
    		]
    	],
    	[
    		[
    			72530,
    			68413
    		],
    		[
    			-176,
    			-268
    		],
    		[
    			-108,
    			-553
    		],
    		[
    			269,
    			-224
    		],
    		[
    			262,
    			-289
    		],
    		[
    			362,
    			-332
    		],
    		[
    			381,
    			-76
    		],
    		[
    			160,
    			-301
    		],
    		[
    			215,
    			-56
    		],
    		[
    			334,
    			-138
    		],
    		[
    			231,
    			10
    		],
    		[
    			32,
    			234
    		],
    		[
    			-36,
    			375
    		],
    		[
    			21,
    			255
    		]
    	],
    	[
    		[
    			74477,
    			67050
    		],
    		[
    			170,
    			124
    		],
    		[
    			23,
    			-465
    		]
    	],
    	[
    		[
    			74670,
    			66709
    		],
    		[
    			6,
    			-119
    		],
    		[
    			252,
    			-224
    		],
    		[
    			175,
    			92
    		],
    		[
    			234,
    			-39
    		],
    		[
    			227,
    			17
    		],
    		[
    			20,
    			363
    		],
    		[
    			-113,
    			189
    		]
    	],
    	[
    		[
    			75471,
    			66988
    		],
    		[
    			224,
    			74
    		],
    		[
    			252,
    			439
    		],
    		[
    			321,
    			376
    		],
    		[
    			233,
    			-145
    		],
    		[
    			198,
    			249
    		],
    		[
    			130,
    			-367
    		],
    		[
    			-94,
    			-248
    		],
    		[
    			300,
    			-89
    		]
    	],
    	[
    		[
    			75657,
    			62792
    		],
    		[
    			-79,
    			308
    		],
    		[
    			-16,
    			301
    		],
    		[
    			-53,
    			285
    		],
    		[
    			-116,
    			344
    		],
    		[
    			-256,
    			23
    		],
    		[
    			25,
    			-243
    		],
    		[
    			-87,
    			-329
    		],
    		[
    			-118,
    			120
    		],
    		[
    			-41,
    			-108
    		],
    		[
    			-78,
    			65
    		],
    		[
    			-108,
    			53
    		]
    	],
    	[
    		[
    			74670,
    			66709
    		],
    		[
    			184,
    			439
    		],
    		[
    			150,
    			150
    		],
    		[
    			198,
    			-137
    		],
    		[
    			147,
    			-14
    		],
    		[
    			122,
    			-159
    		]
    	],
    	[
    		[
    			72530,
    			68413
    		],
    		[
    			115,
    			141
    		],
    		[
    			223,
    			-182
    		],
    		[
    			280,
    			-385
    		],
    		[
    			157,
    			-84
    		],
    		[
    			93,
    			-284
    		],
    		[
    			216,
    			-117
    		],
    		[
    			225,
    			-259
    		],
    		[
    			314,
    			-136
    		],
    		[
    			324,
    			-57
    		]
    	],
    	[
    		[
    			68937,
    			64577
    		],
    		[
    			-203,
    			150
    		],
    		[
    			-83,
    			424
    		],
    		[
    			-215,
    			450
    		],
    		[
    			-512,
    			-111
    		],
    		[
    			-451,
    			-11
    		],
    		[
    			-391,
    			-83
    		]
    	],
    	[
    		[
    			67082,
    			65396
    		],
    		[
    			105,
    			687
    		],
    		[
    			400,
    			305
    		],
    		[
    			-23,
    			272
    		],
    		[
    			-133,
    			96
    		],
    		[
    			-7,
    			520
    		],
    		[
    			-266,
    			260
    		],
    		[
    			-112,
    			357
    		],
    		[
    			-137,
    			310
    		]
    	],
    	[
    		[
    			66909,
    			68203
    		],
    		[
    			465,
    			-301
    		],
    		[
    			278,
    			88
    		],
    		[
    			166,
    			-75
    		],
    		[
    			56,
    			129
    		],
    		[
    			194,
    			-52
    		],
    		[
    			361,
    			246
    		],
    		[
    			10,
    			503
    		],
    		[
    			154,
    			334
    		],
    		[
    			207,
    			-1
    		],
    		[
    			31,
    			166
    		],
    		[
    			212,
    			77
    		],
    		[
    			103,
    			-55
    		],
    		[
    			108,
    			166
    		],
    		[
    			-15,
    			355
    		],
    		[
    			118,
    			356
    		],
    		[
    			177,
    			150
    		],
    		[
    			-110,
    			390
    		],
    		[
    			265,
    			-18
    		],
    		[
    			76,
    			213
    		],
    		[
    			-12,
    			227
    		],
    		[
    			139,
    			248
    		],
    		[
    			-32,
    			294
    		],
    		[
    			-66,
    			250
    		],
    		[
    			163,
    			258
    		],
    		[
    			298,
    			124
    		],
    		[
    			319,
    			68
    		],
    		[
    			141,
    			109
    		],
    		[
    			162,
    			67
    		]
    	],
    	[
    		[
    			70877,
    			72519
    		],
    		[
    			205,
    			-276
    		],
    		[
    			82,
    			-454
    		],
    		[
    			457,
    			-239
    		]
    	],
    	[
    		[
    			68841,
    			72526
    		],
    		[
    			85,
    			-72
    		],
    		[
    			201,
    			189
    		],
    		[
    			93,
    			-114
    		],
    		[
    			90,
    			271
    		],
    		[
    			166,
    			-12
    		],
    		[
    			43,
    			86
    		],
    		[
    			29,
    			239
    		],
    		[
    			120,
    			205
    		],
    		[
    			150,
    			-134
    		],
    		[
    			-30,
    			-181
    		],
    		[
    			84,
    			-28
    		],
    		[
    			-26,
    			-496
    		],
    		[
    			110,
    			-194
    		],
    		[
    			97,
    			125
    		],
    		[
    			123,
    			58
    		],
    		[
    			173,
    			265
    		],
    		[
    			192,
    			-44
    		],
    		[
    			286,
    			-1
    		]
    	],
    	[
    		[
    			70827,
    			72688
    		],
    		[
    			50,
    			-169
    		]
    	],
    	[
    		[
    			66909,
    			68203
    		],
    		[
    			252,
    			536
    		],
    		[
    			-23,
    			380
    		],
    		[
    			-210,
    			100
    		],
    		[
    			-22,
    			375
    		],
    		[
    			-91,
    			472
    		],
    		[
    			119,
    			323
    		],
    		[
    			-121,
    			87
    		],
    		[
    			76,
    			430
    		],
    		[
    			113,
    			736
    		]
    	],
    	[
    		[
    			67002,
    			71642
    		],
    		[
    			284,
    			-224
    		],
    		[
    			209,
    			79
    		],
    		[
    			58,
    			268
    		],
    		[
    			219,
    			89
    		],
    		[
    			157,
    			180
    		],
    		[
    			55,
    			472
    		],
    		[
    			234,
    			114
    		],
    		[
    			44,
    			211
    		],
    		[
    			131,
    			-158
    		],
    		[
    			84,
    			-19
    		]
    	],
    	[
    		[
    			69725,
    			74357
    		],
    		[
    			-101,
    			-182
    		],
    		[
    			-303,
    			98
    		],
    		[
    			-26,
    			-340
    		],
    		[
    			301,
    			46
    		],
    		[
    			343,
    			-192
    		],
    		[
    			526,
    			89
    		]
    	],
    	[
    		[
    			70465,
    			73876
    		],
    		[
    			70,
    			-546
    		],
    		[
    			91,
    			59
    		],
    		[
    			169,
    			-134
    		],
    		[
    			-10,
    			-230
    		],
    		[
    			42,
    			-337
    		]
    	],
    	[
    		[
    			72294,
    			75601
    		],
    		[
    			-39,
    			-134
    		],
    		[
    			-438,
    			-320
    		],
    		[
    			-99,
    			-234
    		],
    		[
    			-356,
    			-70
    		],
    		[
    			-105,
    			-378
    		],
    		[
    			-294,
    			80
    		],
    		[
    			-192,
    			-116
    		],
    		[
    			-266,
    			-279
    		],
    		[
    			39,
    			-138
    		],
    		[
    			-79,
    			-136
    		]
    	],
    	[
    		[
    			67002,
    			71642
    		],
    		[
    			-24,
    			498
    		],
    		[
    			-207,
    			21
    		],
    		[
    			-318,
    			523
    		],
    		[
    			-221,
    			65
    		],
    		[
    			-308,
    			299
    		],
    		[
    			-197,
    			55
    		],
    		[
    			-122,
    			-110
    		],
    		[
    			-186,
    			17
    		],
    		[
    			-197,
    			-338
    		],
    		[
    			-244,
    			-114
    		]
    	],
    	[
    		[
    			64978,
    			72558
    		],
    		[
    			-52,
    			417
    		],
    		[
    			40,
    			618
    		],
    		[
    			-216,
    			200
    		],
    		[
    			71,
    			405
    		],
    		[
    			-184,
    			34
    		],
    		[
    			61,
    			498
    		],
    		[
    			262,
    			-145
    		],
    		[
    			244,
    			189
    		],
    		[
    			-202,
    			355
    		],
    		[
    			-80,
    			338
    		],
    		[
    			-224,
    			-151
    		],
    		[
    			-28,
    			-433
    		],
    		[
    			-87,
    			383
    		]
    	],
    	[
    		[
    			62436,
    			72541
    		],
    		[
    			-152,
    			473
    		],
    		[
    			55,
    			183
    		],
    		[
    			-87,
    			678
    		],
    		[
    			190,
    			168
    		]
    	],
    	[
    		[
    			62442,
    			74043
    		],
    		[
    			44,
    			-223
    		],
    		[
    			141,
    			-273
    		],
    		[
    			190,
    			-78
    		]
    	],
    	[
    		[
    			62817,
    			73469
    		],
    		[
    			101,
    			17
    		]
    	],
    	[
    		[
    			62918,
    			73486
    		],
    		[
    			327,
    			436
    		],
    		[
    			104,
    			44
    		],
    		[
    			82,
    			-174
    		],
    		[
    			-95,
    			-292
    		],
    		[
    			173,
    			-309
    		],
    		[
    			69,
    			29
    		]
    	],
    	[
    		[
    			63578,
    			73220
    		],
    		[
    			88,
    			-436
    		],
    		[
    			263,
    			-123
    		],
    		[
    			193,
    			-296
    		],
    		[
    			395,
    			-102
    		],
    		[
    			434,
    			156
    		],
    		[
    			27,
    			139
    		]
    	],
    	[
    		[
    			67082,
    			65396
    		],
    		[
    			-523,
    			179
    		],
    		[
    			-303,
    			136
    		],
    		[
    			-313,
    			76
    		],
    		[
    			-118,
    			725
    		],
    		[
    			-133,
    			105
    		],
    		[
    			-214,
    			-106
    		],
    		[
    			-280,
    			-286
    		],
    		[
    			-339,
    			196
    		],
    		[
    			-281,
    			454
    		],
    		[
    			-267,
    			168
    		],
    		[
    			-186,
    			561
    		],
    		[
    			-205,
    			788
    		],
    		[
    			-149,
    			-96
    		],
    		[
    			-177,
    			196
    		],
    		[
    			-104,
    			-231
    		]
    	],
    	[
    		[
    			59999,
    			71049
    		],
    		[
    			-26,
    			452
    		],
    		[
    			68,
    			243
    		]
    	],
    	[
    		[
    			60041,
    			71744
    		],
    		[
    			74,
    			129
    		],
    		[
    			75,
    			130
    		],
    		[
    			15,
    			329
    		],
    		[
    			91,
    			-115
    		],
    		[
    			306,
    			165
    		],
    		[
    			147,
    			-112
    		],
    		[
    			229,
    			2
    		],
    		[
    			320,
    			222
    		],
    		[
    			149,
    			-10
    		],
    		[
    			316,
    			92
    		]
    	],
    	[
    		[
    			62817,
    			73469
    		],
    		[
    			-113,
    			342
    		],
    		[
    			1,
    			91
    		],
    		[
    			-123,
    			-2
    		],
    		[
    			-82,
    			159
    		],
    		[
    			-58,
    			-16
    		]
    	],
    	[
    		[
    			62442,
    			74043
    		],
    		[
    			-109,
    			172
    		],
    		[
    			-207,
    			147
    		],
    		[
    			27,
    			288
    		],
    		[
    			-47,
    			208
    		]
    	],
    	[
    		[
    			62106,
    			74858
    		],
    		[
    			386,
    			92
    		]
    	],
    	[
    		[
    			62492,
    			74950
    		],
    		[
    			57,
    			-155
    		],
    		[
    			106,
    			-103
    		],
    		[
    			-56,
    			-148
    		],
    		[
    			148,
    			-202
    		],
    		[
    			-78,
    			-189
    		],
    		[
    			118,
    			-160
    		],
    		[
    			124,
    			-97
    		],
    		[
    			7,
    			-410
    		]
    	],
    	[
    		[
    			55734,
    			91409
    		],
    		[
    			371,
    			-289
    		],
    		[
    			433,
    			-402
    		],
    		[
    			8,
    			-910
    		],
    		[
    			93,
    			-230
    		]
    	],
    	[
    		[
    			56639,
    			89578
    		],
    		[
    			-478,
    			-167
    		],
    		[
    			-269,
    			-413
    		],
    		[
    			43,
    			-361
    		],
    		[
    			-441,
    			-475
    		],
    		[
    			-537,
    			-509
    		],
    		[
    			-202,
    			-832
    		],
    		[
    			198,
    			-416
    		],
    		[
    			265,
    			-328
    		],
    		[
    			-255,
    			-666
    		],
    		[
    			-289,
    			-138
    		],
    		[
    			-106,
    			-992
    		],
    		[
    			-157,
    			-554
    		],
    		[
    			-337,
    			57
    		],
    		[
    			-158,
    			-468
    		],
    		[
    			-321,
    			-27
    		],
    		[
    			-89,
    			558
    		],
    		[
    			-232,
    			671
    		],
    		[
    			-211,
    			835
    		]
    	],
    	[
    		[
    			58829,
    			81362
    		],
    		[
    			-239,
    			-35
    		],
    		[
    			-85,
    			-129
    		],
    		[
    			-18,
    			-298
    		],
    		[
    			-111,
    			57
    		],
    		[
    			-250,
    			-28
    		],
    		[
    			-73,
    			138
    		],
    		[
    			-104,
    			-103
    		],
    		[
    			-105,
    			86
    		],
    		[
    			-218,
    			12
    		],
    		[
    			-310,
    			141
    		],
    		[
    			-281,
    			47
    		],
    		[
    			-215,
    			-14
    		],
    		[
    			-152,
    			-160
    		],
    		[
    			-133,
    			-23
    		]
    	],
    	[
    		[
    			56535,
    			81053
    		],
    		[
    			-6,
    			263
    		],
    		[
    			-85,
    			274
    		],
    		[
    			166,
    			121
    		],
    		[
    			2,
    			235
    		],
    		[
    			-77,
    			225
    		],
    		[
    			-12,
    			261
    		]
    	],
    	[
    		[
    			56523,
    			82432
    		],
    		[
    			268,
    			-4
    		],
    		[
    			302,
    			223
    		],
    		[
    			64,
    			333
    		],
    		[
    			228,
    			190
    		],
    		[
    			-26,
    			264
    		]
    	],
    	[
    		[
    			57359,
    			83438
    		],
    		[
    			169,
    			100
    		],
    		[
    			298,
    			228
    		]
    	],
    	[
    		[
    			60617,
    			78409
    		],
    		[
    			-222,
    			-48
    		],
    		[
    			-185,
    			-191
    		],
    		[
    			-260,
    			-31
    		],
    		[
    			-239,
    			-220
    		],
    		[
    			14,
    			-317
    		]
    	],
    	[
    		[
    			59287,
    			77741
    		],
    		[
    			-38,
    			64
    		],
    		[
    			-432,
    			149
    		],
    		[
    			-19,
    			221
    		],
    		[
    			-257,
    			-73
    		],
    		[
    			-103,
    			-325
    		],
    		[
    			-215,
    			-437
    		]
    	],
    	[
    		[
    			58223,
    			77340
    		],
    		[
    			-126,
    			101
    		],
    		[
    			-131,
    			-95
    		],
    		[
    			-124,
    			109
    		]
    	],
    	[
    		[
    			57842,
    			77455
    		],
    		[
    			70,
    			64
    		],
    		[
    			49,
    			203
    		],
    		[
    			76,
    			188
    		],
    		[
    			-20,
    			106
    		],
    		[
    			58,
    			47
    		],
    		[
    			27,
    			-81
    		],
    		[
    			164,
    			-18
    		],
    		[
    			74,
    			44
    		],
    		[
    			-52,
    			60
    		],
    		[
    			19,
    			88
    		],
    		[
    			-97,
    			150
    		],
    		[
    			-40,
    			247
    		],
    		[
    			-101,
    			97
    		],
    		[
    			20,
    			200
    		],
    		[
    			-125,
    			159
    		],
    		[
    			-115,
    			22
    		],
    		[
    			-204,
    			184
    		],
    		[
    			-185,
    			-58
    		],
    		[
    			-66,
    			-87
    		]
    	],
    	[
    		[
    			57394,
    			79070
    		],
    		[
    			-118,
    			0
    		],
    		[
    			-69,
    			-139
    		],
    		[
    			-205,
    			-56
    		],
    		[
    			-95,
    			-91
    		],
    		[
    			-129,
    			144
    		],
    		[
    			-178,
    			3
    		],
    		[
    			-172,
    			65
    		],
    		[
    			-120,
    			-127
    		]
    	],
    	[
    		[
    			56308,
    			78869
    		],
    		[
    			-19,
    			159
    		],
    		[
    			-155,
    			161
    		]
    	],
    	[
    		[
    			56134,
    			79189
    		],
    		[
    			55,
    			238
    		],
    		[
    			77,
    			154
    		]
    	],
    	[
    		[
    			56266,
    			79581
    		],
    		[
    			60,
    			-35
    		],
    		[
    			-71,
    			266
    		],
    		[
    			252,
    			491
    		],
    		[
    			138,
    			69
    		],
    		[
    			29,
    			166
    		],
    		[
    			-139,
    			515
    		]
    	],
    	[
    		[
    			56266,
    			79581
    		],
    		[
    			-264,
    			227
    		],
    		[
    			-200,
    			-84
    		],
    		[
    			-131,
    			61
    		],
    		[
    			-165,
    			-127
    		],
    		[
    			-140,
    			210
    		],
    		[
    			-114,
    			-81
    		],
    		[
    			-16,
    			36
    		]
    	],
    	[
    		[
    			55236,
    			79823
    		],
    		[
    			-127,
    			291
    		],
    		[
    			-207,
    			36
    		],
    		[
    			-26,
    			185
    		],
    		[
    			-191,
    			66
    		],
    		[
    			-41,
    			-153
    		],
    		[
    			-151,
    			122
    		],
    		[
    			17,
    			163
    		],
    		[
    			-207,
    			51
    		],
    		[
    			-132,
    			191
    		]
    	],
    	[
    		[
    			54171,
    			80775
    		],
    		[
    			-114,
    			377
    		],
    		[
    			22,
    			204
    		],
    		[
    			-69,
    			316
    		],
    		[
    			-101,
    			210
    		],
    		[
    			77,
    			158
    		],
    		[
    			-64,
    			300
    		]
    	],
    	[
    		[
    			53922,
    			82340
    		],
    		[
    			189,
    			174
    		],
    		[
    			434,
    			273
    		],
    		[
    			350,
    			200
    		],
    		[
    			277,
    			-100
    		],
    		[
    			21,
    			-144
    		],
    		[
    			268,
    			-7
    		]
    	],
    	[
    		[
    			56314,
    			82678
    		],
    		[
    			142,
    			-64
    		],
    		[
    			67,
    			-182
    		]
    	],
    	[
    		[
    			54716,
    			79012
    		],
    		[
    			-21,
    			-241
    		],
    		[
    			-156,
    			-2
    		],
    		[
    			53,
    			-128
    		],
    		[
    			-92,
    			-380
    		]
    	],
    	[
    		[
    			54500,
    			78261
    		],
    		[
    			-53,
    			-100
    		],
    		[
    			-243,
    			-14
    		],
    		[
    			-140,
    			-134
    		],
    		[
    			-229,
    			45
    		]
    	],
    	[
    		[
    			53835,
    			78058
    		],
    		[
    			-398,
    			153
    		],
    		[
    			-62,
    			205
    		],
    		[
    			-274,
    			-102
    		],
    		[
    			-32,
    			-113
    		],
    		[
    			-169,
    			84
    		]
    	],
    	[
    		[
    			52900,
    			78285
    		],
    		[
    			-142,
    			16
    		],
    		[
    			-125,
    			108
    		],
    		[
    			42,
    			145
    		],
    		[
    			-10,
    			104
    		]
    	],
    	[
    		[
    			52665,
    			78658
    		],
    		[
    			83,
    			33
    		],
    		[
    			141,
    			-164
    		],
    		[
    			39,
    			156
    		],
    		[
    			245,
    			-25
    		],
    		[
    			199,
    			106
    		],
    		[
    			133,
    			-18
    		],
    		[
    			87,
    			-121
    		],
    		[
    			26,
    			100
    		],
    		[
    			-40,
    			385
    		],
    		[
    			100,
    			75
    		],
    		[
    			98,
    			272
    		]
    	],
    	[
    		[
    			53776,
    			79457
    		],
    		[
    			206,
    			-190
    		],
    		[
    			157,
    			242
    		],
    		[
    			98,
    			44
    		],
    		[
    			215,
    			-180
    		],
    		[
    			131,
    			30
    		],
    		[
    			128,
    			-111
    		]
    	],
    	[
    		[
    			54711,
    			79292
    		],
    		[
    			-23,
    			-75
    		],
    		[
    			28,
    			-205
    		]
    	],
    	[
    		[
    			56308,
    			78869
    		],
    		[
    			-170,
    			-123
    		],
    		[
    			-131,
    			-401
    		],
    		[
    			-168,
    			-401
    		],
    		[
    			-223,
    			-111
    		]
    	],
    	[
    		[
    			55616,
    			77833
    		],
    		[
    			-173,
    			26
    		],
    		[
    			-213,
    			-155
    		]
    	],
    	[
    		[
    			55230,
    			77704
    		],
    		[
    			-104,
    			-89
    		],
    		[
    			-229,
    			114
    		],
    		[
    			-208,
    			253
    		],
    		[
    			-88,
    			73
    		]
    	],
    	[
    		[
    			54601,
    			78055
    		],
    		[
    			-54,
    			200
    		],
    		[
    			-47,
    			6
    		]
    	],
    	[
    		[
    			54716,
    			79012
    		],
    		[
    			141,
    			-151
    		],
    		[
    			103,
    			-65
    		],
    		[
    			233,
    			73
    		],
    		[
    			22,
    			118
    		],
    		[
    			111,
    			18
    		],
    		[
    			135,
    			92
    		],
    		[
    			30,
    			-38
    		],
    		[
    			130,
    			74
    		],
    		[
    			66,
    			139
    		],
    		[
    			91,
    			36
    		],
    		[
    			297,
    			-180
    		],
    		[
    			59,
    			61
    		]
    	],
    	[
    		[
    			57842,
    			77455
    		],
    		[
    			-50,
    			270
    		],
    		[
    			30,
    			252
    		],
    		[
    			-9,
    			259
    		],
    		[
    			-160,
    			352
    		],
    		[
    			-89,
    			249
    		],
    		[
    			-86,
    			175
    		],
    		[
    			-84,
    			58
    		]
    	],
    	[
    		[
    			58223,
    			77340
    		],
    		[
    			6,
    			-152
    		],
    		[
    			-135,
    			-128
    		],
    		[
    			-84,
    			56
    		],
    		[
    			-78,
    			-713
    		]
    	],
    	[
    		[
    			57932,
    			76403
    		],
    		[
    			-163,
    			62
    		],
    		[
    			-202,
    			215
    		],
    		[
    			-327,
    			-138
    		],
    		[
    			-138,
    			-150
    		],
    		[
    			-408,
    			31
    		],
    		[
    			-213,
    			92
    		],
    		[
    			-108,
    			-43
    		],
    		[
    			-80,
    			243
    		]
    	],
    	[
    		[
    			56293,
    			76715
    		],
    		[
    			-51,
    			103
    		],
    		[
    			65,
    			99
    		],
    		[
    			-69,
    			74
    		],
    		[
    			-87,
    			-133
    		],
    		[
    			-162,
    			172
    		],
    		[
    			-22,
    			244
    		],
    		[
    			-169,
    			139
    		],
    		[
    			-31,
    			188
    		],
    		[
    			-151,
    			232
    		]
    	],
    	[
    		[
    			55907,
    			83187
    		],
    		[
    			-59,
    			497
    		]
    	],
    	[
    		[
    			55848,
    			83684
    		],
    		[
    			318,
    			181
    		],
    		[
    			466,
    			-38
    		],
    		[
    			273,
    			59
    		],
    		[
    			39,
    			-123
    		],
    		[
    			148,
    			-38
    		],
    		[
    			267,
    			-287
    		]
    	],
    	[
    		[
    			55848,
    			83684
    		],
    		[
    			10,
    			445
    		],
    		[
    			136,
    			371
    		],
    		[
    			262,
    			202
    		],
    		[
    			221,
    			-442
    		],
    		[
    			223,
    			12
    		],
    		[
    			53,
    			453
    		]
    	],
    	[
    		[
    			56753,
    			84725
    		],
    		[
    			237,
    			105
    		],
    		[
    			121,
    			-73
    		],
    		[
    			239,
    			-219
    		],
    		[
    			229,
    			-1
    		]
    	],
    	[
    		[
    			56753,
    			84725
    		],
    		[
    			32,
    			349
    		],
    		[
    			-102,
    			-75
    		],
    		[
    			-176,
    			210
    		],
    		[
    			-24,
    			340
    		],
    		[
    			351,
    			164
    		],
    		[
    			350,
    			86
    		],
    		[
    			301,
    			-97
    		],
    		[
    			287,
    			17
    		]
    	],
    	[
    		[
    			54171,
    			80775
    		],
    		[
    			-124,
    			-62
    		],
    		[
    			-73,
    			68
    		],
    		[
    			-70,
    			-113
    		],
    		[
    			-200,
    			-114
    		],
    		[
    			-103,
    			-147
    		],
    		[
    			-202,
    			-129
    		],
    		[
    			49,
    			-176
    		],
    		[
    			30,
    			-249
    		],
    		[
    			141,
    			-142
    		],
    		[
    			157,
    			-254
    		]
    	],
    	[
    		[
    			52665,
    			78658
    		],
    		[
    			-298,
    			181
    		],
    		[
    			-57,
    			-128
    		],
    		[
    			-236,
    			4
    		]
    	],
    	[
    		[
    			51718,
    			79804
    		],
    		[
    			16,
    			259
    		],
    		[
    			-56,
    			133
    		]
    	],
    	[
    		[
    			51678,
    			80196
    		],
    		[
    			32,
    			400
    		]
    	],
    	[
    		[
    			51710,
    			80596
    		],
    		[
    			-47,
    			619
    		],
    		[
    			167,
    			0
    		],
    		[
    			70,
    			222
    		],
    		[
    			69,
    			541
    		],
    		[
    			-51,
    			200
    		]
    	],
    	[
    		[
    			51918,
    			82178
    		],
    		[
    			54,
    			125
    		],
    		[
    			232,
    			32
    		],
    		[
    			52,
    			-130
    		],
    		[
    			188,
    			291
    		],
    		[
    			-63,
    			222
    		],
    		[
    			-13,
    			335
    		]
    	],
    	[
    		[
    			52368,
    			83053
    		],
    		[
    			210,
    			-78
    		],
    		[
    			178,
    			90
    		]
    	],
    	[
    		[
    			52756,
    			83065
    		],
    		[
    			4,
    			-228
    		],
    		[
    			281,
    			-138
    		],
    		[
    			-3,
    			-210
    		],
    		[
    			283,
    			111
    		],
    		[
    			156,
    			162
    		],
    		[
    			313,
    			-233
    		],
    		[
    			132,
    			-189
    		]
    	],
    	[
    		[
    			57932,
    			76403
    		],
    		[
    			-144,
    			-245
    		],
    		[
    			-101,
    			-422
    		],
    		[
    			89,
    			-337
    		]
    	],
    	[
    		[
    			57776,
    			75399
    		],
    		[
    			-239,
    			79
    		],
    		[
    			-283,
    			-186
    		]
    	],
    	[
    		[
    			57254,
    			75292
    		],
    		[
    			-3,
    			-294
    		],
    		[
    			-252,
    			-56
    		],
    		[
    			-196,
    			206
    		],
    		[
    			-222,
    			-162
    		],
    		[
    			-206,
    			17
    		]
    	],
    	[
    		[
    			56375,
    			75003
    		],
    		[
    			-20,
    			391
    		],
    		[
    			-139,
    			189
    		]
    	],
    	[
    		[
    			56216,
    			75583
    		],
    		[
    			46,
    			84
    		],
    		[
    			-30,
    			70
    		],
    		[
    			47,
    			188
    		],
    		[
    			105,
    			185
    		],
    		[
    			-135,
    			255
    		],
    		[
    			-24,
    			216
    		],
    		[
    			68,
    			134
    		]
    	],
    	[
    		[
    			57302,
    			71436
    		],
    		[
    			-35,
    			-175
    		],
    		[
    			-400,
    			-50
    		],
    		[
    			3,
    			98
    		],
    		[
    			-339,
    			115
    		],
    		[
    			52,
    			251
    		],
    		[
    			152,
    			-199
    		],
    		[
    			216,
    			34
    		],
    		[
    			207,
    			-42
    		],
    		[
    			-7,
    			-103
    		],
    		[
    			151,
    			71
    		]
    	],
    	[
    		[
    			57254,
    			75292
    		],
    		[
    			135,
    			-157
    		],
    		[
    			-86,
    			-369
    		],
    		[
    			-66,
    			-67
    		]
    	],
    	[
    		[
    			57237,
    			74699
    		],
    		[
    			-169,
    			17
    		],
    		[
    			-145,
    			56
    		],
    		[
    			-336,
    			-154
    		],
    		[
    			192,
    			-332
    		],
    		[
    			-141,
    			-96
    		],
    		[
    			-154,
    			-1
    		],
    		[
    			-147,
    			305
    		],
    		[
    			-52,
    			-130
    		],
    		[
    			62,
    			-353
    		],
    		[
    			139,
    			-277
    		],
    		[
    			-105,
    			-129
    		],
    		[
    			155,
    			-273
    		],
    		[
    			137,
    			-171
    		],
    		[
    			4,
    			-334
    		],
    		[
    			-257,
    			157
    		],
    		[
    			82,
    			-302
    		],
    		[
    			-176,
    			-62
    		],
    		[
    			105,
    			-521
    		],
    		[
    			-184,
    			-8
    		],
    		[
    			-228,
    			257
    		],
    		[
    			-104,
    			473
    		],
    		[
    			-49,
    			393
    		],
    		[
    			-108,
    			272
    		],
    		[
    			-143,
    			337
    		],
    		[
    			-18,
    			168
    		]
    	],
    	[
    		[
    			55597,
    			73991
    		],
    		[
    			129,
    			287
    		],
    		[
    			16,
    			192
    		],
    		[
    			91,
    			85
    		],
    		[
    			5,
    			155
    		]
    	],
    	[
    		[
    			55838,
    			74710
    		],
    		[
    			182,
    			53
    		],
    		[
    			106,
    			129
    		],
    		[
    			150,
    			-12
    		],
    		[
    			46,
    			103
    		],
    		[
    			53,
    			20
    		]
    	],
    	[
    		[
    			60041,
    			71744
    		],
    		[
    			-102,
    			268
    		],
    		[
    			105,
    			222
    		],
    		[
    			-169,
    			-51
    		],
    		[
    			-233,
    			136
    		],
    		[
    			-191,
    			-340
    		],
    		[
    			-421,
    			-66
    		],
    		[
    			-225,
    			317
    		],
    		[
    			-300,
    			20
    		],
    		[
    			-64,
    			-245
    		],
    		[
    			-192,
    			-70
    		],
    		[
    			-268,
    			314
    		],
    		[
    			-303,
    			-11
    		],
    		[
    			-165,
    			588
    		],
    		[
    			-203,
    			328
    		],
    		[
    			135,
    			459
    		],
    		[
    			-176,
    			283
    		],
    		[
    			308,
    			565
    		],
    		[
    			428,
    			23
    		],
    		[
    			117,
    			449
    		],
    		[
    			529,
    			-78
    		],
    		[
    			334,
    			383
    		],
    		[
    			324,
    			167
    		],
    		[
    			459,
    			13
    		],
    		[
    			485,
    			-417
    		],
    		[
    			399,
    			-228
    		],
    		[
    			323,
    			91
    		],
    		[
    			239,
    			-53
    		],
    		[
    			328,
    			309
    		]
    	],
    	[
    		[
    			61542,
    			75120
    		],
    		[
    			296,
    			28
    		],
    		[
    			268,
    			-290
    		]
    	],
    	[
    		[
    			57776,
    			75399
    		],
    		[
    			33,
    			-228
    		],
    		[
    			243,
    			-190
    		],
    		[
    			-51,
    			-145
    		],
    		[
    			-330,
    			-33
    		],
    		[
    			-118,
    			-182
    		],
    		[
    			-232,
    			-319
    		],
    		[
    			-87,
    			276
    		],
    		[
    			3,
    			121
    		]
    	],
    	[
    		[
    			55597,
    			73991
    		],
    		[
    			-48,
    			41
    		],
    		[
    			-5,
    			130
    		],
    		[
    			-154,
    			199
    		],
    		[
    			-24,
    			281
    		],
    		[
    			23,
    			403
    		],
    		[
    			38,
    			184
    		],
    		[
    			-47,
    			93
    		]
    	],
    	[
    		[
    			55380,
    			75322
    		],
    		[
    			-18,
    			188
    		],
    		[
    			120,
    			291
    		],
    		[
    			18,
    			-111
    		],
    		[
    			75,
    			52
    		]
    	],
    	[
    		[
    			55575,
    			75742
    		],
    		[
    			59,
    			-159
    		],
    		[
    			66,
    			-60
    		],
    		[
    			19,
    			-214
    		]
    	],
    	[
    		[
    			55719,
    			75309
    		],
    		[
    			-35,
    			-201
    		],
    		[
    			39,
    			-254
    		],
    		[
    			115,
    			-144
    		]
    	],
    	[
    		[
    			55230,
    			77704
    		],
    		[
    			67,
    			-229
    		],
    		[
    			89,
    			-169
    		],
    		[
    			-107,
    			-222
    		]
    	],
    	[
    		[
    			55279,
    			77084
    		],
    		[
    			-126,
    			131
    		],
    		[
    			-192,
    			-8
    		],
    		[
    			-239,
    			98
    		],
    		[
    			-130,
    			-13
    		],
    		[
    			-60,
    			-123
    		],
    		[
    			-99,
    			136
    		],
    		[
    			-59,
    			-245
    		],
    		[
    			136,
    			-277
    		],
    		[
    			61,
    			-183
    		],
    		[
    			127,
    			-221
    		],
    		[
    			106,
    			-130
    		],
    		[
    			105,
    			-247
    		],
    		[
    			246,
    			-224
    		]
    	],
    	[
    		[
    			55155,
    			75778
    		],
    		[
    			-31,
    			-100
    		]
    	],
    	[
    		[
    			55124,
    			75678
    		],
    		[
    			-261,
    			218
    		],
    		[
    			-161,
    			213
    		],
    		[
    			-254,
    			176
    		],
    		[
    			-233,
    			434
    		],
    		[
    			56,
    			45
    		],
    		[
    			-127,
    			248
    		],
    		[
    			-5,
    			200
    		],
    		[
    			-179,
    			93
    		],
    		[
    			-85,
    			-255
    		],
    		[
    			-82,
    			198
    		],
    		[
    			6,
    			205
    		],
    		[
    			10,
    			9
    		]
    	],
    	[
    		[
    			53809,
    			77462
    		],
    		[
    			194,
    			-20
    		],
    		[
    			51,
    			100
    		],
    		[
    			94,
    			-97
    		],
    		[
    			109,
    			-11
    		],
    		[
    			-1,
    			165
    		],
    		[
    			97,
    			60
    		],
    		[
    			27,
    			239
    		],
    		[
    			221,
    			157
    		]
    	],
    	[
    		[
    			52900,
    			78285
    		],
    		[
    			-22,
    			-242
    		],
    		[
    			-122,
    			-100
    		],
    		[
    			-206,
    			75
    		],
    		[
    			-60,
    			-239
    		],
    		[
    			-132,
    			-19
    		],
    		[
    			-48,
    			94
    		],
    		[
    			-156,
    			-200
    		],
    		[
    			-134,
    			-28
    		],
    		[
    			-120,
    			126
    		]
    	],
    	[
    		[
    			51576,
    			79843
    		],
    		[
    			30,
    			331
    		],
    		[
    			72,
    			22
    		]
    	],
    	[
    		[
    			50698,
    			80799
    		],
    		[
    			222,
    			117
    		]
    	],
    	[
    		[
    			50920,
    			80916
    		],
    		[
    			204,
    			-47
    		],
    		[
    			257,
    			123
    		],
    		[
    			176,
    			-258
    		],
    		[
    			153,
    			-138
    		]
    	],
    	[
    		[
    			50920,
    			80916
    		],
    		[
    			143,
    			162
    		],
    		[
    			244,
    			869
    		],
    		[
    			380,
    			248
    		],
    		[
    			231,
    			-17
    		]
    	],
    	[
    		[
    			47490,
    			75324
    		],
    		[
    			101,
    			150
    		],
    		[
    			113,
    			86
    		],
    		[
    			70,
    			-289
    		],
    		[
    			164,
    			0
    		],
    		[
    			47,
    			75
    		],
    		[
    			162,
    			-21
    		],
    		[
    			78,
    			-296
    		],
    		[
    			-129,
    			-160
    		],
    		[
    			-3,
    			-461
    		],
    		[
    			-45,
    			-86
    		],
    		[
    			-11,
    			-280
    		],
    		[
    			-120,
    			-48
    		],
    		[
    			111,
    			-355
    		],
    		[
    			-77,
    			-388
    		],
    		[
    			96,
    			-175
    		],
    		[
    			-38,
    			-161
    		],
    		[
    			-103,
    			-222
    		],
    		[
    			23,
    			-195
    		]
    	],
    	[
    		[
    			47929,
    			72498
    		],
    		[
    			-112,
    			-153
    		],
    		[
    			-146,
    			83
    		],
    		[
    			-143,
    			-65
    		],
    		[
    			42,
    			462
    		],
    		[
    			-26,
    			363
    		],
    		[
    			-124,
    			55
    		],
    		[
    			-67,
    			224
    		],
    		[
    			22,
    			386
    		],
    		[
    			111,
    			215
    		],
    		[
    			20,
    			239
    		],
    		[
    			58,
    			355
    		],
    		[
    			-6,
    			250
    		],
    		[
    			-56,
    			212
    		],
    		[
    			-12,
    			200
    		]
    	],
    	[
    		[
    			47490,
    			75324
    		],
    		[
    			14,
    			420
    		],
    		[
    			-114,
    			257
    		],
    		[
    			393,
    			426
    		],
    		[
    			340,
    			-106
    		],
    		[
    			373,
    			3
    		],
    		[
    			296,
    			-101
    		],
    		[
    			230,
    			31
    		],
    		[
    			449,
    			-19
    		]
    	],
    	[
    		[
    			50829,
    			75674
    		],
    		[
    			15,
    			-344
    		],
    		[
    			-263,
    			-393
    		],
    		[
    			-356,
    			-125
    		],
    		[
    			-25,
    			-199
    		],
    		[
    			-171,
    			-327
    		],
    		[
    			-107,
    			-481
    		],
    		[
    			108,
    			-338
    		],
    		[
    			-160,
    			-263
    		],
    		[
    			-60,
    			-384
    		],
    		[
    			-210,
    			-118
    		],
    		[
    			-197,
    			-454
    		],
    		[
    			-352,
    			-9
    		],
    		[
    			-265,
    			11
    		],
    		[
    			-174,
    			-209
    		],
    		[
    			-106,
    			-223
    		],
    		[
    			-136,
    			49
    		],
    		[
    			-103,
    			199
    		],
    		[
    			-79,
    			340
    		],
    		[
    			-259,
    			92
    		]
    	],
    	[
    		[
    			48278,
    			82406
    		],
    		[
    			46,
    			-422
    		],
    		[
    			-210,
    			-528
    		],
    		[
    			-493,
    			-349
    		],
    		[
    			-393,
    			89
    		],
    		[
    			225,
    			617
    		],
    		[
    			-145,
    			601
    		],
    		[
    			378,
    			463
    		],
    		[
    			210,
    			276
    		]
    	],
    	[
    		[
    			47896,
    			83153
    		],
    		[
    			57,
    			-317
    		],
    		[
    			-57,
    			-317
    		],
    		[
    			172,
    			9
    		],
    		[
    			210,
    			-122
    		]
    	],
    	[
    		[
    			96049,
    			38125
    		],
    		[
    			228,
    			-366
    		],
    		[
    			144,
    			-272
    		],
    		[
    			-105,
    			-142
    		],
    		[
    			-153,
    			160
    		],
    		[
    			-199,
    			266
    		],
    		[
    			-179,
    			313
    		],
    		[
    			-184,
    			416
    		],
    		[
    			-38,
    			201
    		],
    		[
    			119,
    			-9
    		],
    		[
    			156,
    			-201
    		],
    		[
    			122,
    			-200
    		],
    		[
    			89,
    			-166
    		]
    	],
    	[
    		[
    			95032,
    			44386
    		],
    		[
    			78,
    			-203
    		],
    		[
    			-194,
    			4
    		],
    		[
    			-106,
    			363
    		],
    		[
    			166,
    			-142
    		],
    		[
    			56,
    			-22
    		]
    	],
    	[
    		[
    			94910,
    			44908
    		],
    		[
    			-42,
    			-109
    		],
    		[
    			-206,
    			512
    		],
    		[
    			-57,
    			353
    		],
    		[
    			94,
    			0
    		],
    		[
    			100,
    			-473
    		],
    		[
    			111,
    			-283
    		]
    	],
    	[
    		[
    			94680,
    			44747
    		],
    		[
    			-108,
    			-14
    		],
    		[
    			-170,
    			60
    		],
    		[
    			-58,
    			91
    		],
    		[
    			17,
    			235
    		],
    		[
    			183,
    			-93
    		],
    		[
    			91,
    			-124
    		],
    		[
    			45,
    			-155
    		]
    	],
    	[
    		[
    			94344,
    			45841
    		],
    		[
    			65,
    			-187
    		],
    		[
    			12,
    			-119
    		],
    		[
    			-218,
    			251
    		],
    		[
    			-152,
    			212
    		],
    		[
    			-104,
    			197
    		],
    		[
    			41,
    			60
    		],
    		[
    			128,
    			-142
    		],
    		[
    			228,
    			-272
    		]
    	],
    	[
    		[
    			93649,
    			46431
    		],
    		[
    			111,
    			-193
    		],
    		[
    			-56,
    			-33
    		],
    		[
    			-121,
    			134
    		],
    		[
    			-114,
    			243
    		],
    		[
    			14,
    			99
    		],
    		[
    			166,
    			-250
    		]
    	],
    	[
    		[
    			99134,
    			26908
    		],
    		[
    			-105,
    			-319
    		],
    		[
    			-138,
    			-404
    		],
    		[
    			-214,
    			-236
    		],
    		[
    			-48,
    			155
    		],
    		[
    			-116,
    			85
    		],
    		[
    			160,
    			486
    		],
    		[
    			-91,
    			326
    		],
    		[
    			-299,
    			236
    		],
    		[
    			8,
    			214
    		],
    		[
    			201,
    			206
    		],
    		[
    			47,
    			455
    		],
    		[
    			-13,
    			382
    		],
    		[
    			-113,
    			396
    		],
    		[
    			8,
    			104
    		],
    		[
    			-133,
    			244
    		],
    		[
    			-218,
    			523
    		],
    		[
    			-117,
    			418
    		],
    		[
    			104,
    			46
    		],
    		[
    			151,
    			-328
    		],
    		[
    			216,
    			-153
    		],
    		[
    			78,
    			-526
    		],
    		[
    			202,
    			-622
    		],
    		[
    			5,
    			403
    		],
    		[
    			126,
    			-161
    		],
    		[
    			41,
    			-447
    		],
    		[
    			224,
    			-192
    		],
    		[
    			188,
    			-48
    		],
    		[
    			158,
    			226
    		],
    		[
    			141,
    			-69
    		],
    		[
    			-67,
    			-524
    		],
    		[
    			-85,
    			-345
    		],
    		[
    			-212,
    			12
    		],
    		[
    			-74,
    			-179
    		],
    		[
    			26,
    			-254
    		],
    		[
    			-41,
    			-110
    		]
    	],
    	[
    		[
    			97129,
    			24846
    		],
    		[
    			238,
    			310
    		],
    		[
    			167,
    			306
    		],
    		[
    			123,
    			441
    		],
    		[
    			106,
    			149
    		],
    		[
    			41,
    			330
    		],
    		[
    			195,
    			273
    		],
    		[
    			61,
    			-251
    		],
    		[
    			63,
    			-244
    		],
    		[
    			198,
    			239
    		],
    		[
    			80,
    			-249
    		],
    		[
    			0,
    			-249
    		],
    		[
    			-103,
    			-274
    		],
    		[
    			-182,
    			-435
    		],
    		[
    			-142,
    			-238
    		],
    		[
    			103,
    			-284
    		],
    		[
    			-214,
    			-7
    		],
    		[
    			-238,
    			-223
    		],
    		[
    			-75,
    			-387
    		],
    		[
    			-157,
    			-597
    		],
    		[
    			-219,
    			-264
    		],
    		[
    			-138,
    			-169
    		],
    		[
    			-256,
    			13
    		],
    		[
    			-180,
    			194
    		],
    		[
    			-302,
    			42
    		],
    		[
    			-46,
    			217
    		],
    		[
    			149,
    			438
    		],
    		[
    			349,
    			583
    		],
    		[
    			179,
    			111
    		],
    		[
    			200,
    			225
    		]
    	],
    	[
    		[
    			91024,
    			26469
    		],
    		[
    			166,
    			-39
    		],
    		[
    			20,
    			-702
    		],
    		[
    			-95,
    			-203
    		],
    		[
    			-29,
    			-476
    		],
    		[
    			-97,
    			162
    		],
    		[
    			-193,
    			-412
    		],
    		[
    			-57,
    			32
    		],
    		[
    			-171,
    			19
    		],
    		[
    			-171,
    			505
    		],
    		[
    			-38,
    			390
    		],
    		[
    			-160,
    			515
    		],
    		[
    			7,
    			271
    		],
    		[
    			181,
    			-52
    		],
    		[
    			269,
    			-204
    		],
    		[
    			151,
    			81
    		],
    		[
    			217,
    			113
    		]
    	],
    	[
    		[
    			85040,
    			31546
    		],
    		[
    			-294,
    			-303
    		],
    		[
    			-241,
    			-137
    		],
    		[
    			-53,
    			-309
    		],
    		[
    			-103,
    			-240
    		],
    		[
    			-236,
    			-15
    		],
    		[
    			-174,
    			-52
    		],
    		[
    			-246,
    			107
    		],
    		[
    			-199,
    			-64
    		],
    		[
    			-191,
    			-27
    		],
    		[
    			-165,
    			-315
    		],
    		[
    			-81,
    			26
    		],
    		[
    			-140,
    			-167
    		],
    		[
    			-133,
    			-187
    		],
    		[
    			-203,
    			23
    		],
    		[
    			-186,
    			0
    		],
    		[
    			-295,
    			377
    		],
    		[
    			-149,
    			113
    		],
    		[
    			6,
    			338
    		],
    		[
    			138,
    			81
    		],
    		[
    			47,
    			134
    		],
    		[
    			-10,
    			212
    		],
    		[
    			34,
    			411
    		],
    		[
    			-31,
    			350
    		],
    		[
    			-147,
    			598
    		],
    		[
    			-45,
    			337
    		],
    		[
    			12,
    			336
    		],
    		[
    			-111,
    			385
    		],
    		[
    			-7,
    			174
    		],
    		[
    			-123,
    			235
    		],
    		[
    			-35,
    			463
    		],
    		[
    			-158,
    			467
    		],
    		[
    			-39,
    			252
    		],
    		[
    			122,
    			-255
    		],
    		[
    			-93,
    			548
    		],
    		[
    			137,
    			-171
    		],
    		[
    			83,
    			-229
    		],
    		[
    			-5,
    			303
    		],
    		[
    			-138,
    			465
    		],
    		[
    			-26,
    			186
    		],
    		[
    			-65,
    			177
    		],
    		[
    			31,
    			341
    		],
    		[
    			56,
    			146
    		],
    		[
    			38,
    			295
    		],
    		[
    			-29,
    			346
    		],
    		[
    			114,
    			425
    		],
    		[
    			21,
    			-450
    		],
    		[
    			118,
    			406
    		],
    		[
    			225,
    			198
    		],
    		[
    			136,
    			252
    		],
    		[
    			212,
    			217
    		],
    		[
    			126,
    			46
    		],
    		[
    			77,
    			-73
    		],
    		[
    			219,
    			220
    		],
    		[
    			168,
    			66
    		],
    		[
    			42,
    			129
    		],
    		[
    			74,
    			54
    		],
    		[
    			153,
    			-14
    		],
    		[
    			292,
    			173
    		],
    		[
    			151,
    			262
    		],
    		[
    			71,
    			316
    		],
    		[
    			163,
    			300
    		],
    		[
    			13,
    			236
    		],
    		[
    			7,
    			321
    		],
    		[
    			194,
    			502
    		],
    		[
    			117,
    			-510
    		],
    		[
    			119,
    			118
    		],
    		[
    			-99,
    			279
    		],
    		[
    			87,
    			287
    		],
    		[
    			122,
    			-128
    		],
    		[
    			34,
    			449
    		],
    		[
    			152,
    			291
    		],
    		[
    			67,
    			233
    		],
    		[
    			140,
    			101
    		],
    		[
    			4,
    			165
    		],
    		[
    			122,
    			-69
    		],
    		[
    			5,
    			148
    		],
    		[
    			122,
    			85
    		],
    		[
    			134,
    			80
    		],
    		[
    			205,
    			-271
    		],
    		[
    			155,
    			-350
    		],
    		[
    			173,
    			-4
    		],
    		[
    			177,
    			-56
    		],
    		[
    			-59,
    			325
    		],
    		[
    			133,
    			473
    		],
    		[
    			126,
    			155
    		],
    		[
    			-44,
    			147
    		],
    		[
    			121,
    			338
    		],
    		[
    			168,
    			208
    		],
    		[
    			142,
    			-70
    		],
    		[
    			234,
    			111
    		],
    		[
    			-5,
    			302
    		],
    		[
    			-204,
    			195
    		],
    		[
    			148,
    			86
    		],
    		[
    			184,
    			-147
    		],
    		[
    			148,
    			-242
    		],
    		[
    			234,
    			-151
    		],
    		[
    			79,
    			60
    		],
    		[
    			172,
    			-182
    		],
    		[
    			162,
    			169
    		],
    		[
    			105,
    			-51
    		],
    		[
    			65,
    			113
    		],
    		[
    			127,
    			-292
    		],
    		[
    			-74,
    			-316
    		],
    		[
    			-105,
    			-239
    		],
    		[
    			-96,
    			-20
    		],
    		[
    			32,
    			-236
    		],
    		[
    			-81,
    			-295
    		],
    		[
    			-99,
    			-291
    		],
    		[
    			20,
    			-166
    		],
    		[
    			221,
    			-327
    		],
    		[
    			214,
    			-189
    		],
    		[
    			143,
    			-204
    		],
    		[
    			201,
    			-350
    		],
    		[
    			78,
    			1
    		],
    		[
    			145,
    			-151
    		],
    		[
    			43,
    			-183
    		],
    		[
    			265,
    			-200
    		],
    		[
    			183,
    			202
    		],
    		[
    			55,
    			317
    		],
    		[
    			56,
    			262
    		],
    		[
    			34,
    			324
    		],
    		[
    			85,
    			470
    		],
    		[
    			-39,
    			286
    		],
    		[
    			20,
    			171
    		],
    		[
    			-32,
    			339
    		],
    		[
    			37,
    			445
    		],
    		[
    			53,
    			120
    		],
    		[
    			-43,
    			197
    		],
    		[
    			67,
    			313
    		],
    		[
    			52,
    			325
    		],
    		[
    			7,
    			168
    		],
    		[
    			104,
    			222
    		],
    		[
    			78,
    			-289
    		],
    		[
    			19,
    			-371
    		],
    		[
    			70,
    			-71
    		],
    		[
    			11,
    			-249
    		],
    		[
    			101,
    			-300
    		],
    		[
    			21,
    			-335
    		],
    		[
    			-10,
    			-214
    		],
    		[
    			100,
    			-464
    		],
    		[
    			179,
    			223
    		],
    		[
    			92,
    			-250
    		],
    		[
    			133,
    			-231
    		],
    		[
    			-29,
    			-262
    		],
    		[
    			60,
    			-506
    		],
    		[
    			42,
    			-295
    		],
    		[
    			70,
    			-72
    		],
    		[
    			75,
    			-505
    		],
    		[
    			-27,
    			-307
    		],
    		[
    			90,
    			-400
    		],
    		[
    			301,
    			-309
    		],
    		[
    			197,
    			-281
    		],
    		[
    			186,
    			-257
    		],
    		[
    			-37,
    			-143
    		],
    		[
    			159,
    			-371
    		],
    		[
    			108,
    			-639
    		],
    		[
    			111,
    			130
    		],
    		[
    			113,
    			-256
    		],
    		[
    			68,
    			91
    		],
    		[
    			48,
    			-626
    		],
    		[
    			197,
    			-363
    		],
    		[
    			129,
    			-226
    		],
    		[
    			217,
    			-478
    		],
    		[
    			78,
    			-475
    		],
    		[
    			7,
    			-337
    		],
    		[
    			-19,
    			-365
    		],
    		[
    			132,
    			-502
    		],
    		[
    			-16,
    			-523
    		],
    		[
    			-48,
    			-274
    		],
    		[
    			-75,
    			-527
    		],
    		[
    			6,
    			-339
    		],
    		[
    			-55,
    			-423
    		],
    		[
    			-123,
    			-538
    		],
    		[
    			-205,
    			-290
    		],
    		[
    			-102,
    			-458
    		],
    		[
    			-93,
    			-292
    		],
    		[
    			-82,
    			-510
    		],
    		[
    			-107,
    			-294
    		],
    		[
    			-70,
    			-442
    		],
    		[
    			-36,
    			-407
    		],
    		[
    			14,
    			-187
    		],
    		[
    			-159,
    			-205
    		],
    		[
    			-311,
    			-22
    		],
    		[
    			-257,
    			-242
    		],
    		[
    			-127,
    			-229
    		],
    		[
    			-168,
    			-254
    		],
    		[
    			-230,
    			262
    		],
    		[
    			-170,
    			104
    		],
    		[
    			43,
    			308
    		],
    		[
    			-152,
    			-112
    		],
    		[
    			-243,
    			-428
    		],
    		[
    			-240,
    			160
    		],
    		[
    			-158,
    			94
    		],
    		[
    			-159,
    			42
    		],
    		[
    			-269,
    			171
    		],
    		[
    			-179,
    			364
    		],
    		[
    			-52,
    			449
    		],
    		[
    			-64,
    			298
    		],
    		[
    			-137,
    			240
    		],
    		[
    			-267,
    			71
    		],
    		[
    			91,
    			287
    		],
    		[
    			-67,
    			438
    		],
    		[
    			-136,
    			-408
    		],
    		[
    			-247,
    			-109
    		],
    		[
    			146,
    			327
    		],
    		[
    			42,
    			341
    		],
    		[
    			107,
    			289
    		],
    		[
    			-22,
    			438
    		],
    		[
    			-226,
    			-504
    		],
    		[
    			-174,
    			-202
    		],
    		[
    			-106,
    			-470
    		],
    		[
    			-217,
    			243
    		],
    		[
    			9,
    			313
    		],
    		[
    			-174,
    			429
    		],
    		[
    			-147,
    			221
    		],
    		[
    			52,
    			137
    		],
    		[
    			-356,
    			358
    		],
    		[
    			-195,
    			17
    		],
    		[
    			-267,
    			287
    		],
    		[
    			-498,
    			-56
    		],
    		[
    			-359,
    			-211
    		],
    		[
    			-317,
    			-197
    		],
    		[
    			-265,
    			39
    		]
    	],
    	[
    		[
    			72718,
    			55024
    		],
    		[
    			-42,
    			-615
    		],
    		[
    			-116,
    			-168
    		],
    		[
    			-242,
    			-135
    		],
    		[
    			-132,
    			470
    		],
    		[
    			-49,
    			849
    		],
    		[
    			126,
    			959
    		],
    		[
    			192,
    			-328
    		],
    		[
    			129,
    			-416
    		],
    		[
    			134,
    			-616
    		]
    	],
    	[
    		[
    			80409,
    			61331
    		],
    		[
    			-228,
    			183
    		],
    		[
    			-8,
    			509
    		],
    		[
    			137,
    			267
    		],
    		[
    			304,
    			166
    		],
    		[
    			159,
    			-14
    		],
    		[
    			62,
    			-226
    		],
    		[
    			-122,
    			-260
    		],
    		[
    			-64,
    			-341
    		],
    		[
    			-240,
    			-284
    		]
    	],
    	[
    		[
    			84517,
    			74170
    		],
    		[
    			-388,
    			-171
    		],
    		[
    			-204,
    			-277
    		],
    		[
    			-300,
    			-161
    		],
    		[
    			148,
    			274
    		],
    		[
    			-58,
    			230
    		],
    		[
    			220,
    			397
    		],
    		[
    			-147,
    			310
    		],
    		[
    			-242,
    			-209
    		],
    		[
    			-314,
    			-411
    		],
    		[
    			-171,
    			-381
    		],
    		[
    			-272,
    			-29
    		],
    		[
    			-142,
    			-275
    		],
    		[
    			147,
    			-400
    		],
    		[
    			227,
    			-97
    		],
    		[
    			9,
    			-265
    		],
    		[
    			220,
    			-173
    		],
    		[
    			311,
    			422
    		],
    		[
    			247,
    			-230
    		],
    		[
    			179,
    			-15
    		],
    		[
    			45,
    			-310
    		],
    		[
    			-393,
    			-165
    		],
    		[
    			-130,
    			-319
    		],
    		[
    			-270,
    			-296
    		],
    		[
    			-142,
    			-414
    		],
    		[
    			299,
    			-325
    		],
    		[
    			109,
    			-581
    		],
    		[
    			169,
    			-541
    		],
    		[
    			189,
    			-454
    		],
    		[
    			-5,
    			-439
    		],
    		[
    			-174,
    			-161
    		],
    		[
    			66,
    			-315
    		],
    		[
    			164,
    			-184
    		],
    		[
    			-43,
    			-481
    		],
    		[
    			-71,
    			-468
    		],
    		[
    			-155,
    			-53
    		],
    		[
    			-203,
    			-640
    		],
    		[
    			-225,
    			-775
    		],
    		[
    			-258,
    			-705
    		],
    		[
    			-382,
    			-545
    		],
    		[
    			-386,
    			-498
    		],
    		[
    			-313,
    			-68
    		],
    		[
    			-170,
    			-262
    		],
    		[
    			-96,
    			192
    		],
    		[
    			-157,
    			-294
    		],
    		[
    			-388,
    			-296
    		],
    		[
    			-294,
    			-90
    		],
    		[
    			-95,
    			-624
    		],
    		[
    			-154,
    			-35
    		],
    		[
    			-73,
    			429
    		],
    		[
    			66,
    			228
    		],
    		[
    			-373,
    			189
    		],
    		[
    			-131,
    			-96
    		]
    	],
    	[
    		[
    			83826,
    			64992
    		],
    		[
    			-167,
    			-947
    		],
    		[
    			-119,
    			-485
    		],
    		[
    			-146,
    			499
    		],
    		[
    			-32,
    			438
    		],
    		[
    			163,
    			581
    		],
    		[
    			223,
    			447
    		],
    		[
    			127,
    			-176
    		],
    		[
    			-49,
    			-357
    		]
    	],
    	[
    		[
    			53835,
    			78058
    		],
    		[
    			-31,
    			-291
    		],
    		[
    			67,
    			-251
    		]
    	],
    	[
    		[
    			53871,
    			77516
    		],
    		[
    			-221,
    			86
    		],
    		[
    			-226,
    			-210
    		],
    		[
    			15,
    			-293
    		],
    		[
    			-34,
    			-168
    		],
    		[
    			91,
    			-301
    		],
    		[
    			261,
    			-298
    		],
    		[
    			140,
    			-488
    		],
    		[
    			309,
    			-476
    		],
    		[
    			217,
    			3
    		],
    		[
    			68,
    			-130
    		],
    		[
    			-78,
    			-118
    		],
    		[
    			249,
    			-214
    		],
    		[
    			204,
    			-178
    		],
    		[
    			238,
    			-308
    		],
    		[
    			29,
    			-111
    		],
    		[
    			-52,
    			-211
    		],
    		[
    			-154,
    			276
    		],
    		[
    			-242,
    			97
    		],
    		[
    			-116,
    			-382
    		],
    		[
    			200,
    			-219
    		],
    		[
    			-33,
    			-309
    		],
    		[
    			-116,
    			-35
    		],
    		[
    			-148,
    			-506
    		],
    		[
    			-116,
    			-46
    		],
    		[
    			1,
    			181
    		],
    		[
    			57,
    			317
    		],
    		[
    			60,
    			126
    		],
    		[
    			-108,
    			342
    		],
    		[
    			-85,
    			298
    		],
    		[
    			-115,
    			74
    		],
    		[
    			-82,
    			255
    		],
    		[
    			-179,
    			107
    		],
    		[
    			-120,
    			238
    		],
    		[
    			-206,
    			38
    		],
    		[
    			-217,
    			267
    		],
    		[
    			-254,
    			384
    		],
    		[
    			-189,
    			340
    		],
    		[
    			-86,
    			585
    		],
    		[
    			-138,
    			68
    		],
    		[
    			-226,
    			195
    		],
    		[
    			-128,
    			-80
    		],
    		[
    			-161,
    			-274
    		],
    		[
    			-115,
    			-43
    		]
    	],
    	[
    		[
    			54100,
    			73116
    		],
    		[
    			211,
    			51
    		],
    		[
    			-100,
    			-465
    		],
    		[
    			41,
    			-183
    		],
    		[
    			-58,
    			-303
    		],
    		[
    			-213,
    			222
    		],
    		[
    			-141,
    			64
    		],
    		[
    			-387,
    			300
    		],
    		[
    			38,
    			304
    		],
    		[
    			325,
    			-54
    		],
    		[
    			284,
    			64
    		]
    	],
    	[
    		[
    			52419,
    			74744
    		],
    		[
    			139,
    			183
    		],
    		[
    			166,
    			-419
    		],
    		[
    			-39,
    			-782
    		],
    		[
    			-126,
    			38
    		],
    		[
    			-113,
    			-197
    		],
    		[
    			-105,
    			156
    		],
    		[
    			-11,
    			713
    		],
    		[
    			-64,
    			338
    		],
    		[
    			153,
    			-30
    		]
    	],
    	[
    		[
    			52368,
    			83053
    		],
    		[
    			-113,
    			328
    		],
    		[
    			-8,
    			604
    		],
    		[
    			46,
    			159
    		],
    		[
    			80,
    			177
    		],
    		[
    			244,
    			37
    		],
    		[
    			98,
    			163
    		],
    		[
    			223,
    			167
    		],
    		[
    			-9,
    			-304
    		],
    		[
    			-82,
    			-192
    		],
    		[
    			33,
    			-166
    		],
    		[
    			151,
    			-89
    		],
    		[
    			-68,
    			-223
    		],
    		[
    			-83,
    			64
    		],
    		[
    			-200,
    			-425
    		],
    		[
    			76,
    			-288
    		]
    	],
    	[
    		[
    			53436,
    			83731
    		],
    		[
    			88,
    			-296
    		],
    		[
    			-166,
    			-478
    		],
    		[
    			-291,
    			333
    		],
    		[
    			-39,
    			246
    		],
    		[
    			408,
    			195
    		]
    	],
    	[
    		[
    			47896,
    			83153
    		],
    		[
    			233,
    			24
    		],
    		[
    			298,
    			-365
    		],
    		[
    			-149,
    			-406
    		]
    	],
    	[
    		[
    			49140,
    			82132
    		],
    		[
    			1,
    			0
    		],
    		[
    			40,
    			343
    		],
    		[
    			-186,
    			364
    		],
    		[
    			-4,
    			8
    		],
    		[
    			-337,
    			104
    		],
    		[
    			-66,
    			160
    		],
    		[
    			101,
    			264
    		],
    		[
    			-92,
    			163
    		],
    		[
    			-149,
    			-279
    		],
    		[
    			-17,
    			569
    		],
    		[
    			-140,
    			301
    		],
    		[
    			101,
    			611
    		],
    		[
    			216,
    			480
    		],
    		[
    			222,
    			-47
    		],
    		[
    			335,
    			49
    		],
    		[
    			-297,
    			-639
    		],
    		[
    			283,
    			81
    		],
    		[
    			304,
    			-3
    		],
    		[
    			-72,
    			-481
    		],
    		[
    			-250,
    			-530
    		],
    		[
    			287,
    			-38
    		],
    		[
    			22,
    			-62
    		],
    		[
    			248,
    			-697
    		],
    		[
    			190,
    			-95
    		],
    		[
    			171,
    			-673
    		],
    		[
    			79,
    			-233
    		],
    		[
    			337,
    			-113
    		],
    		[
    			-34,
    			-378
    		],
    		[
    			-142,
    			-173
    		],
    		[
    			111,
    			-305
    		],
    		[
    			-250,
    			-310
    		],
    		[
    			-371,
    			6
    		],
    		[
    			-473,
    			-163
    		],
    		[
    			-130,
    			116
    		],
    		[
    			-183,
    			-276
    		],
    		[
    			-257,
    			67
    		],
    		[
    			-195,
    			-226
    		],
    		[
    			-148,
    			118
    		],
    		[
    			407,
    			621
    		],
    		[
    			249,
    			127
    		],
    		[
    			-2,
    			1
    		],
    		[
    			-434,
    			98
    		],
    		[
    			-79,
    			235
    		],
    		[
    			291,
    			183
    		],
    		[
    			-152,
    			319
    		],
    		[
    			52,
    			387
    		],
    		[
    			413,
    			-54
    		]
    	],
    	[
    		[
    			45969,
    			89843
    		],
    		[
    			-64,
    			-382
    		],
    		[
    			314,
    			-403
    		],
    		[
    			-361,
    			-451
    		],
    		[
    			-801,
    			-405
    		],
    		[
    			-240,
    			-107
    		],
    		[
    			-365,
    			87
    		],
    		[
    			-775,
    			187
    		],
    		[
    			273,
    			261
    		],
    		[
    			-605,
    			289
    		],
    		[
    			492,
    			114
    		],
    		[
    			-12,
    			174
    		],
    		[
    			-583,
    			137
    		],
    		[
    			188,
    			385
    		],
    		[
    			421,
    			87
    		],
    		[
    			433,
    			-400
    		],
    		[
    			422,
    			321
    		],
    		[
    			349,
    			-167
    		],
    		[
    			453,
    			315
    		],
    		[
    			461,
    			-42
    		]
    	],
    	[
    		[
    			63495,
    			75281
    		],
    		[
    			146,
    			-311
    		],
    		[
    			141,
    			-419
    		],
    		[
    			130,
    			-28
    		],
    		[
    			85,
    			-159
    		],
    		[
    			-228,
    			-47
    		],
    		[
    			-49,
    			-459
    		],
    		[
    			-48,
    			-207
    		],
    		[
    			-101,
    			-138
    		],
    		[
    			7,
    			-293
    		]
    	],
    	[
    		[
    			62492,
    			74950
    		],
    		[
    			68,
    			96
    		],
    		[
    			207,
    			-169
    		],
    		[
    			149,
    			-36
    		],
    		[
    			38,
    			70
    		],
    		[
    			-136,
    			319
    		],
    		[
    			72,
    			82
    		]
    	],
    	[
    		[
    			61542,
    			75120
    		],
    		[
    			42,
    			252
    		],
    		[
    			-70,
    			403
    		],
    		[
    			-160,
    			218
    		],
    		[
    			-154,
    			68
    		],
    		[
    			-102,
    			181
    		]
    	],
    	[
    		[
    			83564,
    			58086
    		],
    		[
    			-142,
    			450
    		],
    		[
    			238,
    			-22
    		],
    		[
    			97,
    			-213
    		],
    		[
    			-74,
    			-510
    		],
    		[
    			-119,
    			295
    		]
    	],
    	[
    		[
    			84051,
    			56477
    		],
    		[
    			70,
    			165
    		],
    		[
    			30,
    			367
    		],
    		[
    			153,
    			35
    		],
    		[
    			-44,
    			-398
    		],
    		[
    			205,
    			570
    		],
    		[
    			-26,
    			-563
    		],
    		[
    			-100,
    			-195
    		],
    		[
    			-87,
    			-373
    		],
    		[
    			-87,
    			-175
    		],
    		[
    			-171,
    			409
    		],
    		[
    			57,
    			158
    		]
    	],
    	[
    		[
    			85104,
    			55551
    		],
    		[
    			28,
    			-392
    		],
    		[
    			16,
    			-332
    		],
    		[
    			-94,
    			-540
    		],
    		[
    			-102,
    			602
    		],
    		[
    			-130,
    			-300
    		],
    		[
    			89,
    			-435
    		],
    		[
    			-79,
    			-277
    		],
    		[
    			-327,
    			343
    		],
    		[
    			-78,
    			428
    		],
    		[
    			84,
    			280
    		],
    		[
    			-176,
    			280
    		],
    		[
    			-87,
    			-245
    		],
    		[
    			-131,
    			23
    		],
    		[
    			-205,
    			-330
    		],
    		[
    			-46,
    			173
    		],
    		[
    			109,
    			498
    		],
    		[
    			175,
    			166
    		],
    		[
    			151,
    			223
    		],
    		[
    			98,
    			-268
    		],
    		[
    			212,
    			162
    		],
    		[
    			45,
    			264
    		],
    		[
    			196,
    			15
    		],
    		[
    			-16,
    			457
    		],
    		[
    			225,
    			-280
    		],
    		[
    			23,
    			-297
    		],
    		[
    			20,
    			-218
    		]
    	],
    	[
    		[
    			82917,
    			56084
    		],
    		[
    			-369,
    			-561
    		],
    		[
    			136,
    			414
    		],
    		[
    			200,
    			364
    		],
    		[
    			167,
    			409
    		],
    		[
    			146,
    			587
    		],
    		[
    			49,
    			-482
    		],
    		[
    			-183,
    			-325
    		],
    		[
    			-146,
    			-406
    		]
    	],
    	[
    		[
    			83982,
    			61347
    		],
    		[
    			-46,
    			-245
    		],
    		[
    			95,
    			-423
    		],
    		[
    			-73,
    			-491
    		],
    		[
    			-164,
    			-196
    		],
    		[
    			-43,
    			-476
    		],
    		[
    			62,
    			-471
    		],
    		[
    			147,
    			-65
    		],
    		[
    			123,
    			70
    		],
    		[
    			347,
    			-328
    		],
    		[
    			-27,
    			-321
    		],
    		[
    			91,
    			-142
    		],
    		[
    			-29,
    			-272
    		],
    		[
    			-216,
    			290
    		],
    		[
    			-103,
    			310
    		],
    		[
    			-71,
    			-217
    		],
    		[
    			-177,
    			354
    		],
    		[
    			-253,
    			-87
    		],
    		[
    			-138,
    			130
    		],
    		[
    			14,
    			244
    		],
    		[
    			87,
    			151
    		],
    		[
    			-83,
    			136
    		],
    		[
    			-36,
    			-213
    		],
    		[
    			-137,
    			340
    		],
    		[
    			-41,
    			257
    		],
    		[
    			-11,
    			566
    		],
    		[
    			112,
    			-195
    		],
    		[
    			29,
    			925
    		],
    		[
    			90,
    			535
    		],
    		[
    			169,
    			-1
    		],
    		[
    			171,
    			-168
    		],
    		[
    			85,
    			153
    		],
    		[
    			26,
    			-150
    		]
    	],
    	[
    		[
    			83899,
    			57324
    		],
    		[
    			-43,
    			282
    		],
    		[
    			166,
    			-183
    		],
    		[
    			177,
    			1
    		],
    		[
    			-5,
    			-247
    		],
    		[
    			-129,
    			-251
    		],
    		[
    			-176,
    			-178
    		],
    		[
    			-10,
    			275
    		],
    		[
    			20,
    			301
    		]
    	],
    	[
    		[
    			84861,
    			57766
    		],
    		[
    			78,
    			-660
    		],
    		[
    			-214,
    			157
    		],
    		[
    			5,
    			-199
    		],
    		[
    			68,
    			-364
    		],
    		[
    			-132,
    			-133
    		],
    		[
    			-11,
    			416
    		],
    		[
    			-84,
    			31
    		],
    		[
    			-43,
    			357
    		],
    		[
    			163,
    			-47
    		],
    		[
    			-4,
    			224
    		],
    		[
    			-169,
    			451
    		],
    		[
    			266,
    			-13
    		],
    		[
    			77,
    			-220
    		]
    	],
    	[
    		[
    			78372,
    			54256
    		],
    		[
    			64,
    			-56
    		],
    		[
    			164,
    			-356
    		],
    		[
    			116,
    			-396
    		],
    		[
    			16,
    			-398
    		],
    		[
    			-29,
    			-269
    		],
    		[
    			27,
    			-203
    		],
    		[
    			20,
    			-349
    		],
    		[
    			98,
    			-163
    		],
    		[
    			109,
    			-523
    		],
    		[
    			-5,
    			-199
    		],
    		[
    			-197,
    			-40
    		],
    		[
    			-263,
    			438
    		],
    		[
    			-329,
    			469
    		],
    		[
    			-32,
    			301
    		],
    		[
    			-161,
    			395
    		],
    		[
    			-38,
    			489
    		],
    		[
    			-100,
    			322
    		],
    		[
    			30,
    			431
    		],
    		[
    			-61,
    			250
    		]
    	],
    	[
    		[
    			80461,
    			51765
    		],
    		[
    			204,
    			-202
    		],
    		[
    			214,
    			110
    		],
    		[
    			56,
    			500
    		],
    		[
    			119,
    			112
    		],
    		[
    			333,
    			128
    		],
    		[
    			199,
    			467
    		],
    		[
    			137,
    			374
    		]
    	],
    	[
    		[
    			81723,
    			53254
    		],
    		[
    			126,
    			-307
    		],
    		[
    			58,
    			202
    		],
    		[
    			133,
    			-19
    		],
    		[
    			16,
    			377
    		],
    		[
    			13,
    			291
    		]
    	],
    	[
    		[
    			82069,
    			53798
    		],
    		[
    			214,
    			411
    		],
    		[
    			140,
    			462
    		],
    		[
    			112,
    			2
    		],
    		[
    			143,
    			-299
    		],
    		[
    			13,
    			-257
    		],
    		[
    			183,
    			-165
    		],
    		[
    			231,
    			-177
    		],
    		[
    			-20,
    			-232
    		],
    		[
    			-186,
    			-29
    		],
    		[
    			50,
    			-289
    		],
    		[
    			-205,
    			-201
    		]
    	],
    	[
    		[
    			81723,
    			53254
    		],
    		[
    			110,
    			221
    		],
    		[
    			236,
    			323
    		]
    	],
    	[
    		[
    			53809,
    			77462
    		],
    		[
    			62,
    			54
    		]
    	],
    	[
    		[
    			57797,
    			86326
    		],
    		[
    			-504,
    			-47
    		],
    		[
    			-489,
    			-216
    		],
    		[
    			-452,
    			-125
    		],
    		[
    			-161,
    			323
    		],
    		[
    			-269,
    			193
    		],
    		[
    			62,
    			582
    		],
    		[
    			-135,
    			533
    		],
    		[
    			133,
    			345
    		],
    		[
    			252,
    			371
    		],
    		[
    			635,
    			640
    		],
    		[
    			185,
    			124
    		],
    		[
    			-28,
    			250
    		],
    		[
    			-387,
    			279
    		]
    	],
    	[
    		[
    			54711,
    			79292
    		],
    		[
    			39,
    			130
    		],
    		[
    			123,
    			-10
    		],
    		[
    			95,
    			61
    		],
    		[
    			7,
    			55
    		],
    		[
    			54,
    			28
    		],
    		[
    			18,
    			134
    		],
    		[
    			64,
    			26
    		],
    		[
    			43,
    			106
    		],
    		[
    			82,
    			1
    		]
    	],
    	[
    		[
    			60669,
    			61213
    		],
    		[
    			161,
    			-684
    		],
    		[
    			77,
    			-542
    		],
    		[
    			152,
    			-288
    		],
    		[
    			379,
    			-558
    		],
    		[
    			154,
    			-336
    		],
    		[
    			151,
    			-341
    		],
    		[
    			87,
    			-203
    		],
    		[
    			136,
    			-178
    		]
    	],
    	[
    		[
    			61966,
    			58083
    		],
    		[
    			-83,
    			-144
    		],
    		[
    			-119,
    			51
    		]
    	],
    	[
    		[
    			61764,
    			57990
    		],
    		[
    			-95,
    			191
    		],
    		[
    			-114,
    			346
    		],
    		[
    			-124,
    			190
    		],
    		[
    			-71,
    			204
    		],
    		[
    			-242,
    			237
    		],
    		[
    			-191,
    			7
    		],
    		[
    			-67,
    			124
    		],
    		[
    			-163,
    			-139
    		],
    		[
    			-168,
    			268
    		],
    		[
    			-87,
    			-441
    		],
    		[
    			-323,
    			124
    		]
    	],
    	[
    		[
    			89411,
    			73729
    		],
    		[
    			-256,
    			-595
    		],
    		[
    			4,
    			-610
    		],
    		[
    			-104,
    			-472
    		],
    		[
    			48,
    			-296
    		],
    		[
    			-145,
    			-416
    		],
    		[
    			-355,
    			-278
    		],
    		[
    			-488,
    			-36
    		],
    		[
    			-396,
    			-675
    		],
    		[
    			-186,
    			227
    		],
    		[
    			-12,
    			442
    		],
    		[
    			-483,
    			-130
    		],
    		[
    			-329,
    			-279
    		],
    		[
    			-325,
    			-11
    		],
    		[
    			282,
    			-435
    		],
    		[
    			-186,
    			-1004
    		],
    		[
    			-179,
    			-248
    		],
    		[
    			-135,
    			229
    		],
    		[
    			69,
    			533
    		],
    		[
    			-176,
    			172
    		],
    		[
    			-113,
    			405
    		],
    		[
    			263,
    			182
    		],
    		[
    			145,
    			371
    		],
    		[
    			280,
    			306
    		],
    		[
    			203,
    			403
    		],
    		[
    			553,
    			177
    		],
    		[
    			297,
    			-121
    		],
    		[
    			291,
    			1050
    		],
    		[
    			185,
    			-282
    		],
    		[
    			408,
    			591
    		],
    		[
    			158,
    			229
    		],
    		[
    			174,
    			723
    		],
    		[
    			-47,
    			664
    		],
    		[
    			117,
    			374
    		],
    		[
    			295,
    			108
    		],
    		[
    			152,
    			-819
    		],
    		[
    			-9,
    			-479
    		]
    	],
    	[
    		[
    			90169,
    			76553
    		],
    		[
    			197,
    			250
    		],
    		[
    			62,
    			-663
    		],
    		[
    			-412,
    			-162
    		],
    		[
    			-244,
    			-587
    		],
    		[
    			-436,
    			404
    		],
    		[
    			-152,
    			-646
    		],
    		[
    			-308,
    			-9
    		],
    		[
    			-39,
    			587
    		],
    		[
    			138,
    			455
    		],
    		[
    			296,
    			33
    		],
    		[
    			81,
    			817
    		],
    		[
    			83,
    			460
    		],
    		[
    			326,
    			-615
    		],
    		[
    			213,
    			-198
    		],
    		[
    			195,
    			-126
    		]
    	],
    	[
    		[
    			86769,
    			70351
    		],
    		[
    			154,
    			352
    		],
    		[
    			158,
    			-68
    		],
    		[
    			114,
    			248
    		],
    		[
    			204,
    			-127
    		],
    		[
    			35,
    			-203
    		],
    		[
    			-156,
    			-357
    		],
    		[
    			-114,
    			189
    		],
    		[
    			-143,
    			-137
    		],
    		[
    			-73,
    			-346
    		],
    		[
    			-181,
    			168
    		],
    		[
    			2,
    			281
    		]
    	],
    	[
    		[
    			64752,
    			60417
    		],
    		[
    			-201,
    			-158
    		],
    		[
    			-54,
    			-263
    		],
    		[
    			-6,
    			-201
    		],
    		[
    			-277,
    			-249
    		],
    		[
    			-444,
    			-276
    		],
    		[
    			-249,
    			-417
    		],
    		[
    			-122,
    			-33
    		],
    		[
    			-83,
    			35
    		],
    		[
    			-163,
    			-245
    		],
    		[
    			-177,
    			-114
    		],
    		[
    			-233,
    			-30
    		],
    		[
    			-70,
    			-34
    		],
    		[
    			-61,
    			-156
    		],
    		[
    			-73,
    			-43
    		],
    		[
    			-43,
    			-150
    		],
    		[
    			-137,
    			13
    		],
    		[
    			-89,
    			-80
    		],
    		[
    			-192,
    			30
    		],
    		[
    			-72,
    			345
    		],
    		[
    			8,
    			323
    		],
    		[
    			-46,
    			174
    		],
    		[
    			-54,
    			437
    		],
    		[
    			-80,
    			243
    		],
    		[
    			56,
    			29
    		],
    		[
    			-29,
    			270
    		],
    		[
    			34,
    			114
    		],
    		[
    			-12,
    			257
    		]
    	],
    	[
    		[
    			61883,
    			60238
    		],
    		[
    			121,
    			189
    		],
    		[
    			-28,
    			249
    		],
    		[
    			74,
    			290
    		],
    		[
    			114,
    			-153
    		],
    		[
    			75,
    			53
    		],
    		[
    			321,
    			14
    		],
    		[
    			50,
    			-59
    		],
    		[
    			269,
    			-60
    		],
    		[
    			106,
    			30
    		],
    		[
    			70,
    			-197
    		],
    		[
    			130,
    			99
    		],
    		[
    			199,
    			620
    		],
    		[
    			259,
    			266
    		],
    		[
    			801,
    			226
    		]
    	],
    	[
    		[
    			63448,
    			67449
    		],
    		[
    			109,
    			-510
    		],
    		[
    			137,
    			-135
    		],
    		[
    			47,
    			-207
    		],
    		[
    			190,
    			-249
    		],
    		[
    			16,
    			-243
    		],
    		[
    			-27,
    			-197
    		],
    		[
    			35,
    			-199
    		],
    		[
    			80,
    			-165
    		],
    		[
    			37,
    			-194
    		],
    		[
    			41,
    			-145
    		]
    	],
    	[
    		[
    			64274,
    			65130
    		],
    		[
    			53,
    			-226
    		]
    	],
    	[
    		[
    			61883,
    			60238
    		],
    		[
    			-37,
    			252
    		],
    		[
    			-83,
    			178
    		],
    		[
    			-22,
    			236
    		],
    		[
    			-143,
    			212
    		],
    		[
    			-148,
    			495
    		],
    		[
    			-79,
    			482
    		],
    		[
    			-192,
    			406
    		],
    		[
    			-124,
    			97
    		],
    		[
    			-184,
    			563
    		],
    		[
    			-32,
    			411
    		],
    		[
    			12,
    			350
    		],
    		[
    			-159,
    			655
    		],
    		[
    			-130,
    			231
    		],
    		[
    			-150,
    			122
    		],
    		[
    			-92,
    			339
    		],
    		[
    			15,
    			133
    		],
    		[
    			-77,
    			306
    		],
    		[
    			-81,
    			132
    		],
    		[
    			-108,
    			440
    		],
    		[
    			-170,
    			476
    		],
    		[
    			-141,
    			406
    		],
    		[
    			-139,
    			-3
    		],
    		[
    			44,
    			325
    		],
    		[
    			12,
    			206
    		],
    		[
    			34,
    			236
    		]
    	],
    	[
    		[
    			36483,
    			4468
    		],
    		[
    			141,
    			0
    		],
    		[
    			414,
    			127
    		],
    		[
    			419,
    			-127
    		],
    		[
    			342,
    			-255
    		],
    		[
    			120,
    			-359
    		],
    		[
    			33,
    			-254
    		],
    		[
    			11,
    			-301
    		],
    		[
    			-430,
    			-186
    		],
    		[
    			-452,
    			-150
    		],
    		[
    			-522,
    			-139
    		],
    		[
    			-582,
    			-116
    		],
    		[
    			-658,
    			35
    		],
    		[
    			-365,
    			197
    		],
    		[
    			49,
    			243
    		],
    		[
    			593,
    			162
    		],
    		[
    			239,
    			197
    		],
    		[
    			174,
    			254
    		],
    		[
    			126,
    			220
    		],
    		[
    			168,
    			209
    		],
    		[
    			180,
    			243
    		]
    	],
    	[
    		[
    			31586,
    			3163
    		],
    		[
    			625,
    			-23
    		],
    		[
    			599,
    			-58
    		],
    		[
    			207,
    			243
    		],
    		[
    			147,
    			208
    		],
    		[
    			288,
    			-243
    		],
    		[
    			-82,
    			-301
    		],
    		[
    			-81,
    			-266
    		],
    		[
    			-582,
    			81
    		],
    		[
    			-621,
    			-35
    		],
    		[
    			-348,
    			197
    		],
    		[
    			0,
    			23
    		],
    		[
    			-152,
    			174
    		]
    	],
    	[
    		[
    			29468,
    			8472
    		],
    		[
    			190,
    			70
    		],
    		[
    			321,
    			-23
    		],
    		[
    			82,
    			301
    		],
    		[
    			16,
    			219
    		],
    		[
    			-6,
    			475
    		],
    		[
    			158,
    			278
    		],
    		[
    			256,
    			93
    		],
    		[
    			147,
    			-220
    		],
    		[
    			65,
    			-220
    		],
    		[
    			120,
    			-267
    		],
    		[
    			92,
    			-254
    		],
    		[
    			76,
    			-267
    		],
    		[
    			33,
    			-266
    		],
    		[
    			-49,
    			-231
    		],
    		[
    			-76,
    			-220
    		],
    		[
    			-326,
    			-81
    		],
    		[
    			-311,
    			-116
    		],
    		[
    			-364,
    			11
    		],
    		[
    			136,
    			232
    		],
    		[
    			-327,
    			-81
    		],
    		[
    			-310,
    			-81
    		],
    		[
    			-212,
    			174
    		],
    		[
    			-16,
    			243
    		],
    		[
    			305,
    			231
    		]
    	],
    	[
    		[
    			21575,
    			8103
    		],
    		[
    			174,
    			104
    		],
    		[
    			353,
    			-81
    		],
    		[
    			403,
    			-46
    		],
    		[
    			305,
    			-81
    		],
    		[
    			304,
    			69
    		],
    		[
    			163,
    			-335
    		],
    		[
    			-217,
    			46
    		],
    		[
    			-337,
    			-23
    		],
    		[
    			-343,
    			23
    		],
    		[
    			-376,
    			-35
    		],
    		[
    			-283,
    			116
    		],
    		[
    			-146,
    			243
    		]
    	],
    	[
    		[
    			15938,
    			7061
    		],
    		[
    			60,
    			197
    		],
    		[
    			332,
    			-104
    		],
    		[
    			359,
    			-93
    		],
    		[
    			332,
    			104
    		],
    		[
    			-158,
    			-208
    		],
    		[
    			-261,
    			-151
    		],
    		[
    			-386,
    			47
    		],
    		[
    			-278,
    			208
    		]
    	],
    	[
    		[
    			14643,
    			7177
    		],
    		[
    			202,
    			127
    		],
    		[
    			277,
    			-139
    		],
    		[
    			425,
    			-231
    		],
    		[
    			-164,
    			23
    		],
    		[
    			-359,
    			58
    		],
    		[
    			-381,
    			162
    		]
    	],
    	[
    		[
    			4524,
    			4144
    		],
    		[
    			169,
    			220
    		],
    		[
    			517,
    			-93
    		],
    		[
    			277,
    			-185
    		],
    		[
    			212,
    			-209
    		],
    		[
    			76,
    			-266
    		],
    		[
    			-533,
    			-81
    		],
    		[
    			-364,
    			208
    		],
    		[
    			-163,
    			209
    		],
    		[
    			-11,
    			35
    		],
    		[
    			-180,
    			162
    		]
    	],
    	[
    		[
    			0,
    			529
    		],
    		[
    			16,
    			-5
    		],
    		[
    			245,
    			344
    		],
    		[
    			501,
    			-185
    		],
    		[
    			32,
    			21
    		],
    		[
    			294,
    			188
    		],
    		[
    			38,
    			-7
    		],
    		[
    			32,
    			-4
    		],
    		[
    			402,
    			-246
    		],
    		[
    			352,
    			246
    		],
    		[
    			63,
    			34
    		],
    		[
    			816,
    			104
    		],
    		[
    			265,
    			-138
    		],
    		[
    			130,
    			-71
    		],
    		[
    			419,
    			-196
    		],
    		[
    			789,
    			-151
    		],
    		[
    			625,
    			-185
    		],
    		[
    			1072,
    			-139
    		],
    		[
    			800,
    			162
    		],
    		[
    			1181,
    			-116
    		],
    		[
    			669,
    			-185
    		],
    		[
    			734,
    			174
    		],
    		[
    			773,
    			162
    		],
    		[
    			60,
    			278
    		],
    		[
    			-1094,
    			23
    		],
    		[
    			-898,
    			139
    		],
    		[
    			-234,
    			231
    		],
    		[
    			-745,
    			128
    		],
    		[
    			49,
    			266
    		],
    		[
    			103,
    			243
    		],
    		[
    			104,
    			220
    		],
    		[
    			-55,
    			243
    		],
    		[
    			-462,
    			162
    		],
    		[
    			-212,
    			209
    		],
    		[
    			-430,
    			185
    		],
    		[
    			675,
    			-35
    		],
    		[
    			642,
    			93
    		],
    		[
    			402,
    			-197
    		],
    		[
    			495,
    			173
    		],
    		[
    			457,
    			220
    		],
    		[
    			223,
    			197
    		],
    		[
    			-98,
    			243
    		],
    		[
    			-359,
    			162
    		],
    		[
    			-408,
    			174
    		],
    		[
    			-571,
    			35
    		],
    		[
    			-500,
    			81
    		],
    		[
    			-539,
    			58
    		],
    		[
    			-180,
    			220
    		],
    		[
    			-359,
    			185
    		],
    		[
    			-217,
    			208
    		],
    		[
    			-87,
    			672
    		],
    		[
    			136,
    			-58
    		],
    		[
    			250,
    			-185
    		],
    		[
    			457,
    			58
    		],
    		[
    			441,
    			81
    		],
    		[
    			228,
    			-255
    		],
    		[
    			441,
    			58
    		],
    		[
    			370,
    			127
    		],
    		[
    			348,
    			162
    		],
    		[
    			315,
    			197
    		],
    		[
    			419,
    			58
    		],
    		[
    			-11,
    			220
    		],
    		[
    			-97,
    			220
    		],
    		[
    			81,
    			208
    		],
    		[
    			359,
    			104
    		],
    		[
    			163,
    			-196
    		],
    		[
    			425,
    			115
    		],
    		[
    			321,
    			151
    		],
    		[
    			397,
    			12
    		],
    		[
    			375,
    			57
    		],
    		[
    			376,
    			139
    		],
    		[
    			299,
    			128
    		],
    		[
    			337,
    			127
    		],
    		[
    			218,
    			-35
    		],
    		[
    			190,
    			-46
    		],
    		[
    			414,
    			81
    		],
    		[
    			370,
    			-104
    		],
    		[
    			381,
    			11
    		],
    		[
    			364,
    			81
    		],
    		[
    			375,
    			-57
    		],
    		[
    			414,
    			-58
    		],
    		[
    			386,
    			23
    		],
    		[
    			403,
    			-12
    		],
    		[
    			413,
    			-11
    		],
    		[
    			381,
    			23
    		],
    		[
    			283,
    			174
    		],
    		[
    			337,
    			92
    		],
    		[
    			349,
    			-127
    		],
    		[
    			331,
    			104
    		],
    		[
    			300,
    			208
    		],
    		[
    			179,
    			-185
    		],
    		[
    			98,
    			-208
    		],
    		[
    			180,
    			-197
    		],
    		[
    			288,
    			174
    		],
    		[
    			332,
    			-220
    		],
    		[
    			375,
    			-70
    		],
    		[
    			321,
    			-162
    		],
    		[
    			392,
    			35
    		],
    		[
    			354,
    			104
    		],
    		[
    			418,
    			-23
    		],
    		[
    			376,
    			-81
    		],
    		[
    			381,
    			-104
    		],
    		[
    			147,
    			254
    		],
    		[
    			-180,
    			197
    		],
    		[
    			-136,
    			209
    		],
    		[
    			-359,
    			46
    		],
    		[
    			-158,
    			220
    		],
    		[
    			-60,
    			220
    		],
    		[
    			-98,
    			440
    		],
    		[
    			213,
    			-81
    		],
    		[
    			364,
    			-35
    		],
    		[
    			359,
    			35
    		],
    		[
    			327,
    			-93
    		],
    		[
    			283,
    			-174
    		],
    		[
    			119,
    			-208
    		],
    		[
    			376,
    			-35
    		],
    		[
    			359,
    			81
    		],
    		[
    			381,
    			116
    		],
    		[
    			342,
    			70
    		],
    		[
    			283,
    			-139
    		],
    		[
    			370,
    			46
    		],
    		[
    			239,
    			451
    		],
    		[
    			224,
    			-266
    		],
    		[
    			321,
    			-104
    		],
    		[
    			348,
    			58
    		],
    		[
    			228,
    			-232
    		],
    		[
    			365,
    			-23
    		],
    		[
    			337,
    			-69
    		],
    		[
    			332,
    			-128
    		],
    		[
    			218,
    			220
    		],
    		[
    			108,
    			209
    		],
    		[
    			278,
    			-232
    		],
    		[
    			381,
    			58
    		],
    		[
    			283,
    			-127
    		],
    		[
    			190,
    			-197
    		],
    		[
    			370,
    			58
    		],
    		[
    			288,
    			127
    		],
    		[
    			283,
    			151
    		],
    		[
    			337,
    			81
    		],
    		[
    			392,
    			69
    		],
    		[
    			354,
    			81
    		],
    		[
    			272,
    			127
    		],
    		[
    			163,
    			186
    		],
    		[
    			65,
    			254
    		],
    		[
    			-32,
    			244
    		],
    		[
    			-87,
    			231
    		],
    		[
    			-98,
    			232
    		],
    		[
    			-87,
    			231
    		],
    		[
    			-71,
    			209
    		],
    		[
    			-16,
    			231
    		],
    		[
    			27,
    			232
    		],
    		[
    			130,
    			220
    		],
    		[
    			109,
    			243
    		],
    		[
    			44,
    			231
    		],
    		[
    			-55,
    			255
    		],
    		[
    			-32,
    			232
    		],
    		[
    			136,
    			266
    		],
    		[
    			152,
    			173
    		],
    		[
    			180,
    			220
    		],
    		[
    			190,
    			186
    		],
    		[
    			223,
    			173
    		],
    		[
    			109,
    			255
    		],
    		[
    			152,
    			162
    		],
    		[
    			174,
    			151
    		],
    		[
    			267,
    			34
    		],
    		[
    			174,
    			186
    		],
    		[
    			196,
    			115
    		],
    		[
    			228,
    			70
    		],
    		[
    			202,
    			150
    		],
    		[
    			157,
    			186
    		],
    		[
    			218,
    			69
    		],
    		[
    			163,
    			-151
    		],
    		[
    			-103,
    			-196
    		],
    		[
    			-283,
    			-174
    		],
    		[
    			-120,
    			-127
    		],
    		[
    			-206,
    			92
    		],
    		[
    			-229,
    			-58
    		],
    		[
    			-190,
    			-139
    		],
    		[
    			-202,
    			-150
    		],
    		[
    			-136,
    			-174
    		],
    		[
    			-38,
    			-231
    		],
    		[
    			17,
    			-220
    		],
    		[
    			130,
    			-197
    		],
    		[
    			-190,
    			-139
    		],
    		[
    			-261,
    			-46
    		],
    		[
    			-153,
    			-197
    		],
    		[
    			-163,
    			-185
    		],
    		[
    			-174,
    			-255
    		],
    		[
    			-44,
    			-220
    		],
    		[
    			98,
    			-243
    		],
    		[
    			147,
    			-185
    		],
    		[
    			229,
    			-139
    		],
    		[
    			212,
    			-185
    		],
    		[
    			114,
    			-232
    		],
    		[
    			60,
    			-220
    		],
    		[
    			82,
    			-232
    		],
    		[
    			130,
    			-196
    		],
    		[
    			82,
    			-220
    		],
    		[
    			38,
    			-544
    		],
    		[
    			81,
    			-220
    		],
    		[
    			22,
    			-232
    		],
    		[
    			87,
    			-231
    		],
    		[
    			-38,
    			-313
    		],
    		[
    			-152,
    			-243
    		],
    		[
    			-163,
    			-197
    		],
    		[
    			-370,
    			-81
    		],
    		[
    			-125,
    			-208
    		],
    		[
    			-169,
    			-197
    		],
    		[
    			-419,
    			-220
    		],
    		[
    			-370,
    			-93
    		],
    		[
    			-348,
    			-127
    		],
    		[
    			-376,
    			-128
    		],
    		[
    			-223,
    			-243
    		],
    		[
    			-446,
    			-23
    		],
    		[
    			-489,
    			23
    		],
    		[
    			-441,
    			-46
    		],
    		[
    			-468,
    			0
    		],
    		[
    			87,
    			-232
    		],
    		[
    			424,
    			-104
    		],
    		[
    			311,
    			-162
    		],
    		[
    			174,
    			-208
    		],
    		[
    			-310,
    			-185
    		],
    		[
    			-479,
    			58
    		],
    		[
    			-397,
    			-151
    		],
    		[
    			-17,
    			-243
    		],
    		[
    			-11,
    			-232
    		],
    		[
    			327,
    			-196
    		],
    		[
    			60,
    			-220
    		],
    		[
    			353,
    			-220
    		],
    		[
    			588,
    			-93
    		],
    		[
    			500,
    			-162
    		],
    		[
    			398,
    			-185
    		],
    		[
    			506,
    			-186
    		],
    		[
    			690,
    			-92
    		],
    		[
    			681,
    			-162
    		],
    		[
    			473,
    			-174
    		],
    		[
    			517,
    			-197
    		],
    		[
    			272,
    			-278
    		],
    		[
    			136,
    			-220
    		],
    		[
    			337,
    			209
    		],
    		[
    			457,
    			173
    		],
    		[
    			484,
    			186
    		],
    		[
    			577,
    			150
    		],
    		[
    			495,
    			162
    		],
    		[
    			691,
    			12
    		],
    		[
    			680,
    			-81
    		],
    		[
    			560,
    			-139
    		],
    		[
    			180,
    			255
    		],
    		[
    			386,
    			173
    		],
    		[
    			702,
    			12
    		],
    		[
    			550,
    			127
    		],
    		[
    			522,
    			128
    		],
    		[
    			577,
    			81
    		],
    		[
    			614,
    			104
    		],
    		[
    			430,
    			150
    		],
    		[
    			-196,
    			209
    		],
    		[
    			-119,
    			208
    		],
    		[
    			0,
    			220
    		],
    		[
    			-539,
    			-23
    		],
    		[
    			-571,
    			-93
    		],
    		[
    			-544,
    			0
    		],
    		[
    			-77,
    			220
    		],
    		[
    			39,
    			440
    		],
    		[
    			125,
    			128
    		],
    		[
    			397,
    			138
    		],
    		[
    			468,
    			139
    		],
    		[
    			337,
    			174
    		],
    		[
    			337,
    			174
    		],
    		[
    			251,
    			231
    		],
    		[
    			380,
    			104
    		],
    		[
    			376,
    			81
    		],
    		[
    			190,
    			47
    		],
    		[
    			430,
    			23
    		],
    		[
    			408,
    			81
    		],
    		[
    			343,
    			116
    		],
    		[
    			337,
    			139
    		],
    		[
    			305,
    			139
    		],
    		[
    			386,
    			185
    		],
    		[
    			245,
    			197
    		],
    		[
    			261,
    			173
    		],
    		[
    			82,
    			232
    		],
    		[
    			-294,
    			139
    		],
    		[
    			98,
    			243
    		],
    		[
    			185,
    			185
    		],
    		[
    			288,
    			116
    		],
    		[
    			305,
    			139
    		],
    		[
    			283,
    			185
    		],
    		[
    			217,
    			232
    		],
    		[
    			136,
    			277
    		],
    		[
    			202,
    			163
    		],
    		[
    			331,
    			-35
    		],
    		[
    			136,
    			-197
    		],
    		[
    			332,
    			-23
    		],
    		[
    			11,
    			220
    		],
    		[
    			142,
    			231
    		],
    		[
    			299,
    			-58
    		],
    		[
    			71,
    			-220
    		],
    		[
    			331,
    			-34
    		],
    		[
    			360,
    			104
    		],
    		[
    			348,
    			69
    		],
    		[
    			315,
    			-34
    		],
    		[
    			120,
    			-243
    		],
    		[
    			305,
    			196
    		],
    		[
    			283,
    			105
    		],
    		[
    			315,
    			81
    		],
    		[
    			310,
    			81
    		],
    		[
    			283,
    			139
    		],
    		[
    			310,
    			92
    		],
    		[
    			240,
    			128
    		],
    		[
    			168,
    			208
    		],
    		[
    			207,
    			-151
    		],
    		[
    			288,
    			81
    		],
    		[
    			202,
    			-277
    		],
    		[
    			157,
    			-209
    		],
    		[
    			316,
    			116
    		],
    		[
    			125,
    			232
    		],
    		[
    			283,
    			162
    		],
    		[
    			365,
    			-35
    		],
    		[
    			108,
    			-220
    		],
    		[
    			229,
    			220
    		],
    		[
    			299,
    			69
    		],
    		[
    			326,
    			23
    		],
    		[
    			294,
    			-11
    		],
    		[
    			310,
    			-70
    		],
    		[
    			300,
    			-34
    		],
    		[
    			130,
    			-197
    		],
    		[
    			180,
    			-174
    		],
    		[
    			304,
    			104
    		],
    		[
    			327,
    			24
    		],
    		[
    			315,
    			0
    		],
    		[
    			310,
    			11
    		],
    		[
    			278,
    			81
    		],
    		[
    			294,
    			70
    		],
    		[
    			245,
    			162
    		],
    		[
    			261,
    			104
    		],
    		[
    			283,
    			58
    		],
    		[
    			212,
    			162
    		],
    		[
    			152,
    			324
    		],
    		[
    			158,
    			197
    		],
    		[
    			288,
    			-93
    		],
    		[
    			109,
    			-208
    		],
    		[
    			239,
    			-139
    		],
    		[
    			289,
    			46
    		],
    		[
    			196,
    			-208
    		],
    		[
    			206,
    			-151
    		],
    		[
    			283,
    			139
    		],
    		[
    			98,
    			255
    		],
    		[
    			250,
    			104
    		],
    		[
    			289,
    			197
    		],
    		[
    			272,
    			81
    		],
    		[
    			326,
    			116
    		],
    		[
    			218,
    			127
    		],
    		[
    			228,
    			139
    		],
    		[
    			218,
    			127
    		],
    		[
    			261,
    			-69
    		],
    		[
    			250,
    			208
    		],
    		[
    			180,
    			162
    		],
    		[
    			261,
    			-11
    		],
    		[
    			229,
    			139
    		],
    		[
    			54,
    			208
    		],
    		[
    			234,
    			162
    		],
    		[
    			228,
    			116
    		],
    		[
    			278,
    			93
    		],
    		[
    			256,
    			46
    		],
    		[
    			244,
    			-35
    		],
    		[
    			262,
    			-58
    		],
    		[
    			223,
    			-162
    		],
    		[
    			27,
    			-254
    		],
    		[
    			245,
    			-197
    		],
    		[
    			168,
    			-162
    		],
    		[
    			332,
    			-70
    		],
    		[
    			185,
    			-162
    		],
    		[
    			229,
    			-162
    		],
    		[
    			266,
    			-35
    		],
    		[
    			223,
    			116
    		],
    		[
    			240,
    			243
    		],
    		[
    			261,
    			-127
    		],
    		[
    			272,
    			-70
    		],
    		[
    			261,
    			-69
    		],
    		[
    			272,
    			-46
    		],
    		[
    			277,
    			0
    		],
    		[
    			229,
    			-614
    		],
    		[
    			-11,
    			-150
    		],
    		[
    			-33,
    			-267
    		],
    		[
    			-266,
    			-150
    		],
    		[
    			-218,
    			-220
    		],
    		[
    			38,
    			-232
    		],
    		[
    			310,
    			12
    		],
    		[
    			-38,
    			-232
    		],
    		[
    			-141,
    			-220
    		],
    		[
    			-131,
    			-243
    		],
    		[
    			212,
    			-185
    		],
    		[
    			321,
    			-58
    		],
    		[
    			321,
    			104
    		],
    		[
    			153,
    			232
    		],
    		[
    			92,
    			220
    		],
    		[
    			153,
    			185
    		],
    		[
    			174,
    			174
    		],
    		[
    			70,
    			208
    		],
    		[
    			147,
    			289
    		],
    		[
    			174,
    			58
    		],
    		[
    			316,
    			24
    		],
    		[
    			277,
    			69
    		],
    		[
    			283,
    			93
    		],
    		[
    			136,
    			231
    		],
    		[
    			82,
    			220
    		],
    		[
    			190,
    			220
    		],
    		[
    			272,
    			151
    		],
    		[
    			234,
    			115
    		],
    		[
    			153,
    			197
    		],
    		[
    			157,
    			104
    		],
    		[
    			202,
    			93
    		],
    		[
    			277,
    			-58
    		],
    		[
    			250,
    			58
    		],
    		[
    			272,
    			69
    		],
    		[
    			305,
    			-34
    		],
    		[
    			201,
    			162
    		],
    		[
    			142,
    			393
    		],
    		[
    			103,
    			-162
    		],
    		[
    			131,
    			-278
    		],
    		[
    			234,
    			-115
    		],
    		[
    			266,
    			-47
    		],
    		[
    			267,
    			70
    		],
    		[
    			283,
    			-46
    		],
    		[
    			261,
    			-12
    		],
    		[
    			174,
    			58
    		],
    		[
    			234,
    			-35
    		],
    		[
    			212,
    			-127
    		],
    		[
    			250,
    			81
    		],
    		[
    			300,
    			0
    		],
    		[
    			255,
    			81
    		],
    		[
    			289,
    			-81
    		],
    		[
    			185,
    			197
    		],
    		[
    			141,
    			196
    		],
    		[
    			191,
    			163
    		],
    		[
    			348,
    			439
    		],
    		[
    			179,
    			-81
    		],
    		[
    			212,
    			-162
    		],
    		[
    			185,
    			-208
    		],
    		[
    			354,
    			-359
    		],
    		[
    			272,
    			-12
    		],
    		[
    			256,
    			0
    		],
    		[
    			299,
    			70
    		],
    		[
    			299,
    			81
    		],
    		[
    			229,
    			162
    		],
    		[
    			190,
    			174
    		],
    		[
    			310,
    			23
    		],
    		[
    			207,
    			127
    		],
    		[
    			218,
    			-116
    		],
    		[
    			141,
    			-185
    		],
    		[
    			196,
    			-185
    		],
    		[
    			305,
    			23
    		],
    		[
    			190,
    			-150
    		],
    		[
    			332,
    			-151
    		],
    		[
    			348,
    			-58
    		],
    		[
    			288,
    			47
    		],
    		[
    			218,
    			185
    		],
    		[
    			185,
    			185
    		],
    		[
    			250,
    			46
    		],
    		[
    			251,
    			-81
    		],
    		[
    			288,
    			-58
    		],
    		[
    			261,
    			93
    		],
    		[
    			250,
    			0
    		],
    		[
    			245,
    			-58
    		],
    		[
    			256,
    			-58
    		],
    		[
    			250,
    			104
    		],
    		[
    			299,
    			93
    		],
    		[
    			283,
    			23
    		],
    		[
    			316,
    			0
    		],
    		[
    			255,
    			58
    		],
    		[
    			251,
    			46
    		],
    		[
    			76,
    			290
    		],
    		[
    			11,
    			243
    		],
    		[
    			174,
    			-162
    		],
    		[
    			49,
    			-266
    		],
    		[
    			92,
    			-244
    		],
    		[
    			115,
    			-196
    		],
    		[
    			234,
    			-105
    		],
    		[
    			315,
    			35
    		],
    		[
    			365,
    			12
    		],
    		[
    			250,
    			35
    		],
    		[
    			364,
    			0
    		],
    		[
    			262,
    			11
    		],
    		[
    			364,
    			-23
    		],
    		[
    			310,
    			-46
    		],
    		[
    			196,
    			-186
    		],
    		[
    			-54,
    			-220
    		],
    		[
    			179,
    			-173
    		],
    		[
    			299,
    			-139
    		],
    		[
    			310,
    			-151
    		],
    		[
    			360,
    			-104
    		],
    		[
    			375,
    			-92
    		],
    		[
    			283,
    			-93
    		],
    		[
    			315,
    			-12
    		],
    		[
    			180,
    			197
    		],
    		[
    			245,
    			-162
    		],
    		[
    			212,
    			-185
    		],
    		[
    			245,
    			-139
    		],
    		[
    			337,
    			-58
    		],
    		[
    			321,
    			-69
    		],
    		[
    			136,
    			-232
    		],
    		[
    			316,
    			-139
    		],
    		[
    			212,
    			-208
    		],
    		[
    			310,
    			-93
    		],
    		[
    			321,
    			12
    		],
    		[
    			299,
    			-35
    		],
    		[
    			332,
    			12
    		],
    		[
    			332,
    			-47
    		],
    		[
    			310,
    			-81
    		],
    		[
    			288,
    			-139
    		],
    		[
    			289,
    			-116
    		],
    		[
    			195,
    			-173
    		],
    		[
    			-32,
    			-232
    		],
    		[
    			-147,
    			-208
    		],
    		[
    			-125,
    			-266
    		],
    		[
    			-98,
    			-209
    		],
    		[
    			-131,
    			-243
    		],
    		[
    			-364,
    			-93
    		],
    		[
    			-163,
    			-208
    		],
    		[
    			-360,
    			-127
    		],
    		[
    			-125,
    			-232
    		],
    		[
    			-190,
    			-220
    		],
    		[
    			-201,
    			-185
    		],
    		[
    			-115,
    			-243
    		],
    		[
    			-70,
    			-220
    		],
    		[
    			-28,
    			-266
    		],
    		[
    			6,
    			-220
    		],
    		[
    			158,
    			-232
    		],
    		[
    			60,
    			-220
    		],
    		[
    			130,
    			-208
    		],
    		[
    			517,
    			-81
    		],
    		[
    			109,
    			-255
    		],
    		[
    			-501,
    			-93
    		],
    		[
    			-424,
    			-127
    		],
    		[
    			-528,
    			-23
    		],
    		[
    			-234,
    			-336
    		],
    		[
    			-49,
    			-278
    		],
    		[
    			-119,
    			-220
    		],
    		[
    			-147,
    			-220
    		],
    		[
    			370,
    			-196
    		],
    		[
    			141,
    			-244
    		],
    		[
    			239,
    			-219
    		],
    		[
    			338,
    			-197
    		],
    		[
    			386,
    			-186
    		],
    		[
    			419,
    			-185
    		],
    		[
    			636,
    			-185
    		],
    		[
    			142,
    			-289
    		],
    		[
    			800,
    			-128
    		],
    		[
    			53,
    			-45
    		],
    		[
    			208,
    			-175
    		],
    		[
    			767,
    			151
    		],
    		[
    			636,
    			-186
    		],
    		[
    			479,
    			-142
    		],
    		[
    			-99999,
    			0
    		]
    	],
    	[
    		[
    			59092,
    			71341
    		],
    		[
    			19,
    			3
    		],
    		[
    			40,
    			143
    		],
    		[
    			200,
    			-8
    		],
    		[
    			253,
    			176
    		],
    		[
    			-188,
    			-251
    		],
    		[
    			21,
    			-111
    		]
    	],
    	[
    		[
    			59437,
    			71293
    		],
    		[
    			-30,
    			21
    		],
    		[
    			-53,
    			-45
    		],
    		[
    			-42,
    			12
    		],
    		[
    			-14,
    			-22
    		],
    		[
    			-5,
    			59
    		],
    		[
    			-20,
    			37
    		],
    		[
    			-54,
    			6
    		],
    		[
    			-75,
    			-51
    		],
    		[
    			-52,
    			31
    		]
    	],
    	[
    		[
    			59437,
    			71293
    		],
    		[
    			8,
    			-48
    		],
    		[
    			-285,
    			-240
    		],
    		[
    			-136,
    			77
    		],
    		[
    			-64,
    			237
    		],
    		[
    			132,
    			22
    		]
    	],
    	[
    		[
    			45272,
    			63236
    		],
    		[
    			13,
    			274
    		],
    		[
    			106,
    			161
    		],
    		[
    			91,
    			308
    		],
    		[
    			-18,
    			200
    		],
    		[
    			96,
    			417
    		],
    		[
    			155,
    			376
    		],
    		[
    			93,
    			95
    		],
    		[
    			74,
    			344
    		],
    		[
    			6,
    			315
    		],
    		[
    			100,
    			365
    		],
    		[
    			185,
    			216
    		],
    		[
    			177,
    			603
    		],
    		[
    			5,
    			8
    		],
    		[
    			139,
    			227
    		],
    		[
    			259,
    			65
    		],
    		[
    			218,
    			404
    		],
    		[
    			140,
    			158
    		],
    		[
    			232,
    			493
    		],
    		[
    			-70,
    			735
    		],
    		[
    			106,
    			508
    		],
    		[
    			37,
    			312
    		],
    		[
    			179,
    			399
    		],
    		[
    			278,
    			270
    		],
    		[
    			206,
    			244
    		],
    		[
    			186,
    			612
    		],
    		[
    			87,
    			362
    		],
    		[
    			205,
    			-2
    		],
    		[
    			167,
    			-251
    		],
    		[
    			264,
    			41
    		],
    		[
    			288,
    			-131
    		],
    		[
    			121,
    			-6
    		]
    	],
    	[
    		[
    			56944,
    			63578
    		],
    		[
    			0,
    			2175
    		],
    		[
    			0,
    			2101
    		],
    		[
    			-83,
    			476
    		],
    		[
    			71,
    			365
    		],
    		[
    			-43,
    			253
    		],
    		[
    			101,
    			283
    		]
    	],
    	[
    		[
    			56990,
    			69231
    		],
    		[
    			369,
    			10
    		],
    		[
    			268,
    			-156
    		],
    		[
    			275,
    			-175
    		],
    		[
    			129,
    			-92
    		],
    		[
    			214,
    			188
    		],
    		[
    			114,
    			169
    		],
    		[
    			245,
    			49
    		],
    		[
    			198,
    			-75
    		],
    		[
    			75,
    			-293
    		],
    		[
    			65,
    			193
    		],
    		[
    			222,
    			-140
    		],
    		[
    			217,
    			-33
    		],
    		[
    			137,
    			149
    		]
    	],
    	[
    		[
    			59700,
    			68010
    		],
    		[
    			-78,
    			-238
    		],
    		[
    			-60,
    			-446
    		],
    		[
    			-75,
    			-308
    		],
    		[
    			-65,
    			-103
    		],
    		[
    			-93,
    			191
    		],
    		[
    			-125,
    			263
    		],
    		[
    			-198,
    			847
    		],
    		[
    			-29,
    			-53
    		],
    		[
    			115,
    			-624
    		],
    		[
    			171,
    			-594
    		],
    		[
    			210,
    			-920
    		],
    		[
    			102,
    			-321
    		],
    		[
    			90,
    			-334
    		],
    		[
    			249,
    			-654
    		],
    		[
    			-55,
    			-103
    		],
    		[
    			9,
    			-384
    		],
    		[
    			323,
    			-530
    		],
    		[
    			49,
    			-121
    		]
    	],
    	[
    		[
    			53191,
    			70158
    		],
    		[
    			326,
    			-204
    		],
    		[
    			117,
    			51
    		],
    		[
    			232,
    			-98
    		],
    		[
    			368,
    			-264
    		],
    		[
    			130,
    			-526
    		],
    		[
    			250,
    			-114
    		],
    		[
    			391,
    			-248
    		],
    		[
    			296,
    			-293
    		],
    		[
    			136,
    			153
    		],
    		[
    			133,
    			272
    		],
    		[
    			-65,
    			452
    		],
    		[
    			87,
    			288
    		],
    		[
    			200,
    			277
    		],
    		[
    			192,
    			80
    		],
    		[
    			375,
    			-121
    		],
    		[
    			95,
    			-264
    		],
    		[
    			104,
    			-2
    		],
    		[
    			88,
    			-101
    		],
    		[
    			276,
    			-70
    		],
    		[
    			68,
    			-195
    		]
    	],
    	[
    		[
    			59804,
    			53833
    		],
    		[
    			-164,
    			643
    		],
    		[
    			-127,
    			137
    		],
    		[
    			-48,
    			236
    		],
    		[
    			-141,
    			288
    		],
    		[
    			-171,
    			42
    		],
    		[
    			95,
    			337
    		],
    		[
    			147,
    			14
    		],
    		[
    			42,
    			181
    		]
    	],
    	[
    		[
    			61764,
    			57990
    		],
    		[
    			-98,
    			-261
    		],
    		[
    			-94,
    			-277
    		],
    		[
    			22,
    			-163
    		],
    		[
    			4,
    			-180
    		],
    		[
    			155,
    			-10
    		],
    		[
    			67,
    			42
    		],
    		[
    			62,
    			-106
    		]
    	],
    	[
    		[
    			61882,
    			57035
    		],
    		[
    			-61,
    			-209
    		],
    		[
    			103,
    			-325
    		],
    		[
    			102,
    			-285
    		],
    		[
    			106,
    			-210
    		],
    		[
    			909,
    			-702
    		],
    		[
    			233,
    			4
    		]
    	],
    	[
    		[
    			61966,
    			58083
    		],
    		[
    			66,
    			-183
    		],
    		[
    			-9,
    			-245
    		],
    		[
    			-158,
    			-142
    		],
    		[
    			119,
    			-161
    		]
    	],
    	[
    		[
    			61984,
    			57352
    		],
    		[
    			-102,
    			-317
    		]
    	],
    	[
    		[
    			61984,
    			57352
    		],
    		[
    			91,
    			-109
    		],
    		[
    			54,
    			-245
    		],
    		[
    			125,
    			-247
    		],
    		[
    			138,
    			-2
    		],
    		[
    			262,
    			151
    		],
    		[
    			302,
    			70
    		],
    		[
    			245,
    			184
    		],
    		[
    			138,
    			39
    		],
    		[
    			99,
    			108
    		],
    		[
    			158,
    			20
    		]
    	],
    	[
    		[
    			58449,
    			49909
    		],
    		[
    			-166,
    			-182
    		],
    		[
    			-67,
    			60
    		]
    	],
    	[
    		[
    			58564,
    			52653
    		],
    		[
    			115,
    			161
    		],
    		[
    			176,
    			-132
    		],
    		[
    			224,
    			138
    		],
    		[
    			195,
    			-1
    		],
    		[
    			171,
    			272
    		]
    	],
    	[
    		[
    			55279,
    			77084
    		],
    		[
    			100,
    			2
    		],
    		[
    			-69,
    			-260
    		],
    		[
    			134,
    			-227
    		],
    		[
    			-41,
    			-278
    		],
    		[
    			-65,
    			-27
    		]
    	],
    	[
    		[
    			55338,
    			76294
    		],
    		[
    			-52,
    			-53
    		],
    		[
    			-90,
    			-138
    		],
    		[
    			-41,
    			-325
    		]
    	],
    	[
    		[
    			55719,
    			75309
    		],
    		[
    			35,
    			-5
    		],
    		[
    			13,
    			121
    		],
    		[
    			164,
    			91
    		],
    		[
    			62,
    			23
    		]
    	],
    	[
    		[
    			55993,
    			75539
    		],
    		[
    			95,
    			35
    		],
    		[
    			128,
    			9
    		]
    	],
    	[
    		[
    			55993,
    			75539
    		],
    		[
    			-9,
    			44
    		],
    		[
    			33,
    			71
    		],
    		[
    			31,
    			144
    		],
    		[
    			-39,
    			-4
    		],
    		[
    			-54,
    			110
    		],
    		[
    			-46,
    			28
    		],
    		[
    			-36,
    			94
    		],
    		[
    			-52,
    			36
    		],
    		[
    			-40,
    			84
    		],
    		[
    			-50,
    			-33
    		],
    		[
    			-38,
    			-196
    		],
    		[
    			-66,
    			-43
    		]
    	],
    	[
    		[
    			55627,
    			75874
    		],
    		[
    			22,
    			51
    		],
    		[
    			-106,
    			123
    		],
    		[
    			-91,
    			63
    		],
    		[
    			-40,
    			82
    		],
    		[
    			-74,
    			101
    		]
    	],
    	[
    		[
    			55380,
    			75322
    		],
    		[
    			-58,
    			46
    		],
    		[
    			-78,
    			192
    		],
    		[
    			-120,
    			118
    		]
    	],
    	[
    		[
    			55627,
    			75874
    		],
    		[
    			-52,
    			-132
    		]
    	],
    	[
    		[
    			32866,
    			56937
    		],
    		[
    			160,
    			77
    		],
    		[
    			58,
    			-21
    		],
    		[
    			-11,
    			-440
    		],
    		[
    			-232,
    			-65
    		],
    		[
    			-50,
    			53
    		],
    		[
    			81,
    			163
    		],
    		[
    			-6,
    			233
    		]
    	]
    ];
    var bbox = [
    	-180,
    	-85.60903777459771,
    	180,
    	83.64513000000001
    ];
    var transform$1 = {
    	scale: [
    		0.0036000360003600037,
    		0.0016925586033320105
    	],
    	translate: [
    		-180,
    		-85.60903777459771
    	]
    };
    var worldAtlasData = {
    	type: type,
    	objects: objects,
    	arcs: arcs,
    	bbox: bbox,
    	transform: transform$1
    };

    function identity(x) {
      return x;
    }

    function transform(topology) {
      if ((transform = topology.transform) == null) return identity;
      var transform,
          x0,
          y0,
          kx = transform.scale[0],
          ky = transform.scale[1],
          dx = transform.translate[0],
          dy = transform.translate[1];
      return function(point, i) {
        if (!i) x0 = y0 = 0;
        point[0] = (x0 += point[0]) * kx + dx;
        point[1] = (y0 += point[1]) * ky + dy;
        return point;
      };
    }

    function reverse(array, n) {
      var t, j = array.length, i = j - n;
      while (i < --j) t = array[i], array[i++] = array[j], array[j] = t;
    }

    function feature(topology, o) {
      return o.type === "GeometryCollection"
          ? {type: "FeatureCollection", features: o.geometries.map(function(o) { return feature$1(topology, o); })}
          : feature$1(topology, o);
    }

    function feature$1(topology, o) {
      var id = o.id,
          bbox = o.bbox,
          properties = o.properties == null ? {} : o.properties,
          geometry = object(topology, o);
      return id == null && bbox == null ? {type: "Feature", properties: properties, geometry: geometry}
          : bbox == null ? {type: "Feature", id: id, properties: properties, geometry: geometry}
          : {type: "Feature", id: id, bbox: bbox, properties: properties, geometry: geometry};
    }

    function object(topology, o) {
      var transformPoint = transform(topology),
          arcs = topology.arcs;

      function arc(i, points) {
        if (points.length) points.pop();
        for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length; k < n; ++k) {
          points.push(transformPoint(a[k].slice(), k));
        }
        if (i < 0) reverse(points, n);
      }

      function point(p) {
        return transformPoint(p.slice());
      }

      function line(arcs) {
        var points = [];
        for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points);
        if (points.length < 2) points.push(points[0].slice());
        return points;
      }

      function ring(arcs) {
        var points = line(arcs);
        while (points.length < 4) points.push(points[0].slice());
        return points;
      }

      function polygon(arcs) {
        return arcs.map(ring);
      }

      function geometry(o) {
        var type = o.type, coordinates;
        switch (type) {
          case "GeometryCollection": return {type: type, geometries: o.geometries.map(geometry)};
          case "Point": coordinates = point(o.coordinates); break;
          case "MultiPoint": coordinates = o.coordinates.map(point); break;
          case "LineString": coordinates = line(o.arcs); break;
          case "MultiLineString": coordinates = o.arcs.map(line); break;
          case "Polygon": coordinates = polygon(o.arcs); break;
          case "MultiPolygon": coordinates = o.arcs.map(polygon); break;
          default: return null;
        }
        return {type: type, coordinates: coordinates};
      }

      return geometry(o);
    }

    function stitch(topology, arcs) {
      var stitchedArcs = {},
          fragmentByStart = {},
          fragmentByEnd = {},
          fragments = [],
          emptyIndex = -1;

      // Stitch empty arcs first, since they may be subsumed by other arcs.
      arcs.forEach(function(i, j) {
        var arc = topology.arcs[i < 0 ? ~i : i], t;
        if (arc.length < 3 && !arc[1][0] && !arc[1][1]) {
          t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t;
        }
      });

      arcs.forEach(function(i) {
        var e = ends(i),
            start = e[0],
            end = e[1],
            f, g;

        if (f = fragmentByEnd[start]) {
          delete fragmentByEnd[f.end];
          f.push(i);
          f.end = end;
          if (g = fragmentByStart[end]) {
            delete fragmentByStart[g.start];
            var fg = g === f ? f : f.concat(g);
            fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
          } else {
            fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
          }
        } else if (f = fragmentByStart[end]) {
          delete fragmentByStart[f.start];
          f.unshift(i);
          f.start = start;
          if (g = fragmentByEnd[start]) {
            delete fragmentByEnd[g.end];
            var gf = g === f ? f : g.concat(f);
            fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
          } else {
            fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
          }
        } else {
          f = [i];
          fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f;
        }
      });

      function ends(i) {
        var arc = topology.arcs[i < 0 ? ~i : i], p0 = arc[0], p1;
        if (topology.transform) p1 = [0, 0], arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; });
        else p1 = arc[arc.length - 1];
        return i < 0 ? [p1, p0] : [p0, p1];
      }

      function flush(fragmentByEnd, fragmentByStart) {
        for (var k in fragmentByEnd) {
          var f = fragmentByEnd[k];
          delete fragmentByStart[f.start];
          delete f.start;
          delete f.end;
          f.forEach(function(i) { stitchedArcs[i < 0 ? ~i : i] = 1; });
          fragments.push(f);
        }
      }

      flush(fragmentByEnd, fragmentByStart);
      flush(fragmentByStart, fragmentByEnd);
      arcs.forEach(function(i) { if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]); });

      return fragments;
    }

    function mesh(topology) {
      return object(topology, meshArcs.apply(this, arguments));
    }

    function meshArcs(topology, object, filter) {
      var arcs, i, n;
      if (arguments.length > 1) arcs = extractArcs(topology, object, filter);
      else for (i = 0, arcs = new Array(n = topology.arcs.length); i < n; ++i) arcs[i] = i;
      return {type: "MultiLineString", arcs: stitch(topology, arcs)};
    }

    function extractArcs(topology, object, filter) {
      var arcs = [],
          geomsByArc = [],
          geom;

      function extract0(i) {
        var j = i < 0 ? ~i : i;
        (geomsByArc[j] || (geomsByArc[j] = [])).push({i: i, g: geom});
      }

      function extract1(arcs) {
        arcs.forEach(extract0);
      }

      function extract2(arcs) {
        arcs.forEach(extract1);
      }

      function extract3(arcs) {
        arcs.forEach(extract2);
      }

      function geometry(o) {
        switch (geom = o, o.type) {
          case "GeometryCollection": o.geometries.forEach(geometry); break;
          case "LineString": extract1(o.arcs); break;
          case "MultiLineString": case "Polygon": extract2(o.arcs); break;
          case "MultiPolygon": extract3(o.arcs); break;
        }
      }

      geometry(object);

      geomsByArc.forEach(filter == null
          ? function(geoms) { arcs.push(geoms[0].i); }
          : function(geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); });

      return arcs;
    }

    const _hoisted_1$_ = { class: "static-tooltip-content" };
    const _hoisted_2$Q = ["innerHTML"];

    const dotSize = 1.8;


    var script$16 = {
      __name: 'geomap',
      props: {
        tooltipFormatter: Function,
        geomapDataArray: Array,
        getGeomapData: Function,
        glowDots: Boolean
    },
      setup(__props) {

    const d3 = d3v7;
    const topoData = ref(null);
    const countryMapping = ref(null);
    const props = __props;

    const geomapDataArray = ref(props.geomapDataArray);
    // Refs
    const mapContainer = ref(null);
    const svgElement = ref(null);
    const tooltipRef = ref(null);
    const isLoading = ref(true);

    // tooltip state
    const tooltip = ref({
        show: false,
        x: 0,
        y: 0,
        content: '',
        targetElement: null
    });

    // D3 data
    let svg = null;
    let projection = null;
    let path = null;
    let g = null;
    let zoomGroup = null;
    let width = 0;
    let height = 0;
    let resizeObserver = null;
    let worldData = null;

    let highlightedCountry = null;

    const closeTooltip = () => {
        tooltip.value.show = false;

        // reset highlited dot
        if (tooltip.value.targetElement) {
            const dotElement = d3.select(tooltip.value.targetElement).select('.alert-dot');
            if (!dotElement.empty()) {
                const originalRadius = parseFloat(dotElement.attr('data-original-radius')) || 2;
                const originalColor = dotElement.attr('data-original-color') || '#ff0000';

                dotElement
                    .transition()
                    .duration(200)
                    .attr('r', originalRadius)
                    .attr('fill', originalColor);
            }
        }

        // reset highlighted country
        if (highlightedCountry) {
            d3.select(highlightedCountry).attr('fill', '#1e293b');
            highlightedCountry = null;
        }

        tooltip.value.targetElement = null;
        tooltip.value.content = '';
    };

    const initializeMap = async () => {
        if (!mapContainer.value || !svgElement.value) return;

        isLoading.value = true;

        if (!topoData.value || !topoData.value.objects || !topoData.value.objects.countries) {
            isLoading.value = false;
            return;
        }

        // set dimensions
        const containerRect = mapContainer.value.getBoundingClientRect();
        width = containerRect.width;
        height = containerRect.height;

        // clear previous SVG content
        d3.select(svgElement.value).selectAll('*').remove();

        // initialize SVG and groups
        svg = d3.select(svgElement.value)
            .attr('width', width)
            .attr('height', height)
            .attr('viewBox', [0, 0, width, height]);

        zoomGroup = svg.append('g');

        projection = d3.geoEquirectangular()
            .scale((width) / (2 * Math.PI) * 0.9)
            .translate([width / 2, height / 2]);

        path = d3.geoPath().projection(projection);

        // Add grid/graticule with cybersecurity theme
        const graticule = d3.geoGraticule().step([15, 15]);
        zoomGroup.append('path')
            .attr('class', 'graticule')
            .attr('d', path(graticule()))
            .attr('fill', 'none')
            .attr('stroke', '#1e293b')
            .attr('stroke-width', 0.3)
            .attr('stroke-opacity', 0.7);

        g = zoomGroup.append('g');

        // zoom behaviour
        const zoom = d3.zoom()
            .scaleExtent([1, 8])
            .on('zoom', (event) => {
                zoomGroup.attr('transform', event.transform);
            });

        svg.call(zoom);

        // load world data if not already loaded
        if (!worldData) {
            try {
                // Convert TopoJSON to GeoJSON using topojson-client
                const countries = feature(topoData.value, topoData.value.objects.countries);
                worldData = countries;
            } catch (error) {
                console.error('Error loading world map data:', error);
                isLoading.value = false;
                return;
            }
        }

        // draw countries
        const countryPaths = g.selectAll('path.country')
            .data(worldData.features)
            .enter()
            .append('path')
            .attr('class', 'country')
            .attr('id', d => `country-${d.id || 'unknown'}`)
            .attr('d', path)
            .attr('fill', '#1e293b')
            .attr('stroke', '#334155')
            .attr('stroke-width', 0.5)
            .attr('stroke-opacity', 0.7)
            .attr('data-country-id', d => d.id)
            .attr('data-country-name', d => getCountryNameFromTopoData(d))
            .style('cursor', 'pointer');

        // add mouse event handlers
        countryPaths
            .on('mouseover', function (event, d) {
                if (highlightedCountry !== this) {
                    d3.select(this).attr('fill', '#334155');
                }
            })
            .on('mouseout', function (event, d) {
                if (highlightedCountry !== this) {
                    d3.select(this).attr('fill', '#1e293b');
                }
            })
            .on('click', function (event, d) {
                // stop event propagation first
                event.stopPropagation();

                // reset previous highlighted country
                if (highlightedCountry) {
                    d3.select(highlightedCountry).attr('fill', '#1e293b');
                }

                // highlight this country
                d3.select(this).attr('fill', '#475569');
                highlightedCountry = this;
            });

        // draw country shape
        const borders = mesh(topoData.value, topoData.value.objects.countries, (a, b) => a !== b);

        g.append('path')
            .attr('class', 'country-borders')
            .attr('d', path(borders))
            .attr('fill', 'none')
            .attr('stroke', '#475569')
            .attr('stroke-width', 0.7)
            .attr('stroke-opacity', 0.7);

        // close tooltip on svg click
        svg.on('click', function (event) {
            // Close tooltip if open
            if (tooltip.value.show) {
                closeTooltip();
            }

            // reset previous highlighted country if exists
            if (highlightedCountry) {
                d3.select(highlightedCountry).attr('fill', '#1e293b');
                highlightedCountry = null;
            }
        });

        isLoading.value = false;
    };

    // display data on map, 
    // check data format, if lat and lng are present use renderDotsByCoordinates
    // else render in the centroid of the country given in the json
    const displayData = () => {
        if (!worldData || !worldData.features) {
            console.log('Missing required data for displaying events');
            return;
        }

        // Clear existing dots on map
        g.selectAll('.alert-dot').remove();
        g.selectAll('.pulse-circle').remove();
        g.selectAll('.alert-label').remove();

        // Decide format type
        const sample = geomapDataArray.value[0];
        const isCoordinateBased = sample && 'lat' in sample && 'lng' in sample;

        if (isCoordinateBased) {
            renderDotsByCoordinates();
        } else {
            renderDotsByCountryCentroid();
        }
    };

    ///////////////////////////////
    const renderDotsByCoordinates = () => {
        geomapDataArray.value.forEach(alert => {
            const latitude = alert.lat;
            const longitude = alert.lng;
            const color = '#FF8F00';

            if (latitude == null || longitude == null) return;

            const coordinates = projection([longitude, latitude]);
            if (!coordinates || isNaN(coordinates[0]) || isNaN(coordinates[1])) return;

            const [x, y] = coordinates;

            const nodeGroup = g.append('g')
                .attr('class', 'alert-group')
                .attr('transform', `translate(${x}, ${y})`)
                .style('cursor', 'pointer');

            nodeGroup.append('circle')
                .attr('class', 'alert-dot')
                .attr('r', dotSize)
                .attr('fill', color)
                .attr('stroke', '#ffffff')
                .attr('stroke-width', 0.5)
                .attr('data-original-radius', dotSize)
                .attr('data-original-color', color);

            nodeGroup.on('click', function (event) {
                // prevent map click handler from firing
                event.stopPropagation();
                const tooltipContent = props.tooltipFormatter(alert);

                // get mouse position to put tooltip
                const [mouseX, mouseY] = d3.pointer(event, mapContainer.value);

                // show tooltip
                tooltip.value = {
                    show: true,
                    x: mouseX,
                    y: mouseY,
                    content: tooltipContent,
                    targetElement: this
                };
            });
        });
    };

    ///////////////////////////////
    const renderDotsByCountryCentroid = () => {
        const alertsByCountry = {};

        geomapDataArray.value.forEach(alert => {
            const countryId = alert.country_id;
            if (!alertsByCountry[countryId]) alertsByCountry[countryId] = [];
            alertsByCountry[countryId].push(alert);
        });

        Object.keys(alertsByCountry).forEach(countryId => {
            const feature = worldData.features.find(f => Number(f.id) === Number(countryId));
            if (!feature) return;

            const centroid = path.centroid(feature);
            const offsetBase = 10;
            const offsets = [
                [0, 0], [offsetBase, 0], [-offsetBase, 0],
                [0, offsetBase], [0, -offsetBase],
                [offsetBase, offsetBase], [-offsetBase, offsetBase],
                [offsetBase, -offsetBase], [-offsetBase, -offsetBase]
            ];

            alertsByCountry[countryId].forEach((alert, index) => {
                const color = alert.color || '#ff0000';
                const severity = alert.severity || 'Info';
                const offset = offsets[index % offsets.length];
                const [x, y] = [centroid[0] + offset[0], centroid[1] + offset[1]];

                const nodeGroup = g.append('g')
                    .attr('class', 'alert-group')
                    .attr('transform', `translate(${x}, ${y})`)
                    .style('cursor', 'pointer');

                const alertDot = nodeGroup.append('circle')
                    .attr('class', 'alert-dot')
                    .attr('r', dotSize)
                    .attr('fill', color)
                    .attr('stroke', '#ffffff')
                    .attr('stroke-width', 0.5)
                    .attr('data-original-radius', dotSize)
                    .attr('data-original-color', color);

                nodeGroup.on('click', function (event) {
                    event.stopPropagation();
                    const countryName = getCountryNameFromTopoData(feature);

                    // get tooltip content
                    const tooltipContent = props.tooltipFormatter(alert, countryName);

                    // get mouse position to show tooltup
                    const [mouseX, mouseY] = d3.pointer(event, mapContainer.value);

                    // show tooltip
                    tooltip.value = {
                        show: true,
                        x: mouseX,
                        y: mouseY,
                        content: tooltipContent,
                        targetElement: this
                    };

                });

                if (props.glowDots && ['Critical', 'Emergency', 'Warning'].includes(severity)) {
                    const glowId = `glow-${alert.country_id}-${severity}`;
                    if (!document.getElementById(glowId)) {
                        const glowFilter = svg.append('defs')
                            .append('filter')
                            .attr('id', glowId)
                            .attr('x', '-50%')
                            .attr('y', '-50%')
                            .attr('width', '200%')
                            .attr('height', '200%');

                        glowFilter.append('feGaussianBlur')
                            .attr('stdDeviation', severity === 'Warning' ? '1' : '2')
                            .attr('result', 'coloredBlur');

                        const feMerge = glowFilter.append('feMerge');
                        feMerge.append('feMergeNode').attr('in', 'coloredBlur');
                        feMerge.append('feMergeNode').attr('in', 'SourceGraphic');
                    }

                    alertDot.attr('filter', `url(#${glowId})`);
                }

                if (['Critical', 'Emergency', 'Warning', 'Error'].includes(severity)) {
                    nodeGroup.append('circle')
                        .attr('class', 'pulse-circle')
                        .attr('r', dotSize)
                        .attr('fill', 'none')
                        .attr('stroke', color)
                        .attr('stroke-width', 1)
                        .attr('opacity', 0.8)
                        .call(animatePulseElement);
                }

            });
        });
    };

    ///////////////////////////////

    // get country name from topodata feature
    const getCountryNameFromTopoData = (feature) => {
        if (!feature) return 'Unknown';

        if (feature.properties) {
            return feature.properties.NAME ||
                feature.properties.name ||
                feature.properties.ADMIN ||
                feature.properties.admin ||
                `Country #${feature.id || 'unknown'}`;
        }

        return `Country #${feature.id || 'unknown'}`;
    };

    // pulse nodes
    function animatePulseElement(element) {
        let baseRadius = parseFloat(element.attr('r')) * 1.2;

        if (baseRadius >= 2) baseRadius = 2;

        element
            .attr('r', baseRadius)
            .attr('opacity', 0.8)
            .transition()
            .duration(1500)
            .attr('r', baseRadius)
            .attr('opacity', 0)
            .ease(d3.easeQuadOut)
            .on('end', function () {
                d3.select(this).call(animatePulseElement);
            });
    }

    // Handle window resize
    const handleResize = async () => {
        await nextTick();
        
        // Close tooltip on resize
        if (tooltip.value.show) {
            closeTooltip();
        }
        
        // Re-display the data dots after map is reinitialized
        if (geomapDataArray.value && geomapDataArray.value.length > 0) {
            displayData();
        }
    };

    function buildCountryNameToIdMap() {
        if (!topoData.value || !topoData.value.objects || !topoData.value.objects.countries) {
            console.warn('TopoJSON data not loaded yet');
            return {};
        }

        const geometries = topoData.value.objects.countries.geometries;
        const map = {};

        geometries.forEach(geom => {
            const name = geom.properties.name;
            const id = parseInt(geom.id, 10);
            if (name && id) {
                map[name] = id;
            }
        });

        return map;
    }

    onMounted(async () => {
        try {
            // Use the imported world atlas data
            topoData.value = worldAtlasData;        
            // Build country mapping after data is loaded
            countryMapping.value = buildCountryNameToIdMap();
            
        } catch (error) {
            console.error('Error loading map data:', error);
            isLoading.value = false;
        }
        isLoading.value = false;
    });

    onUnmounted(() => {
        // clean up resources
        if (resizeObserver) {
            resizeObserver.disconnect();
        }

        // clear all transitions and intervals
        d3.selectAll('.pulse-circle').interrupt();
    });

    // watch to render data only after topojson is ready
    watch(topoData, async (newData) => {
        if (newData && mapContainer.value) {
            await initializeMap();
            displayData();
            // set up resize observer
            resizeObserver = new ResizeObserver(handleResize);
            resizeObserver.observe(mapContainer.value);
        }
    }, { immediate: true });

    // watch data props change and re-render
    watch(() => props.geomapDataArray, async (newData) => {
        geomapDataArray.value = newData;

        if (g && worldData) {
            displayData();
        } else {
            await initializeMap();
        }
    }, { immediate: true, deep: true });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", {
        class: "geomap-container",
        ref_key: "mapContainer",
        ref: mapContainer
      }, [
        createVNode(script$2y, { isLoading: isLoading.value }, null, 8 /* PROPS */, ["isLoading"]),
        createCommentVNode(" Tooltip "),
        (tooltip.value.show)
          ? (openBlock(), createElementBlock("div", {
              key: 0,
              ref_key: "tooltipRef",
              ref: tooltipRef,
              class: "static-tooltip",
              style: normalizeStyle({
                left: tooltip.value.x + 'px',
                top: tooltip.value.y + 'px'
            })
            }, [
              createBaseVNode("div", _hoisted_1$_, [
                createBaseVNode("div", { class: "tooltip-header" }, [
                  createBaseVNode("button", {
                    onClick: closeTooltip,
                    class: "close-btn",
                    type: "button",
                    "aria-label": "Close"
                  }, [...(_cache[0] || (_cache[0] = [
                    createBaseVNode("span", { "aria-hidden": "true" }, "×", -1 /* CACHED */)
                  ]))])
                ]),
                createBaseVNode("div", {
                  innerHTML: tooltip.value.content
                }, null, 8 /* PROPS */, _hoisted_2$Q)
              ])
            ], 4 /* STYLE */))
          : createCommentVNode("v-if", true),
        (openBlock(), createElementBlock("svg", {
          ref_key: "svgElement",
          ref: svgElement,
          width: "100%",
          height: "100vh"
        }, null, 512 /* NEED_PATCH */))
      ], 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$j = "\n.geomap-container[data-v-78ce4598] {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    min-height: 500px;\n    background-color: #0f172a;\n    border-radius: 8px;\n    overflow: hidden;\n    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);\n    font-family: 'Inter', 'Segoe UI', sans-serif;\n}\n.static-tooltip[data-v-78ce4598] {\n    position: absolute;\n    z-index: 1000;\n    pointer-events: auto;\n    background-color: rgba(15, 23, 42, 0.95);\n    border: 1px solid #334155;\n    border-radius: 8px;\n    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);\n    backdrop-filter: blur(10px);\n    min-width: 200px;\n    max-width: 300px;\n    color: #e2e8f0;\n}\n.static-tooltip-content[data-v-78ce4598] {\n    position: relative;\n    padding: 12px;\n}\n.tooltip-header[data-v-78ce4598] {\n    display: flex;\n    justify-content: flex-end;\n    margin-bottom: 8px;\n}\n.close-btn[data-v-78ce4598] {\n    background: none;\n    border: none;\n    color: #e2e8f0;\n    font-size: 18px;\n    font-weight: bold;\n    cursor: pointer;\n    padding: 0;\n    width: 20px;\n    height: 20px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    border-radius: 50%;\n    transition: all 0.2s ease;\n}\n.close-btn[data-v-78ce4598]:hover {\n    background-color: rgba(255, 255, 255, 0.1);\n    color: #ffffff;\n}\n.close-btn[data-v-78ce4598]:focus {\n    outline: none;\n    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);\n}\n[data-v-78ce4598] .custom-tooltip-content {\n    color: #e2e8f0;\n}\n[data-v-78ce4598] .custom-tooltip-content h6 {\n    color: #ffffff;\n    margin-bottom: 8px;\n}\n[data-v-78ce4598] .custom-tooltip-content hr {\n    border-color: rgba(148, 163, 184, 0.3);\n}\n[data-v-78ce4598] .custom-tooltip-content .flag {\n    width: 16px;\n    height: 12px;\n    margin-right: 8px;\n}\n[data-v-78ce4598] .custom-tooltip-content .badge {\n    font-size: 0.75rem;\n}\n.map-controls[data-v-78ce4598] {\n    position: absolute;\n    top: 15px;\n    right: 15px;\n    z-index: 5;\n    display: flex;\n    flex-direction: column;\n    gap: 10px;\n}\n.control-button[data-v-78ce4598] {\n    background-color: rgba(15, 23, 42, 0.8);\n    color: #e2e8f0;\n    border: 1px solid rgba(148, 163, 184, 0.3);\n    border-radius: 4px;\n    padding: 8px 12px;\n    font-size: 12px;\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    cursor: pointer;\n    transition: all 0.2s;\n}\n.control-button[data-v-78ce4598]:hover {\n    background-color: rgba(30, 41, 59, 0.9);\n    border-color: rgba(148, 163, 184, 0.5);\n}\n.button-icon[data-v-78ce4598] {\n    font-size: 14px;\n}\n.legend[data-v-78ce4598] {\n    background-color: rgba(15, 23, 42, 0.8);\n    border-radius: 4px;\n    padding: 10px;\n    border: 1px solid rgba(148, 163, 184, 0.3);\n}\n.legend-title[data-v-78ce4598] {\n    color: #e2e8f0;\n    font-size: 12px;\n    font-weight: 600;\n    margin-bottom: 8px;\n    border-bottom: 1px solid rgba(148, 163, 184, 0.2);\n    padding-bottom: 4px;\n}\n.legend-item[data-v-78ce4598] {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    color: #e2e8f0;\n    font-size: 11px;\n    margin-bottom: 5px;\n}\n.severity-dot[data-v-78ce4598] {\n    width: 10px;\n    height: 10px;\n    border-radius: 50%;\n}\n.severity-dot.critical[data-v-78ce4598] {\n    background-color: #ff2d55;\n    box-shadow: 0 0 5px rgba(255, 45, 85, 0.7);\n}\n.severity-dot.high[data-v-78ce4598] {\n    background-color: #ff9500;\n    box-shadow: 0 0 5px rgba(255, 149, 0, 0.7);\n}\n.severity-dot.medium[data-v-78ce4598] {\n    background-color: #ffcc00;\n    box-shadow: 0 0 5px rgba(255, 204, 0, 0.7);\n}\n.severity-dot.low[data-v-78ce4598] {\n    background-color: #5cd65c;\n    box-shadow: 0 0 5px rgba(92, 214, 92, 0.7);\n}\n\n/* Basic styling for map elements */\n[data-v-78ce4598] .country {\n    transition: fill 0.3s ease;\n}\n[data-v-78ce4598] .alert-dot {\n    transition: r 0.2s ease;\n}\n\n/* Pulse animation */\n@keyframes pulse-78ce4598 {\n0% {\n        transform: scale(1);\n        opacity: 0.8;\n}\n100% {\n        transform: scale(3);\n        opacity: 0;\n}\n}\n[data-v-78ce4598] .pulse-circle {\n    animation: pulse-78ce4598 1.5s infinite ease-out;\n}\n";
    styleInject(css_248z$j);

    script$16.__scopeId = "data-v-78ce4598";
    script$16.__file = "http_src/vue/geomap.vue";

    const _hoisted_1$Z = { class: "row" };
    const _hoisted_2$P = { class: "col-12" };
    const _hoisted_3$J = { class: "card card-shadow" };
    const _hoisted_4$I = { class: "card-body" };
    const _hoisted_5$C = { class: "row" };
    const _hoisted_6$v = { class: "col-sm-2" };
    const _hoisted_7$q = { class: "col-md-12 col-lg-12 col-xs-12 mb-4" };
    const _hoisted_8$n = { id: "error-message" };

    const endpoint = `/lua/rest/v2/get/geo_map/hosts.lua?`;


    var script$15 = {
      __name: 'hosts-geomap',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const ifid = props.context.ifid;
    const is_host_details = ref(false);
    const geomapDataArray = ref([]);

    const dropdownOptions = ref([
        { id: 0, label: "Active Hosts" },
        { id: 1, label: "Alerted Hosts" },
        { id: 2, label: "Local Hosts" },
        { id: 3, label: "Remote Hosts" },
    ]);
    const selectSearch = ref("selectSearch");
    const selectedHostType = ref(dropdownOptions.value[0]);

    const alertMessage = ref("");
    const alertClass = ref("alert-warning");
    const showAlert = ref(false);

    onMounted(() => {
        ntopng_url_manager$1.set_key_to_url("hosts_category", 0);
        ntopng_url_manager$1.set_key_to_url("ifid", ifid);
        getGeomapData();
    });

    function closeAlert() {
        showAlert.value = false;
    }

    // formatting function to handle tooltip formatter
    function formatTooltipData(host) {
      if (!host) return "";

      let html = `<div>`;

      // IP with clickable link
      if (host.ip) {
        html += `IP: <a href='${http_prefix}/lua/host_details.lua?host=${host.ip}'>${host.ip}</a><br>`;
      }

      // Country with flag and link
      if (host.country) {
        const countryCode = (host.country_code || host.country).toLowerCase();
        html += `Country: <b>${host.country}</b><a href='/lua/hosts_stats.lua?country=${host.country}'>
                <img src='/dist/images/blank.gif' class='flag flag-${countryCode}' />
             </a><br>`;
      }

      // City
      if (host.city) {
        html += `City: <b>${host.city}</b><br>`;
      }

      // Alerts Count
      html += `Alerts Count: <b>${host.numAlerts ?? 0}</b><br>`;

      // Score As Client
      html += `Score As Client: <b>${host.scoreClient ?? 0}</b><br>`;

      // Score As Server
      html += `Score As Server: <b>${host.scoreServer ?? 0}</b>`;

      html += `</div>`;

      return html;
    }


    async function getGeomapData() {
        // flush data before calling endpoint to remove data points that are not of this element
        geomapDataArray.value = [];

        const url = `${http_prefix}${endpoint}${ntopng_url_manager$1.get_url_params()}`;
        try {
            const rsp = await ntopng_utility$1.http_request(url);
            geomapDataArray.value = rsp;
            is_host_details.value = !!ntopng_url_manager$1.get_url_entry("host");
        } catch (error) {
            displayLocalizedError();
        }
    }

    async function updateSelectedOption(option) {
        ntopng_url_manager$1.set_key_to_url("hosts_category", option.id);
        getGeomapData();
    }

    function displayLocalizedError(error) {
        alertMessage.value = `${i18n("geo_map.geolocation_warning")}: ${i18n("geo_map.using_default_location")}`;
        alertClass.value = "alert-warning";
        showAlert.value = true;
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$Z, [
        createBaseVNode("div", _hoisted_2$P, [
          createBaseVNode("div", _hoisted_3$J, [
            createBaseVNode("div", _hoisted_4$I, [
              createBaseVNode("div", _hoisted_5$C, [
                createBaseVNode("div", _hoisted_6$v, [
                  createVNode(script$2H, {
                    id: selectSearch.value,
                    options: dropdownOptions.value,
                    selected_option: selectedHostType.value,
                    modelValue: selectedHostType.value,
                    "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((selectedHostType).value = $event)),
                    "onUpdate:selected_option": updateSelectedOption
                  }, null, 8 /* PROPS */, ["id", "options", "selected_option", "modelValue"])
                ]),
                createBaseVNode("div", _hoisted_7$q, [
                  _cache[1] || (_cache[1] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                  createBaseVNode("template", null, [
                    (showAlert.value)
                      ? (openBlock(), createElementBlock("div", {
                          key: 0,
                          class: normalizeClass(['alert', alertClass.value]),
                          id: "geomap-alert",
                          role: "alert"
                        }, [
                          createBaseVNode("span", _hoisted_8$n, toDisplayString(alertMessage.value), 1 /* TEXT */),
                          createBaseVNode("button", {
                            type: "button",
                            class: "btn-close",
                            onClick: closeAlert,
                            "aria-label": "Close"
                          })
                        ], 2 /* CLASS */))
                      : createCommentVNode("v-if", true)
                  ]),
                  createVNode(script$16, {
                    tooltipFormatter: formatTooltipData,
                    geomapDataArray: geomapDataArray.value,
                    getGeomapData: getGeomapData,
                    style: normalizeStyle([(is_host_details.value) ? 'height: 65vh;' : 'height: 75vh']),
                    glowDots: false
                  }, null, 8 /* PROPS */, ["geomapDataArray", "style"])
                ])
              ])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$15.__file = "http_src/vue/hosts-geomap.vue";

    const _hoisted_1$Y = ["innerHTML"];
    const _hoisted_2$O = { class: "m-2 mb-3" };


    var script$14 = {
      __name: 'page-country-stats',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const error_message = ref(i18n('country_historical_disabled'));
    const table_id = ref('countries_stats');
    const table_countries_stats = ref(null);
    const csrf = props.context.csrf;

    const map_table_def_columns = (columns) => {
        //country_details.lua?country=IT&page=historical

        let map_columns = {
            "name": (value, row) => {
                const url = `${http_prefix}/lua/hosts_stats.lua?country=${value}`;
                return `<img src='/dist/images/blank.gif' class='flag flag-${value.toLowerCase()}'>&nbsp&nbsp<a href=${url}>${value}</a>`
            },
            "charts_enabled": (value, row) => {
                // redirect to country details historical
                const url = `${http_prefix}/lua/country_details.lua?country=${row["name"]}&page=historical`;
                return `<a class='btn btn-sm btn-info ${(props.context.show_historical) ? '' : 'disabled'}' data-bs-toggle='tooltip' data-bs-placement='bottom' title="${(props.context.show_historical) ? i18n('country_historical_enabled') : i18n('country_historical_disabled')}" href=${url}><i class="fas fa-chart-area fa-lg"></i></a>`
            },
            "hosts": (value, row) => {
                return formatterUtils.getFormatter("number")(value);
            },
            "seen_since": (value, row) => {
                // `seen_since` might require formatting, e.g., date formatting.
                const formattedDate = NtopUtils$1.secondsToTime(Math.round(new Date().getTime() / 1000) - value);
                return formattedDate;
            },
            "score": (value, row) => {
                // Assuming `score` is a number that might require some formatting.
                return formatterUtils.getFormatter("number")(value);
            },
            "breakdown": (value, row) => {
                console.log(value);
                let traffic = value.bytes_sent + value.bytes_rcvd;
                const sent_pctg = value.bytes_sent * 100 / traffic;
                const rcvd_pctg = value.bytes_rcvd * 100 / traffic;
                return NtopUtils$1.createBreakdown(sent_pctg, rcvd_pctg, _i18n('sent'), _i18n('rcvd'))
            },
            "throughput": (value, row) => {
                return formatterUtils.getFormatter("bps")(value);
            },
            "traffic": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id === "charts_enabled") {
                const visible_dict = {
                    historical_data: props.context.show_historical,
                };

                c.button_def_array.forEach((b) => {

                    if (!visible_dict[b.id]) {
                        b.class.push("disabled");
                    }
                });
            }
        });

        return columns;
    };


    function columns_sorting(col, r0, r1) {
        if (col != null) {
        if (col.id == "name") {
          return sortingFunctions$1.sortByName(r0.name, r1.name, col.sort);
        } else if (col.id == "hosts") {
          return sortingFunctions$1.sortByNumber(r0.hosts, r1.hosts, col.sort);
        } else if (col.id == "seen_since") {
          return sortingFunctions$1.sortByNumber(r0.seen_since, r1.seen_since, col.sort);
        } else if (col.id == "score") {
          return sortingFunctions$1.sortByNumber(r0.score, r1.score, col.sort);
        } else if (col.id == "throughput") {
          return sortingFunctions$1.sortByNumber(r0.throughput, r1.throughput, col.sort);
        } else if (col.id == "traffic") {
          return sortingFunctions$1.sortByNumber(r0.traffic, r1.traffic, col.sort);
        } 
      }
     
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        ((!props.context.show_historical))
          ? (openBlock(), createElementBlock("div", {
              key: 0,
              class: "alert alert-warning",
              role: "alert",
              id: "error-alert",
              innerHTML: error_message.value
            }, null, 8 /* PROPS */, _hoisted_1$Y))
          : createCommentVNode("v-if", true),
        createBaseVNode("div", _hoisted_2$O, [
          createVNode(script$2w, {
            ref_key: "table_countries_stats",
            ref: table_countries_stats,
            table_id: table_id.value,
            csrf: unref(csrf),
            f_map_columns: map_table_def_columns,
            f_sort_rows: columns_sorting
          }, null, 8 /* PROPS */, ["table_id", "csrf"])
        ])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$14.__file = "http_src/vue/page-country-stats.vue";

    const _hoisted_1$X = { class: "mb-3 mt-3" };
    const _hoisted_2$N = {
      for: "pool_name",
      class: "form-label fw-bold"
    };
    const _hoisted_3$I = ["placeholder"];
    const _hoisted_4$H = {
      key: 0,
      class: "invalid-feedback"
    };
    const _hoisted_5$B = { class: "d-flex justify-content-end w-100" };
    const _hoisted_6$u = ["disabled"];


    var script$13 = {
      __name: 'modal-add-host-pool',
      props: {
        context: Object,
    },
      emits: ["add", "edit"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);

    const modal_id = ref(null);

    // Define both emits for add and edit operations
    const emit = __emit;

    // Form inputs
    const pool_name = ref("");
    const pool_members = ref("");
    const name_error = ref("");

    // edit mode and current item
    const isEditMode = ref(false);
    const currentItem = ref(null);

    // check if name is valid
    const is_name_valid = computed$1(() => {
        return pool_name.value &&
            pool_name.value.trim().length > 1 &&
            !name_error.value;
    });

    const validateName = () => {
        const trimmed_name = pool_name.value.trim();

        if (!trimmed_name) {
            name_error.value = _i18n("error_messages.name_cannot_be_empty");
        } else if (trimmed_name.length <= 1) {
            name_error.value = _i18n("error_messages.name_must_be_longer_than_1_character");
        } else {
            name_error.value = "";
        }
    };

    onMounted(() => { });

    // call correct emit if edit or not
    function handleSubmit() {
        // validate before submitting
        validateName();

        if (!is_name_valid.value) {
            return;
        }

        const formData = {
            pool_name: pool_name.value.trim(),
            pool_members: pool_members.value
        };

        if (isEditMode.value) {
            emit('edit', {
                ...formData,
                item: currentItem.value
            });
        } else {
            emit('add', formData);
        }

        // Close the modal
        close();
    }

    // Show modal for adding new item
    const show = () => {
        // Reset form values
        pool_name.value = "";
        pool_members.value = "";
        name_error.value = "";
        isEditMode.value = false;
        currentItem.value = null;

        modal_id.value.show();
    };

    // show modal for editing host pool name
    const showEdit = async (item) => {
        name_error.value = "";

        isEditMode.value = true;
        currentItem.value = item;
        pool_name.value = item.pool_name;
        pool_members.value = item.pool_members || "";

        await nextTick();

        // Check if modal reference exists
        if (!modal_id.value) {
            console.error('Modal reference is null after nextTick');
            return;
        }

        modal_id.value.show();
    };

    const close = () => {
        modal_id.value.close();
    };

    // show close and show edit emits
    __expose({ show, showEdit, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(isEditMode.value ? _i18n("host_pools.edit_host_pool_name") : _i18n("host_pools.add_host_pool_member")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Name Input Box "),
          createBaseVNode("div", _hoisted_1$X, [
            createBaseVNode("label", _hoisted_2$N, toDisplayString(_i18n("host_pools.pool_name")), 1 /* TEXT */),
            withDirectives(createBaseVNode("input", {
              id: "pool_name",
              type: "text",
              class: normalizeClass(["form-control", { 'is-invalid': name_error.value }]),
              "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((pool_name).value = $event)),
              placeholder: _i18n('enter_pool_name'),
              onInput: validateName,
              required: ""
            }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_3$I), [
              [vModelText, pool_name.value]
            ]),
            (name_error.value)
              ? (openBlock(), createElementBlock("div", _hoisted_4$H, toDisplayString(name_error.value), 1 /* TEXT */))
              : createCommentVNode("v-if", true)
          ]),
          createCommentVNode(" modal-body ")
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", _hoisted_5$B, [
            createBaseVNode("button", {
              type: "button",
              onClick: handleSubmit,
              class: "btn btn-primary",
              disabled: !is_name_valid.value
            }, toDisplayString(isEditMode.value ? _i18n("save") : _i18n("add")), 9 /* TEXT, PROPS */, _hoisted_6$u)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$13.__file = "http_src/vue/modal-add-host-pool.vue";

    const _hoisted_1$W = { class: "m-2 mb-3" };
    const _hoisted_2$M = { class: "card-footer mt-3" };
    const _hoisted_3$H = { class: "d-flex gap-2" };
    const _hoisted_4$G = ["title"];
    const _hoisted_5$A = ["title"];


    var script$12 = {
      __name: 'page-host-pools',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const table_id = ref('host_pools');
    const table_host_pools = ref(null);
    const csrf = props.context.csrf;
    const isnEdge = props.context.isnEdge;
    const isPro = props.context.isPro;
    const pool_base_url = `${http_prefix}/lua/hosts_stats.lua?pool=`;
    const modal_add_pool = ref(null);
    const modal_delete_pool = ref(null);
    const add_new_pool = ref(null);
    const title_delete = i18n("host_pools.delete_pool");
    let body_delete_18n = i18n("pools.remove_pool");

    // delete modal variables
    const body_delete = ref('');
    const current_pool_to_delete = ref(null);

    // table footer buttons urls
    const export_policy_url = `${http_prefix}/lua/pro/rest/v2/export/pool/policy.lua?download=1`;
    const pool_configuration_url = `${http_prefix}/lua/admin/manage_configurations.lua?item=pool`;

    // add new pool
    const addNewPool = () => {
        modal_add_pool.value.show();
    };

    /* handles post request to add a new pool from modal */
    const handleAddPool = async (params) => {
        const url = `${http_prefix}/lua/rest/v2/add/host/pool.lua`;

        const requestParams = {
            csrf: props.context.csrf,
            pool_name: params.pool_name,
            pool_members: params.pool_members
        };

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            await ntopng_utility$1.http_request(url, {
                method: 'post',
                headers,
                body: JSON.stringify(requestParams)
            });

            // Refresh the table
            table_host_pools.value.refresh_table(true);
        } catch (e) {
            console.error('Network error:', e.message);
        }
    };

    /* called on edit modal close to post the new pool config */
    const handleEditPool = async (params) => {
        const url = `${http_prefix}/lua/rest/v2/edit/host/pool.lua`;

        const requestParams = {
            csrf: props.context.csrf,
            pool_name: params.pool_name,
            pool_members: params.pool_members,
            pool: params.item.pool // this is the pool id
        };
        console.log(requestParams);

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            await ntopng_utility$1.http_request(url, {
                method: 'post',
                headers,
                body: JSON.stringify(requestParams)
            });

            // Refresh the table
            table_host_pools.value.refresh_table(true);

        } catch (e) {
            console.error('Network error:', e.message);
        }
    };

    /* deletes the selected pool */
    const handleDeletePool = async (params) => {
        const url = `${http_prefix}/lua/rest/v2/delete/host/pool.lua`;

        const requestParams = {
            csrf: props.context.csrf,
            pool: current_pool_to_delete.value
        };

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            await ntopng_utility$1.http_request(url, {
                method: 'post',
                headers,
                body: JSON.stringify(requestParams)
            });

            // Refresh the table
            table_host_pools.value.refresh_table(true);
            // Clear the selected pool
            current_pool_to_delete.value = null;
        } catch (e) {
            console.error('Network error:', e.message);
        }
    };

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "pool_name": (value, row) => {
                const url = pool_base_url + row["pool_id"];
                return `<a href="${url}">${value}</a>`
            },
            "hosts": (value, row) => {
                if (value != "0") { return formatterUtils.getFormatter("number")(value); } else { return (""); }
            },
            "seen_since": (value, row) => {
                if (value == 0) return ("");
                const formattedDate = NtopUtils$1.secondsToTime(Math.round(new Date().getTime() / 1000) - value);
                return formattedDate;
            },
            "breakdown": (value, row) => {
                const total_bytes = value.bytes_sent + value.bytes_rcvd;
                const sent_pctg = value.bytes_sent * 100 / total_bytes;
                const rcvd_pctg = value.bytes_rcvd * 100 / total_bytes;
                return NtopUtils$1.createBreakdown(sent_pctg, rcvd_pctg, _i18n('sent'), _i18n('rcvd'))
            },
            "throughput": (value, row) => {
                return formatterUtils.getFormatter("bps")(value);
            },
            "traffic": (value, row) => {
                if (value == 0) return ("");
                return formatterUtils.getFormatter("bytes")(value);
            },
            "clean_members": (value, row) => {
                // format as member1, member2, member3 etc
                return Object.values(value).join(', ');
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            // disable action dropdown items
            if (c.id == "actions") {
                const visible_dict = {
                    timeseries_chart: props.context.timeseriesEnabled,
                };

                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        if ((visible_dict[b.id] !== null) && (visible_dict[b.id] === false)) {
                            current_class.push("disabled");
                        }
                        // disable dropdown button delete for pools: 'Jailed Hosts' and 'Default'
                        if ((row.pool_name === "Jailed Hosts" || row.pool_name === "Default") &&
                            b.id === "delete") {
                            current_class.push("disabled");

                        } else if (row.pool_name === "Default" && b.id === "manage_pool") {
                            // disable dropdown button manage pool for pool: 'Default'
                            current_class.push("disabled");
                        }
                        else if (b.id === "edit_pool_policy" && (!isPro || !isnEdge)) {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });
        return columns;
    };

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "pool_name") {
                return sortingFunctions$1.sortByName(r0.pool_name, r1.pool_name, col.sort);
            } else if (col.id == "hosts") {
                return sortingFunctions$1.sortByNumber(r0.hosts, r1.hosts, col.sort);
            } else if (col.id == "seen_since") {
                return sortingFunctions$1.sortByNumber(r0.seen_since, r1.seen_since, col.sort);
            } else if (col.id == "throughput") {
                return sortingFunctions$1.sortByNumber(r0.throughput, r1.throughput, col.sort);
            } else if (col.id == "traffic") {
                return sortingFunctions$1.sortByNumber(r0.traffic, r1.traffic, col.sort);
            }
        }
    }

    /* Used to Handle click on actions dropdown */
    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_timeseries": click_button_timeseries,
            "click_manage_pool": click_manage_pool,
            "click_edit_pool": click_edit_pool,
            "click_edit_pool_policy": click_edit_pool_policy,
            "click_delete_pool": click_delete_pool,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* Functions below are used to handle 'actions' dropdown click */

    /* redirect to host pools page */
    const click_button_timeseries = (param) => {
        let pool_id = param.row.pool_id;
        let manage_pool_url = `${http_prefix}/lua/pool_details.lua?pool=${pool_id}`;
        // open page in current tab
        window.location.href = manage_pool_url;
    };

    /* redirect to manage pool page */
    const click_manage_pool = (param) => {
        let pool_id = param.row.pool_id;
        let manage_pool_url = `${http_prefix}/lua/admin/manage_host_members.lua?pool=${pool_id}`;
        // open page in current tab
        window.location.href = manage_pool_url;
    };

    /* edit pool button to change pool name */
    const click_edit_pool = (param) => {
        // memebers is an array, rest expects string of elements
        let members_array = param.row.members;
        let members_string = members_array.join(",");

        // Check if the modal ref exists
        if (!modal_add_pool.value) {
            console.error('Edit Modal reference is null in parent');
            return;
        }

        const poolData = {
            pool: param.row.pool_id,
            pool_name: param.row.pool_name,
            pool_members: members_string,
        };

        modal_add_pool.value.showEdit(poolData);
    };

    /* edit pool button to change pool policy */
    const click_edit_pool_policy = (param) => {
        let pool_id = param.row.pool_id;
        let pool_name = param.row.pool_name;
        let manage_pool_url;

        if (isnEdge)
            manage_pool_url = `${http_prefix}/lua/pro/nedge/admin/nf_edit_user.lua?username=${pool_name}`;
        else
            manage_pool_url = `${http_prefix}/lua/pro/policy.lua?pool=${pool_id}`;
        // open page in current tab
        window.location.href = manage_pool_url;
    };

    /* delete host pool */
    const click_delete_pool = (param) => {

        if (!modal_delete_pool.value) {
            console.error('Delete modal reference is null');
            return;
        }

        // store pool to be deleted
        current_pool_to_delete.value = param.row.pool_id;

        // reate body text
        const body_text = body_delete_18n.replace("%{pool}", param.row.pool_name);

        // Pass the dynamic body and title to the show method
        modal_delete_pool.value.show(body_text, title_delete);
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$W, [
          createVNode(script$2w, {
            ref_key: "table_host_pools",
            ref: table_host_pools,
            table_id: table_id.value,
            csrf: unref(csrf),
            f_map_columns: map_table_def_columns,
            f_sort_rows: columns_sorting,
            onCustom_event: on_table_custom_event
          }, {
            custom_buttons: withCtx(() => [
              createBaseVNode("button", {
                class: "btn btn-link",
                type: "button",
                ref_key: "add_new_pool",
                ref: add_new_pool,
                onClick: addNewPool
              }, [...(_cache[0] || (_cache[0] = [
                createBaseVNode("i", { class: "fas fa-plus" }, null, -1 /* CACHED */)
              ]))], 512 /* NEED_PATCH */)
            ]),
            _: 1 /* STABLE */
          }, 8 /* PROPS */, ["table_id", "csrf"]),
          createCommentVNode(" Buttons for policy export and management "),
          createBaseVNode("div", _hoisted_2$M, [
            createBaseVNode("div", _hoisted_3$H, [
              createBaseVNode("a", {
                class: "btn btn-secondary",
                style: {},
                href: pool_configuration_url
              }, [
                createBaseVNode("i", {
                  class: "fas fa-tasks",
                  "data-bs-toggle": "tooltip",
                  "data-bs-placement": "top",
                  title: _i18n('manage_configurations.manage_configuration')
                }, null, 8 /* PROPS */, _hoisted_4$G),
                createTextVNode(" " + toDisplayString(_i18n('manage_configurations.manage_configuration')), 1 /* TEXT */)
              ]),
              (!unref(isnEdge))
                ? (openBlock(), createElementBlock("a", {
                    key: 0,
                    class: "btn btn-primary",
                    download: "policy.json",
                    href: export_policy_url
                  }, [
                    createBaseVNode("i", {
                      class: "fas fa-file-export",
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      title: _i18n('manage_configurations.export_policy')
                    }, null, 8 /* PROPS */, _hoisted_5$A),
                    createTextVNode(" " + toDisplayString(_i18n('manage_configurations.export_policy')), 1 /* TEXT */)
                  ]))
                : createCommentVNode("v-if", true)
            ])
          ])
        ]),
        createCommentVNode(" Modals to add and delete host pools "),
        createVNode(script$13, {
          ref_key: "modal_add_pool",
          ref: modal_add_pool,
          context: __props.context,
          onAdd: handleAddPool,
          onEdit: handleEditPool
        }, null, 8 /* PROPS */, ["context"]),
        createVNode(script$2a, {
          ref_key: "modal_delete_pool",
          ref: modal_delete_pool,
          title: unref(title_delete),
          body: body_delete.value,
          onDelete: handleDeletePool
        }, null, 8 /* PROPS */, ["title", "body"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$12.__file = "http_src/vue/page-host-pools.vue";

    const _hoisted_1$V = { class: "ms-2 me-2 mt-3 row" };
    const _hoisted_2$L = { class: "col-form-label col-sm-4" };
    const _hoisted_3$G = { class: "col-sm-8" };
    const _hoisted_4$F = {
      class: "btn-group w-75",
      role: "group"
    };
    const _hoisted_5$z = { class: "ms-2 me-2 mt-3 row" };
    const _hoisted_6$t = { class: "col-form-label col-sm-4" };
    const _hoisted_7$p = { class: "col-sm-8" };
    const _hoisted_8$m = {
      class: "btn-group w-50",
      role: "group"
    };
    const _hoisted_9$k = ["placeholder"];
    const _hoisted_10$h = {
      key: 0,
      class: "text-danger"
    };
    const _hoisted_11$f = { class: "ms-2 me-2 mt-3 row" };
    const _hoisted_12$c = { class: "col-form-label col-sm-4" };
    const _hoisted_13$b = { class: "col-sm-8" };
    const _hoisted_14$b = {
      class: "btn-group w-50",
      role: "group"
    };
    const _hoisted_15$b = {
      key: 0,
      class: "text-danger"
    };
    const _hoisted_16$b = { class: "ms-2 me-2 mt-3 row" };
    const _hoisted_17$b = { class: "col-form-label col-sm-4" };
    const _hoisted_18$8 = { class: "col-sm-8 d-flex" };
    const _hoisted_19$8 = { class: "w-50" };
    const _hoisted_20$8 = ["placeholder"];
    const _hoisted_21$8 = {
      key: 0,
      class: "text-danger"
    };
    const _hoisted_22$8 = ["placeholder", "max"];
    const _hoisted_23$8 = {
      key: 0,
      class: "text-danger"
    };
    const _hoisted_24$8 = { class: "ms-2 me-2 mt-3 row" };
    const _hoisted_25$7 = { class: "col-form-label col-sm-4" };
    const _hoisted_26$6 = { class: "col-sm-8" };
    const _hoisted_27$6 = {
      class: "btn-group w-50",
      role: "group"
    };
    const _hoisted_28$5 = {
      key: 0,
      class: "text-danger"
    };
    const _hoisted_29$5 = {
      key: 2,
      class: "ms-2 me-2 mt-3 row"
    };
    const _hoisted_30$5 = { class: "col-form-label col-sm-4" };
    const _hoisted_31$4 = { class: "col-sm-8" };
    const _hoisted_32$4 = {
      class: "btn-group w-50",
      role: "group"
    };
    const _hoisted_33$4 = ["placeholder"];
    const _hoisted_34$4 = {
      key: 0,
      class: "text-danger"
    };
    const _hoisted_35$4 = {
      key: 0,
      class: "me-auto text-danger d-inline"
    };
    const _hoisted_36$4 = ["disabled"];


    var script$11 = {
      __name: 'modal-host-pool-member',
      props: {
        context: Object,
    },
      emits: ["add", "edit"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);

    const modal_id = ref(null);

    // Define emits for add and edit operations
    const emit = __emit;

    // Member type and form fields
    const memberType = ref("ip");
    const ipAddress = ref("");
    const ipVlan = ref(0);
    const networkAddress = ref("");
    const cidr = ref(24);
    const networkVlan = ref(0);
    const macAddress = ref("");

    // Error states
    const ipAddressError = ref("");
    const ipVlanError = ref("");
    const networkAddressError = ref("");
    const cidrError = ref("");
    const networkVlanError = ref("");
    const macAddressError = ref("");
    const generalError = ref("");

    // Track edit mode and current item
    const isEditMode = ref(false);
    const currentItem = ref(null);

    const props = __props;

    // Regex patterns for validation
    const IPV4_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
    const IPV6_REGEX = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::1$|^::$/;
    const MAC_REGEX = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;

    // Computed properties
    const isIPv6Network = computed$1(() => {
        return networkAddress.value && isValidIPv6(networkAddress.value);
    });

    const isFormValid = computed$1(() => {
        if (memberType.value === "ip") {
            return ipAddress.value && !ipAddressError.value && !ipVlanError.value;
        } else if (memberType.value === "network") {
            return networkAddress.value && cidr.value && !networkAddressError.value && !cidrError.value && !networkVlanError.value;
        } else if (memberType.value === "mac") {
            return macAddress.value && !macAddressError.value;
        }
        return false;
    });

    // Validation functions
    const isValidIPv4 = (ip) => IPV4_REGEX.test(ip);
    const isValidIPv6 = (ip) => IPV6_REGEX.test(ip);
    const isValidMAC = (mac) => MAC_REGEX.test(mac);

    const validateIpAddress = () => {
        const trimmed = ipAddress.value.trim();
        if (!trimmed) {
            ipAddressError.value = _i18n("host_pools.invalid_member");
        } else if (!isValidIPv4(trimmed) && !isValidIPv6(trimmed)) {
            ipAddressError.value = _i18n("host_pools.invalid_member");
        } else {
            ipAddressError.value = "";
        }
    };

    const validateIpVlan = () => {
        const value = ipVlan.value;
        const isInteger = /^\d+$/.test(value);
        const hasLeadingZero = value.length > 1 && value[0] === "0";
        const num = Number(value);

        if (!isInteger || hasLeadingZero || num < 0 || num > 4094) {
            ipVlanError.value = _i18n("host_pools.invalid_vlan");
        } else {
            ipVlanError.value = "";
        }
    };


    const validateNetworkAddress = () => {
        const trimmed = networkAddress.value.trim();
        if (!trimmed) {
            networkAddressError.value = _i18n("host_pools.network_address_required");
        } else if (!isValidIPv4(trimmed) && !isValidIPv6(trimmed)) {
            networkAddressError.value = _i18n("host_pools.invalid_network_address");
        } else {
            networkAddressError.value = "";
        }
    };

    const validateCidr = () => {
        const maxCidr = isIPv6Network.value ? 128 : 32;
        if (!cidr.value || cidr.value < 1 || cidr.value > maxCidr) {
            cidrError.value = _i18n("host_pools.invalid_cidr_range");
        } else {
            cidrError.value = "";
        }
    };

    const validateMacAddress = () => {
        const trimmed = macAddress.value.trim();
        if (!trimmed) {
            macAddressError.value = _i18n("host_pools.mac_required");
        } else if (!isValidMAC(trimmed)) {
            macAddressError.value = _i18n("host_pools.invalid_mac");
        } else {
            macAddressError.value = "";
        }
    };

    // Clear all errors
    const clearErrors = () => {
        ipAddressError.value = "";
        ipVlanError.value = "";
        networkAddressError.value = "";
        cidrError.value = "";
        networkVlanError.value = "";
        macAddressError.value = "";
        generalError.value = "";
    };

    // Handle member type change
    const onMemberTypeChange = () => {
        clearErrors();
    };

    // Reset form
    const resetForm = () => {
        memberType.value = "ip";
        ipAddress.value = "";
        ipVlan.value = 0;
        networkAddress.value = "";
        cidr.value = 24;
        networkVlan.value = 0;
        macAddress.value = "";
        clearErrors();
        isEditMode.value = false;
        currentItem.value = null;
    };

    // Handle form submission
    const handleSubmit = () => {
        // Validate current fields
        if (memberType.value === "ip") {
            validateIpAddress();
            validateIpVlan();
        } else if (memberType.value === "network") {
            validateNetworkAddress();
            validateCidr();
            validateIpVlan();
        } else if (memberType.value === "mac") {
            validateMacAddress();
        }

        if (!isFormValid.value) {
            return;
        }

        // Prepare member data based on type
        let member;
        if (memberType.value === "mac") {
            member = macAddress.value.trim();
        } else if (memberType.value === "ip") {
            const ip = ipAddress.value.trim();
            const vlan = ipVlan.value || 0;
            const cidrValue = isValidIPv6(ip) ? 128 : 32;
            member = `${ip}/${cidrValue}@${vlan}`;
        } else { // network
            const network = networkAddress.value.trim();
            const cidrValue = cidr.value;
            const vlan = networkVlan.value || 0;
            member = `${network}/${cidrValue}@${vlan}`;
        }

        const formData = {
            member_type: memberType.value,
            member: member,
            pool: props.context?.selectedPool?.id
        };

        if (isEditMode.value) {
            emit('edit', {
                ...formData,
                old_member: currentItem.value.member,
                item: currentItem.value
            });
        } else {
            emit('add', formData);
        }

        close();
    };

    // Show modal for adding new item
    const show = () => {
        resetForm();
        modal_id.value.show();
    };

    // Show modal for editing existing item
    const showEdit = async (item) => {
        resetForm();

        isEditMode.value = true;
        currentItem.value = item;

        // Parse the item data based on type
        if (item.type === "mac") {
            memberType.value = "mac";
            macAddress.value = item.name;
        } else if (item.type === "ip") {
            memberType.value = "ip";
            ipAddress.value = item.name;
            ipVlan.value = item.vlan || 0;
        } else { // network
            memberType.value = "network";
            const parts = item.name.split('/');
            if (parts.length === 2) {
                networkAddress.value = parts[0];
                cidr.value = parseInt(parts[1]);
            }
            networkVlan.value = item.vlan || 0;
        }

        await nextTick();

        if (!modal_id.value) {
            console.error('Modal reference is null after nextTick');
            return;
        }

        modal_id.value.show();
    };

    const close = () => {
        modal_id.value.close();
    };

    // Handle general error display
    const showError = (error) => {
        generalError.value = error;
    };

    // Expose methods
    __expose({ show, showEdit, close, showError });

    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(isEditMode.value ? _i18n("host_pools.edit_host_pool_name") : _i18n("host_pools.add_host_pool_member")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Member Type Selection "),
          createBaseVNode("div", _hoisted_1$V, [
            createBaseVNode("label", _hoisted_2$L, [
              createBaseVNode("b", null, toDisplayString(_i18n("host_pools.member_type")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$G, [
              createBaseVNode("div", _hoisted_4$F, [
                withDirectives(createBaseVNode("input", {
                  type: "radio",
                  class: "btn-check btn-primary",
                  id: "ip-radio",
                  value: "ip",
                  "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((memberType).value = $event)),
                  onChange: onMemberTypeChange
                }, null, 544 /* NEED_HYDRATION, NEED_PATCH */), [
                  [vModelRadio, memberType.value]
                ]),
                createBaseVNode("label", {
                  class: normalizeClass(["btn btn-sm flex-fill", memberType.value === 'ip' ? 'btn-primary active' : 'btn-secondary']),
                  for: "ip-radio"
                }, toDisplayString(_i18n("host_pools.ipv4")) + "/" + toDisplayString(_i18n("host_pools.ipv6")), 3 /* TEXT, CLASS */),
                withDirectives(createBaseVNode("input", {
                  type: "radio",
                  class: "btn-check btn-primary",
                  id: "network-radio",
                  value: "network",
                  "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((memberType).value = $event)),
                  onChange: onMemberTypeChange
                }, null, 544 /* NEED_HYDRATION, NEED_PATCH */), [
                  [vModelRadio, memberType.value]
                ]),
                createBaseVNode("label", {
                  class: normalizeClass(["btn btn-sm flex-fill", memberType.value === 'network' ? 'btn-primary active' : 'btn-secondary']),
                  for: "network-radio"
                }, toDisplayString(_i18n("network")), 3 /* TEXT, CLASS */),
                withDirectives(createBaseVNode("input", {
                  type: "radio",
                  class: "btn-check btn-primary",
                  id: "mac-radio",
                  value: "mac",
                  "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((memberType).value = $event)),
                  onChange: onMemberTypeChange
                }, null, 544 /* NEED_HYDRATION, NEED_PATCH */), [
                  [vModelRadio, memberType.value]
                ]),
                createBaseVNode("label", {
                  class: normalizeClass(["btn btn-sm flex-fill", memberType.value === 'mac' ? 'btn-primary active' : 'btn-secondary']),
                  for: "mac-radio"
                }, toDisplayString(_i18n("host_pools.mac_filter")), 3 /* TEXT, CLASS */)
              ])
            ])
          ]),
          createCommentVNode(" IP Address Fields "),
          (memberType.value === 'ip')
            ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                createBaseVNode("div", _hoisted_5$z, [
                  createBaseVNode("label", _hoisted_6$t, [
                    createBaseVNode("b", null, toDisplayString(_i18n("ip_address")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_7$p, [
                    createBaseVNode("div", _hoisted_8$m, [
                      withDirectives(createBaseVNode("input", {
                        id: "ip_address",
                        type: "text",
                        class: normalizeClass(["form-control", { 'invalid': ipAddressError.value }]),
                        "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((ipAddress).value = $event)),
                        placeholder: _i18n('enter_ip_address'),
                        onInput: validateIpAddress,
                        onKeyup: withKeys(handleSubmit, ["enter"]),
                        required: ""
                      }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_9$k), [
                        [vModelText, ipAddress.value]
                      ])
                    ]),
                    (ipAddressError.value)
                      ? (openBlock(), createElementBlock("small", _hoisted_10$h, toDisplayString(ipAddressError.value), 1 /* TEXT */))
                      : createCommentVNode("v-if", true)
                  ])
                ]),
                createBaseVNode("div", _hoisted_11$f, [
                  createBaseVNode("label", _hoisted_12$c, [
                    createBaseVNode("b", null, toDisplayString(_i18n("vlan")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_13$b, [
                    createBaseVNode("div", _hoisted_14$b, [
                      withDirectives(createBaseVNode("input", {
                        id: "ip_vlan",
                        type: "number",
                        class: normalizeClass(["form-control", { 'invalid': ipVlanError.value }]),
                        "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((ipVlan).value = $event)),
                        placeholder: "0",
                        min: "0",
                        max: "4094",
                        onInput: validateIpVlan,
                        onKeyup: withKeys(handleSubmit, ["enter"])
                      }, null, 34 /* CLASS, NEED_HYDRATION */), [
                        [
                          vModelText,
                          ipVlan.value,
                          void 0,
                          { number: true }
                        ]
                      ])
                    ]),
                    (ipVlanError.value)
                      ? (openBlock(), createElementBlock("small", _hoisted_15$b, toDisplayString(ipVlanError.value), 1 /* TEXT */))
                      : createCommentVNode("v-if", true)
                  ])
                ])
              ], 64 /* STABLE_FRAGMENT */))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Network Fields "),
          (memberType.value === 'network')
            ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                createBaseVNode("div", _hoisted_16$b, [
                  createBaseVNode("label", _hoisted_17$b, [
                    createBaseVNode("b", null, toDisplayString(_i18n("network")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_18$8, [
                    createBaseVNode("div", _hoisted_19$8, [
                      withDirectives(createBaseVNode("input", {
                        id: "network_address",
                        type: "text",
                        class: normalizeClass(["form-control", { 'invalid': networkAddressError.value }]),
                        "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((networkAddress).value = $event)),
                        placeholder: _i18n('enter_network_address'),
                        onInput: validateNetworkAddress,
                        onKeyup: withKeys(handleSubmit, ["enter"]),
                        required: ""
                      }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_20$8), [
                        [vModelText, networkAddress.value]
                      ]),
                      (networkAddressError.value)
                        ? (openBlock(), createElementBlock("small", _hoisted_21$8, toDisplayString(networkAddressError.value), 1 /* TEXT */))
                        : createCommentVNode("v-if", true)
                    ]),
                    _cache[9] || (_cache[9] = createBaseVNode("div", { class: "me-2 ms-2 align-items-center d-flex" }, "/", -1 /* CACHED */)),
                    createBaseVNode("div", null, [
                      withDirectives(createBaseVNode("input", {
                        id: "cidr",
                        type: "number",
                        class: normalizeClass(["form-control", { 'invalid': cidrError.value }]),
                        "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((cidr).value = $event)),
                        placeholder: _i18n('enter_cidr'),
                        min: "1",
                        max: isIPv6Network.value ? 128 : 32,
                        onInput: validateCidr,
                        onKeyup: withKeys(handleSubmit, ["enter"]),
                        required: ""
                      }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_22$8), [
                        [
                          vModelText,
                          cidr.value,
                          void 0,
                          { number: true }
                        ]
                      ]),
                      (cidrError.value)
                        ? (openBlock(), createElementBlock("small", _hoisted_23$8, toDisplayString(cidrError.value), 1 /* TEXT */))
                        : createCommentVNode("v-if", true)
                    ])
                  ])
                ]),
                createBaseVNode("div", _hoisted_24$8, [
                  createBaseVNode("label", _hoisted_25$7, [
                    createBaseVNode("b", null, toDisplayString(_i18n("vlan")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_26$6, [
                    createBaseVNode("div", _hoisted_27$6, [
                      withDirectives(createBaseVNode("input", {
                        id: "network_vlan",
                        type: "number",
                        class: normalizeClass(["form-control", { 'invalid': networkVlanError.value }]),
                        "onUpdate:modelValue": _cache[7] || (_cache[7] = $event => ((networkVlan).value = $event)),
                        placeholder: "0",
                        min: "0",
                        max: "4094",
                        onInput: validateIpVlan,
                        onKeyup: withKeys(handleSubmit, ["enter"])
                      }, null, 34 /* CLASS, NEED_HYDRATION */), [
                        [
                          vModelText,
                          networkVlan.value,
                          void 0,
                          { number: true }
                        ]
                      ])
                    ]),
                    (networkVlanError.value)
                      ? (openBlock(), createElementBlock("small", _hoisted_28$5, toDisplayString(networkVlanError.value), 1 /* TEXT */))
                      : createCommentVNode("v-if", true)
                  ])
                ])
              ], 64 /* STABLE_FRAGMENT */))
            : createCommentVNode("v-if", true),
          createCommentVNode(" MAC Address Fields "),
          (memberType.value === 'mac')
            ? (openBlock(), createElementBlock("div", _hoisted_29$5, [
                createBaseVNode("label", _hoisted_30$5, [
                  createBaseVNode("b", null, toDisplayString(_i18n("mac_address")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_31$4, [
                  createBaseVNode("div", _hoisted_32$4, [
                    withDirectives(createBaseVNode("input", {
                      id: "mac_address",
                      type: "text",
                      class: normalizeClass(["form-control", { 'invalid': macAddressError.value }]),
                      "onUpdate:modelValue": _cache[8] || (_cache[8] = $event => ((macAddress).value = $event)),
                      placeholder: _i18n('enter_mac_address'),
                      onInput: validateMacAddress,
                      onKeyup: withKeys(handleSubmit, ["enter"]),
                      required: ""
                    }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_33$4), [
                      [vModelText, macAddress.value]
                    ])
                  ]),
                  (macAddressError.value)
                    ? (openBlock(), createElementBlock("small", _hoisted_34$4, toDisplayString(macAddressError.value), 1 /* TEXT */))
                    : createCommentVNode("v-if", true)
                ])
              ]))
            : createCommentVNode("v-if", true)
        ]),
        footer: withCtx(() => [
          (generalError.value)
            ? (openBlock(), createElementBlock("div", _hoisted_35$4, toDisplayString(generalError.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createBaseVNode("button", {
              type: "button",
              onClick: handleSubmit,
              class: "btn btn-primary",
              disabled: !isFormValid.value
            }, toDisplayString(isEditMode.value ? _i18n("save") : _i18n("add")), 9 /* TEXT, PROPS */, _hoisted_36$4)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$11.__file = "http_src/vue/modal-host-pool-member.vue";

    const _hoisted_1$U = { class: "m-2 mb-3" };
    const _hoisted_2$K = { class: "card-footer mt-3" };
    const _hoisted_3$F = { class: "d-flex gap-2" };
    const _hoisted_4$E = ["title"];


    var script$10 = {
      __name: 'page-manage-host-members',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const table_id = ref('manage_host_pool');
    const table_host_pools = ref(null);
    const csrf = props.context.csrf;
    const modal_add_member = ref(null);
    const modal_delete_pool = ref(null);
    const add_new_pool = ref(null);
    const member_to_delete = ref(null);

    const title_delete = i18n("host_pools.delete_pool");
    let body_delete_member_i18n_template = i18n("host_pools.remove_member_pool");

    // delete modal variables
    const body_delete = ref('');
    const current_pool_to_delete = ref(null);

    // table footer buttons urls
    const pool_configuration_url = `${http_prefix}/lua/admin/manage_configurations.lua?item=pool`;


    // add new pool
    const addNewPool = () => {
        modal_add_member.value.show();
    };


    /* Used by table to pass parameter to default rest that populates table with data */
    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* handles post request to add a new pool member from modal */
    const handleAddMember = async (params) => {
        const url = `${http_prefix}/lua/rest/v2/add/host/pool_member.lua`;

        const requestParams = {
            csrf: props.context.csrf,
            pool: props.context.pool_id,
            member: params.member
        };

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            await ntopng_utility$1.http_request(url, {
                method: 'post',
                headers,
                body: JSON.stringify(requestParams)
            });

            // Refresh the table
            table_host_pools.value.refresh_table(true);
        } catch (e) {
            console.error('Network error:', e.message);
        }
    };

    /* called on edit modal to edit a member */
    const handleEditMember = async (params) => {

        const url = `${http_prefix}/lua/rest/v2/edit/host/pool/member.lua`;

        const requestParams = {
            action: "edit",
            csrf: props.context.csrf,
            member: params.member,
            old_member: params.old_member,
            pool: Number(props.context.pool_id)
        };

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            await ntopng_utility$1.http_request(url, {
                method: 'post',
                headers,
                body: JSON.stringify(requestParams)
            });

            // Refresh the table
            table_host_pools.value.refresh_table(true);

        } catch (e) {
            console.error('Network error:', e.message);
        }
    };

    /* deletes the selected member*/
    const handleDeleteMember = async () => {

        const url = `${http_prefix}/lua/rest/v2/delete/host/pool_member.lua`;

        const requestParams = {
            csrf: props.context.csrf,
            pool: parseInt(member_to_delete.value.pool, 10),
            member: member_to_delete.value.member,
            pool_name: member_to_delete.value.pool_name
        };

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            await ntopng_utility$1.http_request(url, {
                method: 'post',
                headers,
                body: JSON.stringify(requestParams)
            });

            // Refresh the table
            table_host_pools.value.refresh_table(true);
            // Clear the selected pool
            current_pool_to_delete.value = null;
        } catch (e) {
            console.error('Network error:', e.message);
        }
    };

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "member_address": (value, row) => {
                return value
            },
            "vlan": (value, row) => {
                return value;
            },
            "type": (value, row) => {
                return formatterUtils.capitalizeFirstLetters(value)
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            // disable action dropdown items
            if (c.id == "actions") {

                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {

                        // Disable dropdown button delete for pools: 'Jailed Hosts' and 'Default'
                        if ((row.pool_name === "Jailed Hosts" || row.pool_name === "Default") &&
                            b.id === "delete") {
                            current_class.push("disabled");

                        } else if (row.pool_name === "Default" && b.id === "manage_pool") {
                            // Disable dropdown button manage pool for pool: 'Default'
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });
        return columns;
    };

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "member_address") {
                return sortingFunctions$1.sortByIP(r0.name, r1.name, col.sort);
            } else if (col.id == "vlan") {
                return sortingFunctions$1.sortByNumber(r0.vlan, r1.vlan, col.sort);
            } else if (col.id == "member_type") {
                return sortingFunctions$1.sortByName(r0.type, r1.type, col.sort);
            }
        }
    }

    /* Used to Handle click on actions dropdown */
    function on_table_custom_event(event) {
        let events_managed = {
            "click_edit_member": click_edit_member,
            "click_delete_member": click_delete_member,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }


    const click_edit_member = (param) => {
        // Check if the modal ref exists
        if (!modal_add_member.value) {
            console.error('Modal reference is null in parent');
            return;
        }

        //  showEdit in modal expects this data
        const memberData = {
            name: param.row.name,
            type: param.row.type,
            vlan: param.row.vlan,
            member: param.row.member,
            pool: props.context.pool_id,
            pool_name: props.context.pool_name
        };

        modal_add_member.value.showEdit(memberData);
    };

    /* delete host pool member*/
    const click_delete_member = (param) => {

        // Check if the modal ref exists
        if (!modal_delete_pool.value) {
            console.error('Delete modal reference is null');
            return;
        }

        // Store the pool to be deleted
        current_pool_to_delete.value = props.context.pool_id;

        // Set the dynamic body text
        body_delete.value = body_delete_member_i18n_template.replace("%{member}", param.row.name);
        member_to_delete.value = { member: param.row.member, pool: current_pool_to_delete.value, pool_name: props.context.pool_name };

        // Show the delete confirmation modal with the body
        modal_delete_pool.value.show(
            body_delete.value,
            title_delete
        );
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$U, [
          createCommentVNode(" Table component "),
          createVNode(script$2w, {
            ref_key: "table_host_pools",
            ref: table_host_pools,
            table_id: table_id.value,
            csrf: unref(csrf),
            f_map_columns: map_table_def_columns,
            f_sort_rows: columns_sorting,
            onCustom_event: on_table_custom_event,
            get_extra_params_obj: get_extra_params_obj
          }, {
            custom_buttons: withCtx(() => [
              createBaseVNode("button", {
                class: "btn btn-link",
                type: "button",
                ref_key: "add_new_pool",
                ref: add_new_pool,
                onClick: addNewPool
              }, [...(_cache[0] || (_cache[0] = [
                createBaseVNode("i", { class: "fas fa-plus" }, null, -1 /* CACHED */)
              ]))], 512 /* NEED_PATCH */)
            ]),
            _: 1 /* STABLE */
          }, 8 /* PROPS */, ["table_id", "csrf"]),
          createBaseVNode("div", _hoisted_2$K, [
            createBaseVNode("div", _hoisted_3$F, [
              createBaseVNode("a", {
                class: "btn btn-secondary",
                style: {},
                href: pool_configuration_url
              }, [
                createBaseVNode("i", {
                  class: "fas fa-file-import",
                  "data-bs-toggle": "tooltip",
                  "data-bs-placement": "top",
                  title: _i18n('host_pools.import_hosts')
                }, null, 8 /* PROPS */, _hoisted_4$E),
                createTextVNode(" " + toDisplayString(_i18n('host_pools.import_hosts')), 1 /* TEXT */)
              ])
            ])
          ])
        ]),
        createVNode(script$11, {
          ref_key: "modal_add_member",
          ref: modal_add_member,
          context: __props.context,
          onAdd: handleAddMember,
          onEdit: handleEditMember
        }, null, 8 /* PROPS */, ["context"]),
        createVNode(script$2a, {
          ref_key: "modal_delete_pool",
          ref: modal_delete_pool,
          title: unref(title_delete),
          body: body_delete.value,
          onDelete: handleDeleteMember
        }, null, 8 /* PROPS */, ["title", "body"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$10.__file = "http_src/vue/page-manage-host-members.vue";

    function render(_ctx, _cache) {
      return (openBlock(), createElementBlock("p", null, "This contains the new policy page"))
    }

    const script$$ = {};


    script$$.render = render;
    script$$.__file = "http_src/vue/page-pool-policy.vue";

    var script$_ = {
      __name: 'modal-pcap-uploaded',
      props: {
      page: String,
      context: Object,
    },
      emits: ["load_interface"],
      setup(__props, { expose: __expose, emit: __emit }) {
    const emit = __emit;

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);

    onMounted(() => {});

    const show = () => {
      modal_id.value.show();
    };

    const close = () => {
      modal_id.value.close();
    };

    const loadNewInterface = function() {
      emit("load_interface");
      close();
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("success")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("analyze_pcap_ok")), 1 /* TEXT */)
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", null, [
            createBaseVNode("button", {
              type: "button",
              onClick: loadNewInterface,
              class: "btn btn-primary"
            }, toDisplayString(_i18n('switch_new_pcap_interface')), 1 /* TEXT */)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$_.__file = "http_src/vue/modal-pcap-uploaded.vue";

    const _hoisted_1$T = { class: "m-4 card card-shadow" };
    const _hoisted_2$J = { class: "card-body" };
    const _hoisted_3$E = { class: "table w-100 table-striped table-hover table-bordered" };
    const _hoisted_4$D = { class: "table_length" };
    const _hoisted_5$y = { class: "d-flex align-items-center" };
    const _hoisted_6$s = { class: "col-4" };
    const _hoisted_7$o = { class: "col-8 form-group d-flex" };
    const _hoisted_8$l = { class: "" };
    const _hoisted_9$j = { class: "d-flex align-items-center" };
    const _hoisted_10$g = { class: "col-4" };
    const _hoisted_11$e = { class: "col-8 form-group d-flex" };
    const _hoisted_12$b = { class: "form-check form-switch" };
    const _hoisted_13$a = ["checked", "disabled"];
    const _hoisted_14$a = {
      key: 0,
      class: "text-danger"
    };
    const _hoisted_15$a = ["innerHTML"];
    const _hoisted_16$a = { class: "d-flex me-1" };
    const _hoisted_17$a = ["disabled"];

    const MAX_SIZE = 25 /* MB */ * 1024 * 1024;

    var script$Z = {
      __name: 'page-upload-pcap',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const pcap_file = ref(null);
    const disable_save = ref(true);
    const error = ref('');
    const analyze_pcap_url = `${http_prefix}/lua/rest/v2/add/ntopng/analyze_pcap.lua`;
    const new_iface_id = ref(null);
    const modal_pcap_uploaded = ref(null);
    const isLoading = ref(true);
    let create_new_interface = false;

    onMounted(() => {
        isLoading.value = false;
    });

    const check_file = function () {
        if (pcap_file.value?.files?.length > 0) {
            if (pcap_file.value.files[0].size > MAX_SIZE) {
                error.value = `${i18n('upload_pcap_max_size')}. File size: ${NtopUtils$1.bytesToSize(pcap_file.value.files[0].size)}`;
                disable_save.value = true;
                pcap_file.value.classList.add('border');
                pcap_file.value.classList.add('border-danger');
            } else {
                error.value = '';
                disable_save.value = false;
                pcap_file.value.classList.remove('border');
                pcap_file.value.classList.remove('border-danger');
            }
        }
    };

    const upload_pcap = async function () {
        try {
            /* Check the file, if it's selected */
            if (!pcap_file.value?.files?.length > 0) {
                error.value = _i18n('please_select_a_file');
                return;
            }

            disable_save.value = true;
            error.value = '';
            isLoading.value = true;
            console.log(pcap_file);
            // Create a Form object to send the data.
            // The Files are no more sent correctly if not used a form
            // due to HTML5, hiding the file while sending normally over a connection
            const formData = new FormData();
            formData.append('pcap_file', pcap_file.value.files[0]);

            const response = await fetch(`${analyze_pcap_url}?create_new_interface=${(!props.context.pcap_interface) ? true : create_new_interface}`, {
                method: 'POST',
                body: formData,
            });

            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }

            const result = await response.json();

            if (result.rc === 0) {
                if (modal_pcap_uploaded.value) {
                    new_iface_id.value = result.rsp.new_ifid;
                    modal_pcap_uploaded.value.show();
                }
            } else {
                error.value = i18n("analyze_pcap_error");
            }
        } catch (e) {
            console.error('Upload error:', e);
            error.value = i18n("analyze_pcap_error");
        } finally {
            disable_save.value = true;
            isLoading.value = false;
        }
    };

    const change_create_interface_option = function () {
        create_new_interface = !create_new_interface;
    };

    const loadNewInterface = function () {
        window.open(`${http_prefix}/lua/flows_stats.lua?ifid=${new_iface_id.value}`);
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$T, [
          createVNode(script$2y, { isLoading: isLoading.value }, null, 8 /* PROPS */, ["isLoading"]),
          createBaseVNode("div", _hoisted_2$J, [
            createBaseVNode("table", _hoisted_3$E, [
              createBaseVNode("tbody", _hoisted_4$D, [
                createBaseVNode("tr", null, [
                  createBaseVNode("td", null, [
                    createBaseVNode("div", _hoisted_5$y, [
                      createBaseVNode("div", _hoisted_6$s, [
                        createBaseVNode("b", null, toDisplayString(_i18n('pcap_file')), 1 /* TEXT */),
                        _cache[0] || (_cache[0] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                        createBaseVNode("small", null, toDisplayString(_i18n('analyze_pcap_descr')), 1 /* TEXT */)
                      ]),
                      createBaseVNode("div", _hoisted_7$o, [
                        createBaseVNode("div", _hoisted_8$l, [
                          createBaseVNode("input", {
                            ref_key: "pcap_file",
                            ref: pcap_file,
                            class: "form-control",
                            type: "file",
                            name: "pcap",
                            accept: ".pcap",
                            onChange: check_file,
                            required: ""
                          }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
                        ])
                      ])
                    ])
                  ])
                ]),
                createBaseVNode("tr", null, [
                  createBaseVNode("td", null, [
                    createBaseVNode("div", _hoisted_9$j, [
                      createBaseVNode("div", _hoisted_10$g, [
                        createBaseVNode("b", null, toDisplayString(_i18n('create_new_pcap_iface')), 1 /* TEXT */),
                        _cache[1] || (_cache[1] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                        createBaseVNode("small", null, toDisplayString(_i18n('create_new_pcap_iface_descr')), 1 /* TEXT */)
                      ]),
                      createBaseVNode("div", _hoisted_11$e, [
                        createBaseVNode("div", _hoisted_12$b, [
                          createBaseVNode("input", {
                            class: "form-check-input",
                            type: "checkbox",
                            onChange: change_create_interface_option,
                            checked: !props.context.pcap_interface,
                            disabled: !props.context.pcap_interface
                          }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_13$a)
                        ])
                      ])
                    ])
                  ])
                ])
              ])
            ]),
            (error.value.length > 0)
              ? (openBlock(), createElementBlock("div", _hoisted_14$a, [
                  createBaseVNode("span", { innerHTML: error.value }, null, 8 /* PROPS */, _hoisted_15$a)
                ]))
              : createCommentVNode("v-if", true),
            createBaseVNode("div", _hoisted_16$a, [
              createBaseVNode("button", {
                class: "btn btn-primary",
                disabled: disable_save.value,
                onClick: upload_pcap
              }, toDisplayString(_i18n('upload_pcap')), 9 /* TEXT, PROPS */, _hoisted_17$a)
            ])
          ])
        ]),
        createVNode(script$_, {
          ref_key: "modal_pcap_uploaded",
          ref: modal_pcap_uploaded,
          onLoad_interface: loadNewInterface
        }, null, 512 /* NEED_PATCH */)
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$Z.__file = "http_src/vue/page-upload-pcap.vue";

    const _hoisted_1$S = { class: "m-2 mb-3" };


    var script$Y = {
      __name: 'page-redis-stats',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const table_id = ref('redis_stats');
    const table_redis_stats = ref(null);

    const map_table_def_columns = (columns) => {

        let map_columns = {
            "command": (value, row) => {
                return value.name
            },
            "hits": (value, row) => {
                return value
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id === "actions") {
                const visible_dict = {
                    timeseries: props.context.timeseries_available,
                };
                c.button_def_array.forEach((b) => {
                    if (!visible_dict[b.id]) {
                        b.class.push("disabled");
                    }
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    // This function handles the sorting of the columns
    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "command") {
                return sortingFunctions$1.sortByName(r0.command.name, r1.command.name, col.sort);
            } else if (col.id == "hits") {
                return sortingFunctions$1.sortByNumber(r0.hits, r1.hits, col.sort);
            }
        }

    }

    /* ************************************** */

    // Create the link to the timeseries
    function create_timeseries_link(row) {
        //timeseries_groups=redis;-1;redis:hits%2Bcommand:num_get;num_calls=true:true:false:false
        return `${http_prefix}/lua/monitor/redis_monitor.lua?page=historical&ifid=-1&timeseries_groups=redis;-1;redis:hits%2Bcommand:${row.command.key};num_calls=true:true:false:false`
    }

    /* ************************************** */

    // Handles the click on the timeseries button
    function click_button_timeseries(event) {
        const row = event.row;
        window.open(create_timeseries_link(row));
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_timeseries": click_button_timeseries,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */



    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$S, [
        createVNode(script$2w, {
          ref_key: "table_redis_stats",
          ref: table_redis_stats,
          table_id: table_id.value,
          csrf: props.context.csrf,
          f_map_columns: map_table_def_columns,
          f_sort_rows: columns_sorting,
          onCustom_event: on_table_custom_event
        }, null, 8 /* PROPS */, ["table_id", "csrf"])
      ]))
    }
    }

    };

    script$Y.__file = "http_src/vue/page-redis-stats.vue";

    /* *************************************************** */

    const height_per_row$2 = 62; /* px */

    var script$X = {
      __name: 'dashboard-timeseries',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        csrf: String,
        filters: Object,
    },
      setup(__props, { expose: __expose }) {

    const chart_type = ref(ntopChartApex.typeChart.TS_LINE);
    const chart = ref(null);
    const timeseries_groups = ref([]);
    const group_option_mode = timeseriesUtils.getGroupOptionMode('1_chart_x_yaxis');
    const height = ref(null);
    const ts_request = ref([]);

    /* *************************************************** */

    const props = __props;

    /* *************************************************** */

    /* Return the base url of the REST API */
    const base_url = computed$1(() => {
        return `${http_prefix}${props.params.url}`;
    });

    /* *************************************************** */

    function substitute_ifid(params_to_format, current_ifid) {
        let new_formatted_params = {};
        for (const param in (params_to_format)) {
            if (params_to_format[param].contains('$IFID$')) {
                /* Contains $IFID$, substitute with the interface id */
                new_formatted_params[param] = params_to_format[param].replace('$IFID$', current_ifid);
            } else {
                /* does NOT Contains $IFID$, add the plain param */
                new_formatted_params[param] = params_to_format[param];
            }
        }

        return new_formatted_params;
    }

    /* *************************************************** */

    function substitute_exporter(params_to_format, current_exporter) {
        let new_formatted_params = {};
        for (const param in (params_to_format)) {
            if (params_to_format[param].contains('$EXPORTER$')) {
                /* Contains $EXPORTER$, substitute with the interface id */
                new_formatted_params[param] = params_to_format[param].replace('$EXPORTER$', current_exporter);
            } else {
                /* does NOT Contains $EXPORTER$, add the plain param */
                new_formatted_params[param] = params_to_format[param];
            }
        }

        return new_formatted_params;
    }

    /* *************************************************** */

    function substitute_network(params_to_format, current_network) {
        let new_formatted_params = {};
        for (const param in (params_to_format)) {
            if (params_to_format[param].contains('$NETWORK$')) {
                /* Contains $NETWORK$, substitute with the interface id */
                new_formatted_params[param] = params_to_format[param].replace('$NETWORK$', current_network);
            } else {
                /* does NOT Contains $NETWORK$, add the plain param */
                new_formatted_params[param] = params_to_format[param];
            }
        }

        return new_formatted_params;
    }

    /* *************************************************** */

    /* This function is used to substitute to the $IFID$ found in the
     * configuration the correct interface id
     */
    async function format_ifids(params_to_format) {
        if (ts_request.value.length > 0) {
            /* Already populated, return */
            return;
        }
        const ifid_url = "lua/rest/v2/get/ntopng/interfaces.lua";
        const ifid_list = await ntopng_utility.http_request(`${http_prefix}/${ifid_url}`) || [];
        ifid_list.forEach((iface) => {
            let new_formatted_params = substitute_ifid(params_to_format, iface.ifid);
            new_formatted_params.source_def = [iface.ifid];
            ts_request.value.push(new_formatted_params);
        });
    }

    /* *************************************************** */

    /* This function is used to substitute to the $EXPORTER$ found in the
     * configuration the correct flow exporter
     */
    async function format_exporters(params_to_format) {
        if (ts_request.value.length > 0) {
            /* Already populated, return */
            return;
        }
        const exporters_url = "lua/pro/rest/v2/get/flowdevices/stats.lua";
        const exporters_list = await ntopng_utility.http_request(`${http_prefix}/${exporters_url}?ifid=${props.ifid}&gui=true`) || [];
        if (exporters_list) {
            exporters_list.forEach((exporter) => {
                if (exporter) {
                    let new_formatted_params = substitute_exporter(params_to_format, exporter.probe_ip);
                    new_formatted_params = substitute_ifid(new_formatted_params, exporter.ifid);
                    new_formatted_params.source_def = [exporter.ifid, exporter.probe_ip];
                    ts_request.value.push(new_formatted_params);
                }
            });
        }
    }

    /* *************************************************** */

    /* This function is used to substitute to the $NETWORK$ found in the
     * configuration in the correct networks
     */
    async function format_networks(params_to_format) {
        if (ts_request.value.length > 0) {
            /* Already populated, return */
            return;
        }
        const networks_url = "lua/rest/v2/get/network/networks.lua";
        const networks_list = await ntopng_utility.http_request(`${http_prefix}/${networks_url}?ifid=${props.ifid}`) || [];
        if (networks_list) {
            networks_list.forEach((network) => {
                if (network) {
                    let new_formatted_params = substitute_network(params_to_format, network.id);
                    new_formatted_params = substitute_ifid(new_formatted_params, props.ifid);
                    new_formatted_params.source_def = [props.ifid, network.id];
                    ts_request.value.push(new_formatted_params);
                }
            });
        }
    }

    /* *************************************************** */

    /* This function is used to transform the $ANY$ params in the 
     * correct value (e.g. $ANY_IFID$ -> list of all ifid)
     */
    async function resolve_any_params() {
        /* Clear the Array */
        ts_request.value = [];
        /* Here possible ANY, can be found in the post_params */
        const params = props.params.post_params?.ts_requests;
        for (const any_param in (params || {})) {
            switch (any_param) {
                case '$ANY_IFID$':
                    await format_ifids(params[any_param]);
                    break;
                case '$ANY_EXPORTER$':
                    await format_exporters(params[any_param]);
                    break;
                case '$ANY_NETWORK$':
                    await format_networks(params[any_param]);
                    break;
                default:
                    let new_formatted_params = substitute_ifid(params[any_param], props.ifid);
                    new_formatted_params.source_def = [props.ifid];
                    ts_request.value.push(new_formatted_params);
                    break;
            }
        }
    }

    /* *************************************************** */

    /* The source_type can be found on the json and the source_array is automatically generated
     * by using the source_type
     */
    async function get_timeseries_groups_from_metric(metric_schema, source_def) {
        const status = {
            epoch_begin: props.epoch_begin,
            epoch_end: props.epoch_end,
        };
        const source_type = metricsManager.get_source_type_from_id(props.params?.source_type);
        const source_array = await metricsManager.get_source_array_from_value_array(http_prefix, source_type, source_def);
        const metric = await metricsManager.get_metric_from_schema(http_prefix, source_type, source_array, metric_schema, null, status, true /* Include empty TS */);
        const ts_group = metricsManager.get_ts_group(source_type, source_array, metric, { past: false });
        return ts_group;
    }

    /* *************************************************** */

    async function retrieve_basic_info() {
        /* Return the timeseries group, info found in the json */
        if (timeseries_groups.value.length == 0) {
            for (const value of ts_request.value) {
                const metric_schema = value?.ts_schema;
                const source_def = value.source_def;
                delete value.source_def; /* Remove the property otherwise it's going to be added to the REST */
                const group = await get_timeseries_groups_from_metric(metric_schema, source_def);
                timeseries_groups.value.push(group);
            }
        }
    }

    /* *************************************************** */

    /* Remove the property otherwise it's going to be added to the REST */
    function remove_extra_params() {
        for (const value of ts_request.value) {
            if (value.source_def) {
                delete value.source_def;
            }
        }
    }

    /* *************************************************** */

    /* This function run the REST API with the data */
    async function get_chart_options() {
        await resolve_any_params();
        await retrieve_basic_info();
        remove_extra_params();
        const url = base_url.value;
        const post_params = {
            csrf: props.csrf,
            ifid: props.ifid,
            epoch_begin: props.epoch_begin,
            epoch_end: props.epoch_end,
            ...props.params.post_params,
            ...{
                ts_requests: ts_request.value
            }
        };

        /* Have to be used this get_component_data, in order to create report too */
        let result = await props.get_component_data(url,
            /* Note: passing query params (not required) to be used by check_diff_params() */
            { ifid: props.ifid, epoch_begin: props.epoch_begin, epoch_end: props.epoch_end },
            post_params, props.epoch_begin);

        /* Format the result in the format needed by Dygraph */
        result = timeseriesUtils.tsArrayToOptionsArray(result, timeseries_groups.value, group_option_mode, '');
        if (result[0]) {
            result[0].height = height.value;
        }
        return result?.[0];
    }

    /* *************************************************** */

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], (cur_value, old_value) => {
        refreshChart();
    }, { flush: 'pre', deep: true });

    /* *************************************************** */

    /* Run the init here */
    onBeforeMount(async () => {
        await init();
    });

    /* *************************************************** */

    onMounted(async () => { });

    /* *************************************************** */

    /* Defining the needed info by the get_chart_options function */
    async function init() {
        height.value = (props.max_height || 4) * height_per_row$2;
    }

    /* *************************************************** */

    /* Refresh function */
    async function refreshChart() {
        if (chart.value) {
            const result = await get_chart_options();
            chart.value.update_chart_series(result.data);
        }
    }

    __expose({ refreshChart });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", null, [
        createVNode(script$2i, {
          ref_key: "chart",
          ref: chart,
          id: __props.id,
          chart_type: chart_type.value,
          base_url_request: base_url.value,
          get_custom_chart_options: get_chart_options,
          register_on_status_change: false,
          disable_fixed_height: true
        }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"])
      ]))
    }
    }

    };

    script$X.__file = "http_src/vue/dashboard-timeseries.vue";

    const _hoisted_1$R = { class: "button-group mb-2 d-flex align-items-center" };
    const _hoisted_2$I = { class: "dropdown me-3 d-flex" };
    const _hoisted_3$D = { class: "no-wrap d-flex align-items-center filters-label me-2" };
    const _hoisted_4$C = {
      key: 0,
      class: "dropdown me-3 d-flex"
    };
    const _hoisted_5$x = { class: "no-wrap d-flex align-items-center filters-label me-2" };
    const _hoisted_6$r = {
      key: 0,
      class: "position-relative",
      style: {"height":"330px"}
    };
    const _hoisted_7$n = { class: "widget-name" };
    const _hoisted_8$k = { class: "m-0" };
    const _hoisted_9$i = { class: "position-relative" };

    const seconds_one_day$1 = 3600 * 24;

    // render table in httpdocs/tables_config/as_stats.json if context.ASNModeEnabled is false, else render the IXP mode table: httpdocs/tables_config/as_stats_ixp_mode.json

    var script$W = {
      __name: 'page-as-stats',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const current_time = Math.floor(Date.now() / 1000);
    const table_id = computed$1(() => {
        return props.context?.ASNModeEnabled ? 'as_stats_ixp_mode' : 'as_stats';
    });

    const chart_title = _i18n('top_active_asn');
    const timeseries_id = ref('top_asn');
    const loading = ref(true);
    const timeseries_chart = ref(null);
    const table_as_stats = ref(null);
    const epoch_begin = ref(current_time - seconds_one_day$1); // Get one day ago
    const epoch_end = ref(current_time);
    const showSankey = props.context.showSankey;
    const showChart = ref(props.context.isEnterprise);
    const current_selected_option = ref([]);
    const customerIcon = '<i class="fa-solid fa-house-flag" data-bs-toggle="tooltip" data-bs-placement="top" title="' + _i18n("asn_configuration.customer_asn_title") + '"></i>';
    const subCustomerIcon = '<i class="fa-solid fa-house-laptop" data-bs-toggle="tooltip" data-bs-placement="top" title="' + _i18n("asn_configuration.sub_customer_asn_title") + '"></i>';
    const remoteIcon = '<i class="fa-solid fa-house-fire" data-bs-toggle="tooltip" data-bs-placement="top" title="' + _i18n("asn_configuration.remote_asn_title") + '"></i>';
    const timeseries_key = ref(false);
    const asn_type_option = ref([{
        key: "show_as",
        value: "all",
        label: i18n("asn_configuration.all_asn")
    }, {
        key: "show_as",
        value: "my_as",
        label: i18n("asn_configuration.customer_asn_title")
    }, {
        key: "show_as",
        value: "my_customer_as",
        label: i18n("asn_configuration.sub_customer_asn_title")
    }, {
        key: "show_as",
        value: "remote_as",
        label: i18n("asn_configuration.remote_asn_title")
    }, {
        key: "show_as",
        value: "other_as",
        label: i18n("asn_configuration.other_asn")
    }]);
    const resolution_options = ref([
        { value: "30_min", label: i18n('show_alerts.presets.30_min'), currently_active: false },
        { value: "hour", label: i18n('show_alerts.presets.hour'), currently_active: false },
        { value: "12_hours", label: i18n('show_alerts.presets.12_hours'), currently_active: false },
        { value: "day", label: i18n('show_alerts.presets.day'), currently_active: true },
        { value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
    ]);
    const selected_resolution = ref(resolution_options[3]);

    const params = {
        post_params: {
            limit: 180,
            version: 4,
            ts_requests: {
                "$IFID$": {
                    ts_query: `ifid:$IFID$`,
                    ts_schema: `top:asn:traffic`,
                }
            }
        },
        source_type: "interface"
    };

    const ts_query = {
        ts_query: `ifid:$IFID$,asn:$ASN$`,
        ts_schema: `asn:traffic`,
    };

    /* *************************************************** */

    const set_component_attr = async (attr, value) => {
        component[attr] = value;
    };

    /* *************************************************** */

    onBeforeMount(async () => {
        const selected_as = ntopng_url_manager.get_url_entry("show_as");
        if (selected_as) {
            const option = asn_type_option.value.find((el) => el.value === selected_as);
            if (option) {
                current_selected_option.value = option;
            }
        } else {
            current_selected_option.value = asn_type_option.value[0];
        }
        ntopng_url_manager.set_key_to_url(current_selected_option.value.key, current_selected_option.value.value);
        let requested_resolution = ntopng_url_manager.get_url_entry("chart_resolution");
        if (!requested_resolution) {
            const resolution = localStorage.getItem('ntopng.timeseries.chartResolution.' + timeseries_id);
            if (resolution) {
                requested_resolution = resolution;
            } else {
                requested_resolution = "day"; // Default 1 day
            }
        }
        resolution_options.value.forEach(el => {
            if (el.value === requested_resolution) {
                selected_resolution.value = el;
            }
        });
        select_resolution(selected_resolution.value, true);
    });

    /* *************************************************** */

    const select_resolution = async (value, skip_reload) => {
        const timeframes = ntopng_utility.get_timeframes_dict();
        if (timeframes[value.value]) {
            epoch_begin.value = epoch_end.value - timeframes[value.value];
        }
        if (!skip_reload) {
            timeseries_chart.value.refreshChart();
        }
        localStorage.setItem('ntopng.timeseries.chartResolution.' + timeseries_id, value.value);
    };

    /* *************************************************** */

    const add_filter = async (value) => {
        loading.value = true;
        current_selected_option.value = value;
        ntopng_url_manager.set_key_to_url(current_selected_option.value.key, current_selected_option.value.value);
        timeseries_key.value = !timeseries_key.value;
        table_as_stats.value.refresh_table(false);
    };

    /* *************************************************** */

    /* Callback to request REST data from components */
    const get_component_data = async (url, query_params, post_params) => {
        loading.value = true;
        query_params.csrf = props.context.csrf;
        query_params.show_as = current_selected_option.value.value;
        const url_params = ntopng_url_manager.obj_to_url_params(query_params);
        const top_url = `${http_prefix}/lua/rest/v2/get/asn/get_top_asn.lua?${url_params}`;
        const top_data = await ntopng_utility.http_request(top_url);
        const ts_requests = [];
        top_data?.forEach((el) => {
            const tmp_query = { ...ts_query };
            tmp_query.ts_query = tmp_query.ts_query.replace('$IFID$', props.context.ifid);
            tmp_query.ts_query = tmp_query.ts_query.replace('$ASN$', el.asn);
            tmp_query.tskey = `${el.asn}`;
            tmp_query.ts_unify = true;
            ts_requests.push(tmp_query);
        });
        post_params.ts_requests = ts_requests;
        const data_url = `${http_prefix}/lua/pro/rest/v2/get/timeseries/ts_multi.lua?${url_params}`;
        const data = await ntopng_utility.http_post_request(data_url, post_params);
        loading.value = false;
        return data;
    };

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    const formatIconAS = (value, row) => {
        if (!value) {
            return ''
        }
        let importantASNIcon = '';
        if (row.is_customer_asn) {
            importantASNIcon = customerIcon;
        } else if (row.is_sub_customer_asn) {
            importantASNIcon = subCustomerIcon;
        } else if (row.is_remote_asn) {
            importantASNIcon = remoteIcon;
        }
        return importantASNIcon
    };

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "asname": (value, row) => {
                // value is ASNumber
                // row is the json element
                const asName = row["asname"];

                let return_value = "";
                let icon = formatIconAS(value, row);

                if (asName.length > 0) {
                    return_value += `${row["asname"]}`;
                    if (row["asn"] != 0) {
                        return_value += ` [ <A class='ntopng-external-link' href='https://stat.ripe.net/app/launchpad/S1_${row["asn"]}_C13C31C4C34C9C22C28C20C6C7C26C29C30C14C17C2C21C33C16C10'>RIPEstat <i class='fas fa-external-link-alt fa-sm'></i></A>`;
                        return_value += ` | <A class='ntopng-external-link' href='https://www.peeringdb.com/asn/${row["asn"]}'>PeeringDB <i class='fas fa-external-link-alt fa-sm'></i></A> ]`;
                    }
                }
                return_value += icon;
                return return_value;
            },
            "asn": (value, row) => {
                let return_value = row["asn"];
                return return_value;
            },
            "hosts": (value, row) => {
                return formatterUtils.getFormatter("number")(value);
            },
            "seen_since": (value, row) => {
                // `seen_since` might require formatting, e.g., date formatting.
                return formatterUtils.formatDateTime(value);
            },
            "score": (value, row) => {
                // Assuming `score` is a number that might require some formatting.
                return formatterUtils.getFormatter("number")(value);
            },
            "breakdown": (value, row) => {
                return NtopUtils$1.createBreakdown(value["bytes_sent"], value["bytes_rcvd"], "Sent", "Rcvd")
            },
            "throughput": (value, row) => {
                return formatterUtils.getFormatter("bps")(value);
            },
            "traffic": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
            "alerted_flows": (value, row) => {
                return formatterUtils.getFormatter("number")(value);
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    host: true,
                    flows: true,
                    exporters_stats: showSankey,
                    timeseries: props.context.showTimeseries,
                };
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        // if is not defined is enabled
                        if (!visible_dict[b.id]) {
                            current_class.push("disabled");
                        } else if (row.asn === 0 && (b.id === "exporters_stats")) {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function click_button_exporters_stats(event) {
        const row = event.row;
        window.location.href = `${http_prefix}/lua/as_overview.lua?asn=${row["asn"]}`;
    }

    /* ************************************** */

    function click_button_host(event) {
        const row = event.row;
        window.location.href = `${http_prefix}/lua/hosts_stats.lua?asn=${row["asn"]}`;
    }

    /* ************************************** */

    function click_button_flows(event) {
        const row = event.row;
        window.location.href = `${http_prefix}/lua/flows_stats.lua?asn=${row["asn"]}`;
    }

    /* ************************************** */

    function click_button_timeseries(event) {
        const row = event.row;
        window.location.href = `${http_prefix}/lua/as_overview.lua?asn=${row["asn"]}&page=historical`;
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_host": click_button_host,
            "click_button_flows": click_button_flows,
            "click_button_exporters_stats": click_button_exporters_stats,
            "click_button_timeseries": click_button_timeseries,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "name") {
                return sortingFunctions$1.sortByName(r0.asname, r1.asname, col.sort);
            } if (col.id == "as_number") {
                return sortingFunctions$1.sortByNumber(r0.asn, r1.asn, col.sort);
            } else if (col.id == "num_hosts") {
                return sortingFunctions$1.sortByNumber(r0.num_hosts, r1.num_hosts, col.sort);
            } else if (col.id == "seen_since") {
                return sortingFunctions$1.sortByNumber(r0.seen_since, r1.seen_since, col.sort);
            } else if (col.id == "avg_host_score") {
                return sortingFunctions$1.sortByNumber(r0.avg_host_score, r1.avg_host_score, col.sort);
            } else if (col.id == "score") {
                return sortingFunctions$1.sortByNumber(r0.score, r1.score, col.sort);
            } else if (col.id == "throughput") {
                return sortingFunctions$1.sortByNumber(r0.throughput, r1.throughput, col.sort);
            } else if (col.id == "traffic") {
                return sortingFunctions$1.sortByNumber(r0.traffic, r1.traffic, col.sort);
            }
            else if (col.id == "alerted_flows") {
                return sortingFunctions$1.sortByNumber(r0.alerted_flows, r1.alerted_flows, col.sort);
            }
        }
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", null, [
        createBaseVNode("div", _hoisted_1$R, [
          createBaseVNode("div", _hoisted_2$I, [
            createBaseVNode("span", _hoisted_3$D, [
              createBaseVNode("b", null, toDisplayString(_i18n("asn_configuration.filter")) + ": ", 1 /* TEXT */)
            ]),
            createVNode(script$2H, {
              selected_option: current_selected_option.value,
              "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((current_selected_option).value = $event)),
              theme: "bootstrap-5",
              options: asn_type_option.value,
              onSelect_option: add_filter,
              dropdown_size: 'small'
            }, null, 8 /* PROPS */, ["selected_option", "options"])
          ]),
          ((showChart.value) && props.context.showTimeseries)
            ? (openBlock(), createElementBlock("div", _hoisted_4$C, [
                createBaseVNode("span", _hoisted_5$x, [
                  createBaseVNode("b", null, toDisplayString(_i18n("resolution")) + ": ", 1 /* TEXT */)
                ]),
                createVNode(script$2H, {
                  selected_option: selected_resolution.value,
                  "onUpdate:selected_option": _cache[1] || (_cache[1] = $event => ((selected_resolution).value = $event)),
                  theme: "bootstrap-5",
                  options: resolution_options.value,
                  onSelect_option: select_resolution,
                  dropdown_size: 'small'
                }, null, 8 /* PROPS */, ["selected_option", "options"])
              ]))
            : createCommentVNode("v-if", true)
        ]),
        ((showChart.value) && props.context.showTimeseries)
          ? (openBlock(), createElementBlock("div", _hoisted_6$r, [
              createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
              createBaseVNode("div", _hoisted_7$n, [
                createBaseVNode("h6", _hoisted_8$k, toDisplayString(unref(chart_title)), 1 /* TEXT */)
              ]),
              createVNode(Transition, {
                name: "add-effect",
                mode: "out-in"
              }, {
                default: withCtx(() => [
                  (openBlock(), createBlock(script$X, {
                    ref_key: "timeseries_chart",
                    ref: timeseries_chart,
                    key: timeseries_key.value,
                    id: timeseries_id.value,
                    epoch_begin: epoch_begin.value,
                    epoch_end: epoch_end.value,
                    i18n_title: unref(chart_title),
                    ifid: props.context.ifid.toString(),
                    max_width: 12,
                    max_height: 4,
                    params: params,
                    get_component_data: get_component_data,
                    set_component_attr: set_component_attr,
                    csrf: props.context.csrf,
                    show_resolution: true,
                    select_resolution: select_resolution,
                    resolution_options: resolution_options.value
                  }, null, 8 /* PROPS */, ["id", "epoch_begin", "epoch_end", "i18n_title", "ifid", "csrf", "resolution_options"]))
                ]),
                _: 1 /* STABLE */
              })
            ]))
          : createCommentVNode("v-if", true),
        createBaseVNode("div", _hoisted_9$i, [
          createVNode(script$2w, {
            ref_key: "table_as_stats",
            ref: table_as_stats,
            table_id: table_id.value,
            csrf: props.context.csrf,
            showLoading: true,
            f_map_columns: map_table_def_columns,
            f_sort_rows: columns_sorting,
            get_extra_params_obj: get_extra_params_obj,
            onCustom_event: on_table_custom_event
          }, null, 8 /* PROPS */, ["table_id", "csrf"])
        ])
      ]))
    }
    }

    };

    var css_248z$i = "\n.add-effect-move[data-v-67d7feb6],\n\n.add-effect-enter-active[data-v-67d7feb6],\n.add-effect-leave-active[data-v-67d7feb6] {\n    transition: all 0.35s ease;\n}\n\n/* Transform: positive pixels, the effects let enters the component\n * from the right, negative pixels from the left\n */\n.add-effect-enter-from[data-v-67d7feb6] {\n    opacity: 0;\n    transform: translateX(-60px);\n}\n.add-effect-leave-to[data-v-67d7feb6] {\n    opacity: 0;\n    transform: translateX(0px);\n}\n\n/* ensure leaving items are taken out of layout flow so that moving\n   animations can be calculated correctly. */\n.add-effect-leave-active[data-v-67d7feb6] {\n    position: absolute;\n}\n";
    styleInject(css_248z$i);

    script$W.__scopeId = "data-v-67d7feb6";
    script$W.__file = "http_src/vue/page-as-stats.vue";

    const _hoisted_1$Q = { class: "button-group mb-2 d-flex align-items-center" };
    const _hoisted_2$H = { class: "dropdown me-3 d-flex" };
    const _hoisted_3$C = { class: "no-wrap d-flex align-items-center filters-label me-2" };
    const _hoisted_4$B = {
      key: 0,
      class: "position-relative",
      style: {"height":"330px"}
    };
    const _hoisted_5$w = { class: "widget-name" };
    const _hoisted_6$q = { class: "m-0" };
    const _hoisted_7$m = { class: "position-relative" };

    const seconds_one_day = 3600 * 24;

    // render table in httpdocs/tables_config/as_stats.json if context.ASNModeEnabled is false, else render the IXP mode table: httpdocs/tables_config/as_stats_ixp_mode.json
    const system_interface_id = '-1';


    var script$V = {
      __name: 'page-exporters-interfaces-stats',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const current_time = Math.floor(Date.now() / 1000);
    const table_id = computed$1(() => {
        return 'exporters_interfaces_stats';
    });

    const chart_title = _i18n('top_active_interfaces');
    const timeseries_id = ref('top_interfaces');
    const loading = ref(true);
    const timeseries_chart = ref(null);
    const table_exporters_interfaces_stats = ref(null);
    const epoch_begin = ref(current_time - seconds_one_day); // Get one day ago
    const epoch_end = ref(current_time);
    const showSankey = props.context.showSankey;
    const showChart = ref(props.context.isEnterprise);
    const current_selected_option = ref([]);
    const interfaces_filters_rest_url = `${http_prefix}/lua/pro/rest/v2/get/flowdevices/interfaces_role_filters.lua`;
    const interfaces_role_filters = ref([]);
    const timeseries_key = ref(true);
    const params = {
        post_params: {
            limit: 180,
            version: 4,
            ts_requests: {
                "$IFID$": {
                    ts_query: `ifid:$IFID$`,
                    ts_schema: `top:flowdev_port:traffic`,
                }
            }
        },
        source_type: "flow_device"
    };

    const ts_query = {
        ts_query: `ifid:$IFID$,device:$DEVICE$,port:$PORT$`,
        ts_schema: `flowdev_port:traffic`,
    };

    /* *************************************************** */

    const set_component_attr = async (attr, value) => {
        component[attr] = value;
    };

    /* *************************************************** */

    onBeforeMount(async () => {
        const filters_list = await ntopng_utility.http_request(interfaces_filters_rest_url);
        // Update the list of filters with the retrieved data
        if (filters_list && filters_list.length > 0) {
            interfaces_role_filters.value = filters_list;
        }
        // Select the filter based on the url (if given) or the first element 
        const selected_interface_role = ntopng_url_manager.get_url_entry("interface_role");
        if (selected_interface_role) {
            const option = interfaces_role_filters.value.find((el) => el.value === selected_interface_role);
            if (option) {
                current_selected_option.value = option;
            }
        } else {
            current_selected_option.value = interfaces_role_filters.value[0];
        }
        ntopng_url_manager.set_key_to_url(current_selected_option.value.key, current_selected_option.value.value);
    });

    /* *************************************************** */

    const add_filter = async (value) => {
        loading.value = true;
        current_selected_option.value = value;
        ntopng_url_manager.set_key_to_url(current_selected_option.value.key, current_selected_option.value.value);
        timeseries_key.value = !timeseries_key.value;
        table_exporters_interfaces_stats.value.refresh_table(false);
    };

    /* *************************************************** */

    /* Callback to request REST data from components */
    const get_component_data = async (url, query_params, post_params) => {
        loading.value = true;
        query_params.csrf = props.context.csrf;
        query_params.interface_role = current_selected_option.value.value;
        const url_params = ntopng_url_manager.obj_to_url_params(query_params);
        const top_url = `${http_prefix}/lua/pro/rest/v2/get/flowdevices/get_top_exporters_interfaces.lua?${url_params}`;
        const top_data = await ntopng_utility.http_request(top_url);
        const ts_requests = [];
        top_data?.forEach((el) => {
            const tmp_query = { ...ts_query };
            tmp_query.ts_query = tmp_query.ts_query.replace('$IFID$', el.ifid); /* SNMP timeseries are in the system interface */
            tmp_query.ts_query = tmp_query.ts_query.replace('$DEVICE$', el.exporter_ip);
            tmp_query.ts_query = tmp_query.ts_query.replace('$PORT$', el.interface_id);
            tmp_query.tskey = `${el.interface_id}`;
            tmp_query.ts_unify = true;
            ts_requests.push(tmp_query);
        });
        post_params.ts_requests = ts_requests;
        const data_url = `${http_prefix}/lua/pro/rest/v2/get/timeseries/ts_multi.lua?${url_params}`;
        const data = await ntopng_utility.http_post_request(data_url, post_params);
        loading.value = false;
        return data;
    };

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "exporter_ip": (value, row) => {
                const url = linksUtils.getExporterDetailsPageURL({ ip: value, probe_uuid: row.probe_uuid, exporter_uuid: row.exporter_uuid }, http_prefix);
                return formatterUtils.formatHTMLaTagNameValue(value, row.exporter_name, url, true)
            },
            "interface_name": (value, row) => {
                const url = linksUtils.getFlowExporterInterfaceOverviewPageURL(row.exporter_ip, row.interface_id, http_prefix);
                return formatterUtils.formatHTMLaTagNameValue(row.interface_id, value, url, false)
            },
            "role": (value, row) => {
                if (!dataUtils.isEmptyString(value.label)) {
                    return value.label
                }
                return "";
            },
            "bytes_sent": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
            "bytes_rcvd": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
            "total_bytes": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    host: true,
                    flows: true,
                    exporters_stats: showSankey,
                    timeseries: props.context.showTimeseries,
                    hosts: true,
                };
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        // if is not defined is enabled
                        if (!visible_dict[b.id]) {
                            current_class.push("disabled");
                        } else if (row.asn === 0 && (b.id === "exporters_stats")) {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function click_button_exporters_stats(event) {
        const row = event.row;
        window.location.href = linksUtils.getFlowExporterInterfaceOverviewPageURL(row["exporter_ip"], row["interface_id"], http_prefix);
    }

    /* ************************************** */

    function click_button_flows(event) {
        const row = event.row;
        window.location.href = `${http_prefix}/lua/flows_stats.lua?deviceIP=${row["exporter_ip"]}&ifIdx=${row["interface_id"]}`;
    }

    /* ************************************** */

    function click_button_hosts(event) {
        const row = event.row;
        const filters = {
            deviceIP: row.exporter_ip,
            ifIdx: row.interface_id
        };
        window.location.href = linksUtils.getAggregatedFlowsURL(filters, "host", http_prefix);
    }

    /* ************************************** */

    function click_button_timeseries(event) {
        const row = event.row;
        const url = linksUtils.getExporterInterfaceDetailsPageURL(row.exporter_ip, row.interface_id, row.ifid, http_prefix);
        window.location.href = url;
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_flows": click_button_flows,
            "click_button_exporters_stats": click_button_exporters_stats,
            "click_button_timeseries": click_button_timeseries,
            "click_button_hosts": click_button_hosts,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "total_bytes") {
                return sortingFunctions$1.sortByNumber(r0.total_bytes, r1.total_bytes, col.sort);
            } else if (col.id == "exporter_ip") {
                return sortingFunctions$1.sortByName(r0.exporter_ip, r1.exporter_ip, col.sort);
            } else if (col.id == "interface_name") {
                return sortingFunctions$1.sortByName(r0.interface_name, r1.interface_name, col.sort);
            } else if (col.id == "role") {
                return sortingFunctions$1.sortByName(r0.role?.value, r1.role?.value, col.sort);
            } else if (col.id == "bytes_sent") {
                return sortingFunctions$1.sortByNumber(r0.bytes_sent, r1.bytes_sent, col.sort);
            } else if (col.id == "bytes_rcvd") {
                return sortingFunctions$1.sortByNumber(r0.bytes_rcvd, r1.bytes_rcvd, col.sort);
            }
            // Default option
            return sortingFunctions$1.sortByName(r0.exporter_ip, r1.exporter_ip, col.sort);
        }
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", null, [
        createBaseVNode("div", _hoisted_1$Q, [
          createBaseVNode("div", _hoisted_2$H, [
            createBaseVNode("span", _hoisted_3$C, [
              createBaseVNode("b", null, toDisplayString(_i18n("exporters_interfaces_configuration.filter")) + ": ", 1 /* TEXT */)
            ]),
            createVNode(script$2H, {
              selected_option: current_selected_option.value,
              "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((current_selected_option).value = $event)),
              theme: "bootstrap-5",
              options: interfaces_role_filters.value,
              onSelect_option: add_filter
            }, null, 8 /* PROPS */, ["selected_option", "options"])
          ])
        ]),
        ((showChart.value) && props.context.showTimeseries)
          ? (openBlock(), createElementBlock("div", _hoisted_4$B, [
              createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
              createBaseVNode("div", _hoisted_5$w, [
                createBaseVNode("h6", _hoisted_6$q, toDisplayString(unref(chart_title)), 1 /* TEXT */)
              ]),
              createVNode(Transition, {
                name: "add-effect",
                mode: "out-in"
              }, {
                default: withCtx(() => [
                  (openBlock(), createBlock(script$X, {
                    ref_key: "timeseries_chart",
                    ref: timeseries_chart,
                    key: timeseries_key.value,
                    id: timeseries_id.value,
                    epoch_begin: epoch_begin.value,
                    epoch_end: epoch_end.value,
                    i18n_title: unref(chart_title),
                    ifid: system_interface_id,
                    max_width: 12,
                    max_height: 4,
                    params: params,
                    get_component_data: get_component_data,
                    set_component_attr: set_component_attr,
                    csrf: props.context.csrf
                  }, null, 8 /* PROPS */, ["id", "epoch_begin", "epoch_end", "i18n_title", "csrf"]))
                ]),
                _: 1 /* STABLE */
              })
            ]))
          : createCommentVNode("v-if", true),
        createBaseVNode("div", _hoisted_7$m, [
          createVNode(script$2w, {
            ref_key: "table_exporters_interfaces_stats",
            ref: table_exporters_interfaces_stats,
            table_id: table_id.value,
            csrf: props.context.csrf,
            showLoading: true,
            f_map_columns: map_table_def_columns,
            f_sort_rows: columns_sorting,
            get_extra_params_obj: get_extra_params_obj,
            onCustom_event: on_table_custom_event
          }, null, 8 /* PROPS */, ["table_id", "csrf"])
        ])
      ]))
    }
    }

    };

    var css_248z$h = "\n.add-effect-move[data-v-10572e48],\n\n.add-effect-enter-active[data-v-10572e48],\n.add-effect-leave-active[data-v-10572e48] {\n    transition: all 0.35s ease;\n}\n\n/* Transform: positive pixels, the effects let enters the component\n * from the right, negative pixels from the left\n */\n.add-effect-enter-from[data-v-10572e48] {\n    opacity: 0;\n    transform: translateX(-60px);\n}\n.add-effect-leave-to[data-v-10572e48] {\n    opacity: 0;\n    transform: translateX(0px);\n}\n\n/* ensure leaving items are taken out of layout flow so that moving\n   animations can be calculated correctly. */\n.add-effect-leave-active[data-v-10572e48] {\n    position: absolute;\n}\n";
    styleInject(css_248z$h);

    script$V.__scopeId = "data-v-10572e48";
    script$V.__file = "http_src/vue/page-exporters-interfaces-stats.vue";

    const _hoisted_1$P = { key: 0 };
    const _hoisted_2$G = {
      key: 1,
      style: {"position":"relative","display":"flex","flex-direction":"column","justify-content":"center","align-items":"center","width":"100%","height":"100%","min-height":"250px","padding":"5% 20px","color":"#666"}
    };
    const _hoisted_3$B = { style: {"font-size":"clamp(16px, 2vw, 18px)","margin-bottom":"2vh"} };
    const _hoisted_4$A = { style: {"font-size":"clamp(14px, 1.5vw, 16px)"} };

    const maxPieSectors$1 = 5;


    var script$U = {
      __name: 'pie-test',
      props: {
        i18n_title: String,
        max_width: Number,
        max_height: Number,
        pie_data: Object,  
        no_data_message: String,  
        filters: Object,

    },
      setup(__props, { expose: __expose }) {


    const d3 = d3v7;
    const _i18n = (t) => i18n(t);

    const chart_data_available = ref(true);
    const chartContainer = ref(null);
    const chartParent = ref(null);
    const url_list = ref(null);
    const chartData = ref(null);
    let resizeObserver = null;
    const props = __props;

    function drawChart(data) {
        const container = chartContainer.value;
        
        // Check that container exists before proceeding
        if (!container) {
            chart_data_available.value = false;
            return;
        }

        if (!data || !data.series || !data.labels || data.series.length === 0) {
            chart_data_available.value = false;
            return;
        }

        // Clear previous content
        container.innerHTML = '';

        // get container dimension
        const containerWidth = Math.max(container.offsetWidth || 300, 300);
        const containerHeight = Math.max(container.offsetHeight || 300, 300);

        // make space for title, chart and legend
        const titleHeight = container.querySelector('h3') ?
            container.querySelector('h3').offsetHeight : 0;

        // Legend space is 20% of height
        const legendHeight = containerHeight * 0.2;
        const chartHeight = containerHeight - titleHeight - legendHeight;

        // margins
        const margin = {
            top: 5,
            right: 5,
            bottom: 5,
            left: 5
        };

        // chart dimensions
        const width = containerWidth - margin.left - margin.right;
        const height = chartHeight - margin.top - margin.bottom;

        // chart radius
        const radius = Math.min(width, height) / 2 * 0.9;

        // Pie chart
        const svg = d3.select(container)
            .append("svg")
            .attr("width", "100%")
            .attr("height", `${chartHeight}px`)
            .attr("viewBox", `0 0 ${containerWidth} ${chartHeight}`)
            .append("g")
            .attr("class", "chart-group")
            .attr("transform", `translate(${width / 2 + margin.left}, ${height / 2 + margin.top})`);

        const sanitizedSeries = data.series.map(val => {
            const numVal = Number(val);
            return isNaN(numVal) ? 0 : numVal;
        });

        // Process data
        const chartPairs = data.labels.map((label, i) => ({
            label: label || "Unnamed",
            value: sanitizedSeries[i] || 0,
            color: (data.colors && data.colors[i]) || d3.schemeCategory10[i % 10],
            url: (url_list.value && url_list.value[i]) || ''
        }));

        // show first maxPieSectors sectors, group the remaining in others
        const topItems = [...chartPairs].slice(0, maxPieSectors$1);
        const otherItems = [...chartPairs].slice(maxPieSectors$1);
        
        const othersSum = otherItems.reduce((sum, item) => sum + item.value, 0);
        
        let finalChartItems = [...topItems];
        
        // add others to the chart if there is at least an element
        if (otherItems.length > 0 && othersSum > 0) {
            finalChartItems.push({
                label: `Others (${NtopUtils.bytesToVolume(othersSum)})`, // othersSum is the total of devices count
                value: othersSum,
                color: "#999999", // gray for others
                url: '' 
            });
        }

        if (finalChartItems.length === 0 || finalChartItems.every(item => item.value <= 0)) {
            chart_data_available.value = false;
            return;
        }

        // if label is empty add Unknown
        const chartLabels = finalChartItems.map((item) => {
            if (item.label.startsWith(" ")) {
                return "Unknown" + item.label;
            }
            return item.label;
        });

        const chartValues = finalChartItems.map(item => item.value);
        const chartColors = finalChartItems.map(item => item.color);
        const chartUrls = finalChartItems.map(item => item.url);

        
        let legendItems = [...finalChartItems];

        // Prepare legend labels adding unknown to not known manufacturers
        const legendLabels = legendItems.map((item) => {
            if (item.label.startsWith(" ")) {
                return "Unknown" + item.label;
            }
            return item.label;
        });
        const legendColors = legendItems.map(item => item.color);
        const legendUrls = legendItems.map(item => item.url);

        const pie = d3.pie()
            .value(d => d)
            .sort(null);

        const pieData = pie(chartValues);

        const arc = d3.arc()
            .innerRadius(radius * 0.5) // For donut chart
            .outerRadius(radius * 0.8);

        const outerArc = d3.arc()
            .innerRadius(radius * 0.5)
            .outerRadius(radius * 0.85);

        // Add tooltip
        const tooltip = d3.select("body")
            .append("div")
            .attr("class", "d3-tooltip")
            .style("opacity", 0)
            .style("position", "absolute")
            .style("background-color", "white")
            .style("border", "1px solid #ddd")
            .style("border-radius", "3px")
            .style("padding", "5px")
            .style("pointer-events", "none")
            .style("z-index", "100");

        // Generate pie slices
        const slices = svg.selectAll(".arc")
            .data(pieData)
            .enter()
            .append("g")
            .attr("class", "arc");

        // Draw paths
        slices.append("path")
            .attr("d", arc)
            .attr("fill", (d, i) => chartColors[i])
            .attr("stroke", "white")
            .style("stroke-width", "2px")
            .style("cursor", (d, i) => chartUrls[i] && !dataUtils.isEmptyString(chartUrls[i]) ? "pointer" : "default")
            .on("mouseover", function (event, d) {
                d3.select(this)
                    .transition()
                    .duration(100)
                    .attr("d", outerArc);

                tooltip.transition()
                    .duration(200)
                    .style("opacity", 0.9);

                tooltip.html(`${chartLabels[d.index]}`)
                    .style("left", (event.pageX) + "px")
                    .style("top", (event.pageY - 28) + "px");
            })
            .on("mouseout", function () {
                d3.select(this)
                    .transition()
                    .duration(100)
                    .attr("d", arc);

                tooltip.transition()
                    .duration(500)
                    .style("opacity", 0);
            })
            .on("click", function (event, d) {
                if (chartUrls[d.index] && !dataUtils.isEmptyString(chartUrls[d.index])) {
                    window.location.href = chartUrls[d.index];
                }
            });

        // Create the legend container
        const legendContainer = d3.select(container)
            .append("div")
            .attr("class", "d3-legend-container")
            .style("margin-top", "2px")
            .style("width", "100%")
            .style("max-height", `${legendHeight}px`)
            .style("overflow", "hidden")
            .style("display", "flex")
            .style("flex-wrap", "wrap")
            .style("justify-content", "center");

        // Add legend items
        legendLabels.forEach((label, i) => {
            const legendItem = legendContainer
                .append("div")
                .style("display", "inline-flex")
                .style("align-items", "center")
                .style("margin", "1px 4px")
                .style("max-width", `${containerWidth / 3 - 10}px`)
                .style("cursor", legendUrls[i] && !dataUtils.isEmptyString(legendUrls[i]) ? "pointer" : "default")
                .on("click", function () {
                    if (legendUrls[i] && !dataUtils.isEmptyString(legendUrls[i])) {
                        window.location.href = legendUrls[i];
                    }
                });

            legendItem.append("div")
                .style("width", "10px")
                .style("height", "10px")
                .style("background-color", legendColors[i])
                .style("margin-right", "3px")
                .style("flex-shrink", "0");

            legendItem.append("div")
                .attr("class", "legend-text")
                .text(label)
                .style("font-size", "12px")
                .style("white-space", "nowrap")
                .style("overflow", "hidden")
                .style("text-overflow", "ellipsis");
        });

        // Return tooltip cleanup function
        return () => {
            tooltip.remove();
        };
    }

    watch(() => [props.epoch_begin, props.epoch_end, props.filters], () => {
        refresh_chart();
    }, { flush: 'pre', deep: true });

    watch(() => props.pie_data, (cur_value, old_value) => {
        refresh_chart();
    });

    onMounted(() => {
        // set initial dimension
        if (chartContainer.value && chartParent.value) {

            chartContainer.value.style.width = '100%';
            chartContainer.value.style.height = '100%';
            chartContainer.value.style.minHeight = '300px';


            resizeObserver = new ResizeObserver(() => {
                if (chartData.value) {
                    // redraw pie chart if resize happens
                    nextTick(() => {
                        redraw_chart();
                    });
                }
            });
            resizeObserver.observe(chartParent.value);

            
        }
    });

    onBeforeUnmount(() => {
        // Clean up resize observer
        if (resizeObserver) {
            resizeObserver.disconnect();
            resizeObserver = null;
        }
    });

    async function refresh_chart() {
        try {
            // First ensure chartContainer exists
            if (!chartContainer.value) {
                return;
            }
            const data = props.pie_data;
            if (!data) {
                chart_data_available.value = false;
                return;
            }
            
            chartData.value = data;
            
            // Use await with nextTick to ensure DOM is updated
            await nextTick();
            
            // Only proceed if chartContainer still exists
            if (chartContainer.value) {
                chartContainer.value.innerHTML = '';
                drawChart(data);
            }
        } catch (error) {
            console.error("Error fetching chart data:", error);
            chart_data_available.value = false;
        }
    }

    // for external usage
    function redraw_chart() {
        if (chartData.value) {

            if (chartContainer.value) {
                chartContainer.value.innerHTML = '';
            }
            drawChart(chartData.value);
        }
    }

    const mountComponent = () => {
        // set initial dimension
        if (chartContainer.value && chartParent.value) {
            chartContainer.value.style.width = '100%';
            chartContainer.value.style.height = '100%';
            chartContainer.value.style.minHeight = '300px';

            resizeObserver = new ResizeObserver(() => {
                if (chartData.value) {
                    // Use nextTick with error handling
                    nextTick().then(() => {
                        if (chartContainer.value) {
                            redraw_chart();
                        }
                    }).catch(err => {
                        console.error("Error in resize redraw:", err);
                    });
                }
            });
            
            resizeObserver.observe(chartParent.value);

            // Use a slightly longer timeout to ensure DOM is fully rendered
            
        } else {
            console.warn("Chart container or parent not available on mount");
            // Try again in a moment
            setTimeout(mountComponent, 100);
        }
    };


    onMounted(() => {
        mountComponent();
    });


    // Expose methods for external use (Match the Sankey component's approach)
    __expose({
        update_chart: refresh_chart,
        redraw_chart: redraw_chart
    });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", {
        class: "d-flex flex-column flex-grow-1 position-relative",
        ref_key: "chartParent",
        ref: chartParent
      }, [
        (chart_data_available.value)
          ? (openBlock(), createElementBlock("div", {
              key: 0,
              class: "d3-chart-container",
              ref_key: "chartContainer",
              ref: chartContainer
            }, [
              (__props.i18n_title)
                ? (openBlock(), createElementBlock("h3", _hoisted_1$P, toDisplayString(_i18n(__props.i18n_title)), 1 /* TEXT */))
                : createCommentVNode("v-if", true)
            ], 512 /* NEED_PATCH */))
          : (openBlock(), createElementBlock("div", _hoisted_2$G, [
              createBaseVNode("div", _hoisted_3$B, [
                _cache[0] || (_cache[0] = createBaseVNode("i", { class: "fas fa-search" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString(_i18n("as_overview.no_asn_available")), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_4$A, toDisplayString(_i18n("as_overview.no_data")), 1 /* TEXT */)
            ]))
      ], 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$g = "\n/* Using scoped style like in the Sankey component */\n.d3-chart-container[data-v-607eb420] {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    min-height: 300px;\n    display: block;\n}\n\n/* Use a flex container approach like in Sankey */\n.d-flex[data-v-607eb420] {\n    display: flex;\n}\n.flex-column[data-v-607eb420] {\n    flex-direction: column;\n}\n.flex-grow-1[data-v-607eb420] {\n    flex-grow: 1;\n}\n.position-relative[data-v-607eb420] {\n    position: relative;\n}\n.d3-legend-container[data-v-607eb420] {\n    display: flex;\n    flex-wrap: wrap;\n    justify-content: center;\n    margin-top: 10px;\n}\n.legend-text[data-v-607eb420] {\n    font-size: 12px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    max-width: 150px;\n}\n.d3-tooltip[data-v-607eb420] {\n    position: absolute;\n    z-index: 1070;\n    font-size: 12px;\n    background-color: white;\n    border: 1px solid #ddd;\n    border-radius: 3px;\n    padding: 5px;\n    pointer-events: none;\n}\n";
    styleInject(css_248z$g);

    script$U.__scopeId = "data-v-607eb420";
    script$U.__file = "http_src/vue/pie-test.vue";

    function u(e){return -1!==[null,void 0,!1].indexOf(e)}function c(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function p(e){var t={exports:{}};return e(t,t.exports),t.exports}var d=p((function(e,t){e.exports=function(){var e=["decimals","thousand","mark","prefix","suffix","encoder","decoder","negativeBefore","negative","edit","undo"];function t(e){return e.split("").reverse().join("")}function r(e,t){return e.substring(0,t.length)===t}function i(e,t){return e.slice(-1*t.length)===t}function n(e,t,r){if((e[t]||e[r])&&e[t]===e[r])throw new Error(t)}function o(e){return "number"==typeof e&&isFinite(e)}function a(e,t){return e=e.toString().split("e"),(+((e=(e=Math.round(+(e[0]+"e"+(e[1]?+e[1]+t:t)))).toString().split("e"))[0]+"e"+(e[1]?+e[1]-t:-t))).toFixed(t)}function s(e,r,i,n,s,l,u,c,p,d,f,h){var m,v,g,b=h,y="",S="";return l&&(h=l(h)),!!o(h)&&(!1!==e&&0===parseFloat(h.toFixed(e))&&(h=0),h<0&&(m=!0,h=Math.abs(h)),!1!==e&&(h=a(h,e)),-1!==(h=h.toString()).indexOf(".")?(g=(v=h.split("."))[0],i&&(y=i+v[1])):g=h,r&&(g=t(g).match(/.{1,3}/g),g=t(g.join(t(r)))),m&&c&&(S+=c),n&&(S+=n),m&&p&&(S+=p),S+=g,S+=y,s&&(S+=s),d&&(S=d(S,b)),S)}function l(e,t,n,a,s,l,u,c,p,d,f,h){var m,v="";return f&&(h=f(h)),!(!h||"string"!=typeof h)&&(c&&r(h,c)&&(h=h.replace(c,""),m=!0),a&&r(h,a)&&(h=h.replace(a,"")),p&&r(h,p)&&(h=h.replace(p,""),m=!0),s&&i(h,s)&&(h=h.slice(0,-1*s.length)),t&&(h=h.split(t).join("")),n&&(h=h.replace(n,".")),m&&(v+="-"),""!==(v=(v+=h).replace(/[^0-9\.\-.]/g,""))&&(v=Number(v),u&&(v=u(v)),!!o(v)&&v))}function u(t){var r,i,o,a={};for(void 0===t.suffix&&(t.suffix=t.postfix),r=0;r<e.length;r+=1)if(void 0===(o=t[i=e[r]]))"negative"!==i||a.negativeBefore?"mark"===i&&"."!==a.thousand?a[i]=".":a[i]=!1:a[i]="-";else if("decimals"===i){if(!(o>=0&&o<8))throw new Error(i);a[i]=o;}else if("encoder"===i||"decoder"===i||"edit"===i||"undo"===i){if("function"!=typeof o)throw new Error(i);a[i]=o;}else {if("string"!=typeof o)throw new Error(i);a[i]=o;}return n(a,"mark","thousand"),n(a,"prefix","negative"),n(a,"prefix","negativeBefore"),a}function c(t,r,i){var n,o=[];for(n=0;n<e.length;n+=1)o.push(t[e[n]]);return o.push(i),r.apply("",o)}function p(e){if(!(this instanceof p))return new p(e);"object"==typeof e&&(e=u(e),this.to=function(t){return c(e,s,t)},this.from=function(t){return c(e,l,t)});}return p}();}));var f=c(p((function(e,t){!function(e){function t(e){return r(e)&&"function"==typeof e.from}function r(e){return "object"==typeof e&&"function"==typeof e.to}function i(e){e.parentElement.removeChild(e);}function n(e){return null!=e}function o(e){e.preventDefault();}function a(e){return e.filter((function(e){return !this[e]&&(this[e]=!0)}),{})}function s(e,t){return Math.round(e/t)*t}function l(e,t){var r=e.getBoundingClientRect(),i=e.ownerDocument,n=i.documentElement,o=g(i);return /webkit.*Chrome.*Mobile/i.test(navigator.userAgent)&&(o.x=0),t?r.top+o.y-n.clientTop:r.left+o.x-n.clientLeft}function u(e){return "number"==typeof e&&!isNaN(e)&&isFinite(e)}function c(e,t,r){r>0&&(h(e,t),setTimeout((function(){m(e,t);}),r));}function p(e){return Math.max(Math.min(e,100),0)}function d(e){return Array.isArray(e)?e:[e]}function f(e){var t=(e=String(e)).split(".");return t.length>1?t[1].length:0}function h(e,t){e.classList&&!/\s/.test(t)?e.classList.add(t):e.className+=" "+t;}function m(e,t){e.classList&&!/\s/.test(t)?e.classList.remove(t):e.className=e.className.replace(new RegExp("(^|\\b)"+t.split(" ").join("|")+"(\\b|$)","gi")," ");}function v(e,t){return e.classList?e.classList.contains(t):new RegExp("\\b"+t+"\\b").test(e.className)}function g(e){var t=void 0!==window.pageXOffset,r="CSS1Compat"===(e.compatMode||"");return {x:t?window.pageXOffset:r?e.documentElement.scrollLeft:e.body.scrollLeft,y:t?window.pageYOffset:r?e.documentElement.scrollTop:e.body.scrollTop}}function b(){return window.navigator.pointerEnabled?{start:"pointerdown",move:"pointermove",end:"pointerup"}:window.navigator.msPointerEnabled?{start:"MSPointerDown",move:"MSPointerMove",end:"MSPointerUp"}:{start:"mousedown touchstart",move:"mousemove touchmove",end:"mouseup touchend"}}function y(){var e=!1;try{var t=Object.defineProperty({},"passive",{get:function(){e=!0;}});window.addEventListener("test",null,t);}catch(e){}return e}function S(){return window.CSS&&CSS.supports&&CSS.supports("touch-action","none")}function x(e,t){return 100/(t-e)}function w(e,t,r){return 100*t/(e[r+1]-e[r])}function E(e,t){return w(e,e[0]<0?t+Math.abs(e[0]):t-e[0],0)}function P(e,t){return t*(e[1]-e[0])/100+e[0]}function N(e,t){for(var r=1;e>=t[r];)r+=1;return r}function C(e,t,r){if(r>=e.slice(-1)[0])return 100;var i=N(r,e),n=e[i-1],o=e[i],a=t[i-1],s=t[i];return a+E([n,o],r)/x(a,s)}function k(e,t,r){if(r>=100)return e.slice(-1)[0];var i=N(r,t),n=e[i-1],o=e[i],a=t[i-1];return P([n,o],(r-a)*x(a,t[i]))}function V(e,t,r,i){if(100===i)return i;var n=N(i,e),o=e[n-1],a=e[n];return r?i-o>(a-o)/2?a:o:t[n-1]?e[n-1]+s(i-e[n-1],t[n-1]):i}var A,M;e.PipsMode=void 0,(M=e.PipsMode||(e.PipsMode={})).Range="range",M.Steps="steps",M.Positions="positions",M.Count="count",M.Values="values",e.PipsType=void 0,(A=e.PipsType||(e.PipsType={}))[A.None=-1]="None",A[A.NoValue=0]="NoValue",A[A.LargeValue=1]="LargeValue",A[A.SmallValue=2]="SmallValue";var L=function(){function e(e,t,r){var i;this.xPct=[],this.xVal=[],this.xSteps=[],this.xNumSteps=[],this.xHighestCompleteStep=[],this.xSteps=[r||!1],this.xNumSteps=[!1],this.snap=t;var n=[];for(Object.keys(e).forEach((function(t){n.push([d(e[t]),t]);})),n.sort((function(e,t){return e[0][0]-t[0][0]})),i=0;i<n.length;i++)this.handleEntryPoint(n[i][1],n[i][0]);for(this.xNumSteps=this.xSteps.slice(0),i=0;i<this.xNumSteps.length;i++)this.handleStepPoint(i,this.xNumSteps[i]);}return e.prototype.getDistance=function(e){for(var t=[],r=0;r<this.xNumSteps.length-1;r++)t[r]=w(this.xVal,e,r);return t},e.prototype.getAbsoluteDistance=function(e,t,r){var i,n=0;if(e<this.xPct[this.xPct.length-1])for(;e>this.xPct[n+1];)n++;else e===this.xPct[this.xPct.length-1]&&(n=this.xPct.length-2);r||e!==this.xPct[n+1]||n++,null===t&&(t=[]);var o=1,a=t[n],s=0,l=0,u=0,c=0;for(i=r?(e-this.xPct[n])/(this.xPct[n+1]-this.xPct[n]):(this.xPct[n+1]-e)/(this.xPct[n+1]-this.xPct[n]);a>0;)s=this.xPct[n+1+c]-this.xPct[n+c],t[n+c]*o+100-100*i>100?(l=s*i,o=(a-100*i)/t[n+c],i=1):(l=t[n+c]*s/100*o,o=0),r?(u-=l,this.xPct.length+c>=1&&c--):(u+=l,this.xPct.length-c>=1&&c++),a=t[n+c]*o;return e+u},e.prototype.toStepping=function(e){return e=C(this.xVal,this.xPct,e)},e.prototype.fromStepping=function(e){return k(this.xVal,this.xPct,e)},e.prototype.getStep=function(e){return e=V(this.xPct,this.xSteps,this.snap,e)},e.prototype.getDefaultStep=function(e,t,r){var i=N(e,this.xPct);return (100===e||t&&e===this.xPct[i-1])&&(i=Math.max(i-1,1)),(this.xVal[i]-this.xVal[i-1])/r},e.prototype.getNearbySteps=function(e){var t=N(e,this.xPct);return {stepBefore:{startValue:this.xVal[t-2],step:this.xNumSteps[t-2],highestStep:this.xHighestCompleteStep[t-2]},thisStep:{startValue:this.xVal[t-1],step:this.xNumSteps[t-1],highestStep:this.xHighestCompleteStep[t-1]},stepAfter:{startValue:this.xVal[t],step:this.xNumSteps[t],highestStep:this.xHighestCompleteStep[t]}}},e.prototype.countStepDecimals=function(){var e=this.xNumSteps.map(f);return Math.max.apply(null,e)},e.prototype.hasNoSize=function(){return this.xVal[0]===this.xVal[this.xVal.length-1]},e.prototype.convert=function(e){return this.getStep(this.toStepping(e))},e.prototype.handleEntryPoint=function(e,t){var r;if(!u(r="min"===e?0:"max"===e?100:parseFloat(e))||!u(t[0]))throw new Error("noUiSlider: 'range' value isn't numeric.");this.xPct.push(r),this.xVal.push(t[0]);var i=Number(t[1]);r?this.xSteps.push(!isNaN(i)&&i):isNaN(i)||(this.xSteps[0]=i),this.xHighestCompleteStep.push(0);},e.prototype.handleStepPoint=function(e,t){if(t)if(this.xVal[e]!==this.xVal[e+1]){this.xSteps[e]=w([this.xVal[e],this.xVal[e+1]],t,0)/x(this.xPct[e],this.xPct[e+1]);var r=(this.xVal[e+1]-this.xVal[e])/this.xNumSteps[e],i=Math.ceil(Number(r.toFixed(3))-1),n=this.xVal[e]+this.xNumSteps[e]*i;this.xHighestCompleteStep[e]=n;}else this.xSteps[e]=this.xHighestCompleteStep[e]=this.xVal[e];},e}(),U={to:function(e){return void 0===e?"":e.toFixed(2)},from:Number},O={target:"target",base:"base",origin:"origin",handle:"handle",handleLower:"handle-lower",handleUpper:"handle-upper",touchArea:"touch-area",horizontal:"horizontal",vertical:"vertical",background:"background",connect:"connect",connects:"connects",ltr:"ltr",rtl:"rtl",textDirectionLtr:"txt-dir-ltr",textDirectionRtl:"txt-dir-rtl",draggable:"draggable",drag:"state-drag",tap:"state-tap",active:"active",tooltip:"tooltip",pips:"pips",pipsHorizontal:"pips-horizontal",pipsVertical:"pips-vertical",marker:"marker",markerHorizontal:"marker-horizontal",markerVertical:"marker-vertical",markerNormal:"marker-normal",markerLarge:"marker-large",markerSub:"marker-sub",value:"value",valueHorizontal:"value-horizontal",valueVertical:"value-vertical",valueNormal:"value-normal",valueLarge:"value-large",valueSub:"value-sub"},D={tooltips:".__tooltips",aria:".__aria"};function j(e,t){if(!u(t))throw new Error("noUiSlider: 'step' is not numeric.");e.singleStep=t;}function F(e,t){if(!u(t))throw new Error("noUiSlider: 'keyboardPageMultiplier' is not numeric.");e.keyboardPageMultiplier=t;}function T(e,t){if(!u(t))throw new Error("noUiSlider: 'keyboardMultiplier' is not numeric.");e.keyboardMultiplier=t;}function z(e,t){if(!u(t))throw new Error("noUiSlider: 'keyboardDefaultStep' is not numeric.");e.keyboardDefaultStep=t;}function H(e,t){if("object"!=typeof t||Array.isArray(t))throw new Error("noUiSlider: 'range' is not an object.");if(void 0===t.min||void 0===t.max)throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'.");e.spectrum=new L(t,e.snap||!1,e.singleStep);}function q(e,t){if(t=d(t),!Array.isArray(t)||!t.length)throw new Error("noUiSlider: 'start' option is incorrect.");e.handles=t.length,e.start=t;}function R(e,t){if("boolean"!=typeof t)throw new Error("noUiSlider: 'snap' option must be a boolean.");e.snap=t;}function B(e,t){if("boolean"!=typeof t)throw new Error("noUiSlider: 'animate' option must be a boolean.");e.animate=t;}function _(e,t){if("number"!=typeof t)throw new Error("noUiSlider: 'animationDuration' option must be a number.");e.animationDuration=t;}function $(e,t){var r,i=[!1];if("lower"===t?t=[!0,!1]:"upper"===t&&(t=[!1,!0]),!0===t||!1===t){for(r=1;r<e.handles;r++)i.push(t);i.push(!1);}else {if(!Array.isArray(t)||!t.length||t.length!==e.handles+1)throw new Error("noUiSlider: 'connect' option doesn't match handle count.");i=t;}e.connect=i;}function X(e,t){switch(t){case"horizontal":e.ort=0;break;case"vertical":e.ort=1;break;default:throw new Error("noUiSlider: 'orientation' option is invalid.")}}function Y(e,t){if(!u(t))throw new Error("noUiSlider: 'margin' option must be numeric.");0!==t&&(e.margin=e.spectrum.getDistance(t));}function I(e,t){if(!u(t))throw new Error("noUiSlider: 'limit' option must be numeric.");if(e.limit=e.spectrum.getDistance(t),!e.limit||e.handles<2)throw new Error("noUiSlider: 'limit' option is only supported on linear sliders with 2 or more handles.")}function W(e,t){var r;if(!u(t)&&!Array.isArray(t))throw new Error("noUiSlider: 'padding' option must be numeric or array of exactly 2 numbers.");if(Array.isArray(t)&&2!==t.length&&!u(t[0])&&!u(t[1]))throw new Error("noUiSlider: 'padding' option must be numeric or array of exactly 2 numbers.");if(0!==t){for(Array.isArray(t)||(t=[t,t]),e.padding=[e.spectrum.getDistance(t[0]),e.spectrum.getDistance(t[1])],r=0;r<e.spectrum.xNumSteps.length-1;r++)if(e.padding[0][r]<0||e.padding[1][r]<0)throw new Error("noUiSlider: 'padding' option must be a positive number(s).");var i=t[0]+t[1],n=e.spectrum.xVal[0];if(i/(e.spectrum.xVal[e.spectrum.xVal.length-1]-n)>1)throw new Error("noUiSlider: 'padding' option must not exceed 100% of the range.")}}function G(e,t){switch(t){case"ltr":e.dir=0;break;case"rtl":e.dir=1;break;default:throw new Error("noUiSlider: 'direction' option was not recognized.")}}function J(e,t){if("string"!=typeof t)throw new Error("noUiSlider: 'behaviour' must be a string containing options.");var r=t.indexOf("tap")>=0,i=t.indexOf("drag")>=0,n=t.indexOf("fixed")>=0,o=t.indexOf("snap")>=0,a=t.indexOf("hover")>=0,s=t.indexOf("unconstrained")>=0,l=t.indexOf("drag-all")>=0,u=t.indexOf("smooth-steps")>=0;if(n){if(2!==e.handles)throw new Error("noUiSlider: 'fixed' behaviour must be used with 2 handles");Y(e,e.start[1]-e.start[0]);}if(s&&(e.margin||e.limit))throw new Error("noUiSlider: 'unconstrained' behaviour cannot be used with margin or limit");e.events={tap:r||o,drag:i,dragAll:l,smoothSteps:u,fixed:n,snap:o,hover:a,unconstrained:s};}function K(e,t){if(!1!==t)if(!0===t||r(t)){e.tooltips=[];for(var i=0;i<e.handles;i++)e.tooltips.push(t);}else {if((t=d(t)).length!==e.handles)throw new Error("noUiSlider: must pass a formatter for all handles.");t.forEach((function(e){if("boolean"!=typeof e&&!r(e))throw new Error("noUiSlider: 'tooltips' must be passed a formatter or 'false'.")})),e.tooltips=t;}}function Q(e,t){if(t.length!==e.handles)throw new Error("noUiSlider: must pass a attributes for all handles.");e.handleAttributes=t;}function Z(e,t){if(!r(t))throw new Error("noUiSlider: 'ariaFormat' requires 'to' method.");e.ariaFormat=t;}function ee(e,r){if(!t(r))throw new Error("noUiSlider: 'format' requires 'to' and 'from' methods.");e.format=r;}function te(e,t){if("boolean"!=typeof t)throw new Error("noUiSlider: 'keyboardSupport' option must be a boolean.");e.keyboardSupport=t;}function re(e,t){e.documentElement=t;}function ie(e,t){if("string"!=typeof t&&!1!==t)throw new Error("noUiSlider: 'cssPrefix' must be a string or `false`.");e.cssPrefix=t;}function ne(e,t){if("object"!=typeof t)throw new Error("noUiSlider: 'cssClasses' must be an object.");"string"==typeof e.cssPrefix?(e.cssClasses={},Object.keys(t).forEach((function(r){e.cssClasses[r]=e.cssPrefix+t[r];}))):e.cssClasses=t;}function oe(e){var t={margin:null,limit:null,padding:null,animate:!0,animationDuration:300,ariaFormat:U,format:U},r={step:{r:!1,t:j},keyboardPageMultiplier:{r:!1,t:F},keyboardMultiplier:{r:!1,t:T},keyboardDefaultStep:{r:!1,t:z},start:{r:!0,t:q},connect:{r:!0,t:$},direction:{r:!0,t:G},snap:{r:!1,t:R},animate:{r:!1,t:B},animationDuration:{r:!1,t:_},range:{r:!0,t:H},orientation:{r:!1,t:X},margin:{r:!1,t:Y},limit:{r:!1,t:I},padding:{r:!1,t:W},behaviour:{r:!0,t:J},ariaFormat:{r:!1,t:Z},format:{r:!1,t:ee},tooltips:{r:!1,t:K},keyboardSupport:{r:!0,t:te},documentElement:{r:!1,t:re},cssPrefix:{r:!0,t:ie},cssClasses:{r:!0,t:ne},handleAttributes:{r:!1,t:Q}},i={connect:!1,direction:"ltr",behaviour:"tap",orientation:"horizontal",keyboardSupport:!0,cssPrefix:"noUi-",cssClasses:O,keyboardPageMultiplier:5,keyboardMultiplier:1,keyboardDefaultStep:10};e.format&&!e.ariaFormat&&(e.ariaFormat=e.format),Object.keys(r).forEach((function(o){if(n(e[o])||void 0!==i[o])r[o].t(t,n(e[o])?e[o]:i[o]);else if(r[o].r)throw new Error("noUiSlider: '"+o+"' is required.")})),t.pips=e.pips;var o=document.createElement("div"),a=void 0!==o.style.msTransform,s=void 0!==o.style.transform;t.transformRule=s?"transform":a?"msTransform":"webkitTransform";var l=[["left","top"],["right","bottom"]];return t.style=l[t.dir][t.ort],t}function ae(t,r,s){var u,f,x,w,E,P=b(),N=S()&&y(),C=t,k=r.spectrum,V=[],A=[],M=[],L=0,U={},O=t.ownerDocument,j=r.documentElement||O.documentElement,F=O.body,T="rtl"===O.dir||1===r.ort?0:100;function z(e,t){var r=O.createElement("div");return t&&h(r,t),e.appendChild(r),r}function H(e,t){var i=z(e,r.cssClasses.origin),n=z(i,r.cssClasses.handle);if(z(n,r.cssClasses.touchArea),n.setAttribute("data-handle",String(t)),r.keyboardSupport&&(n.setAttribute("tabindex","0"),n.addEventListener("keydown",(function(e){return fe(e,t)}))),void 0!==r.handleAttributes){var o=r.handleAttributes[t];Object.keys(o).forEach((function(e){n.setAttribute(e,o[e]);}));}return n.setAttribute("role","slider"),n.setAttribute("aria-orientation",r.ort?"vertical":"horizontal"),0===t?h(n,r.cssClasses.handleLower):t===r.handles-1&&h(n,r.cssClasses.handleUpper),i}function q(e,t){return !!t&&z(e,r.cssClasses.connect)}function R(e,t){var i=z(t,r.cssClasses.connects);f=[],(x=[]).push(q(i,e[0]));for(var n=0;n<r.handles;n++)f.push(H(t,n)),M[n]=n,x.push(q(i,e[n+1]));}function B(e){return h(e,r.cssClasses.target),0===r.dir?h(e,r.cssClasses.ltr):h(e,r.cssClasses.rtl),0===r.ort?h(e,r.cssClasses.horizontal):h(e,r.cssClasses.vertical),h(e,"rtl"===getComputedStyle(e).direction?r.cssClasses.textDirectionRtl:r.cssClasses.textDirectionLtr),z(e,r.cssClasses.base)}function _(e,t){return !(!r.tooltips||!r.tooltips[t])&&z(e.firstChild,r.cssClasses.tooltip)}function $(){return C.hasAttribute("disabled")}function X(e){return f[e].hasAttribute("disabled")}function Y(){E&&(ge("update"+D.tooltips),E.forEach((function(e){e&&i(e);})),E=null);}function I(){Y(),E=f.map(_),me("update"+D.tooltips,(function(e,t,i){if(E&&r.tooltips&&!1!==E[t]){var n=e[t];!0!==r.tooltips[t]&&(n=r.tooltips[t].to(i[t])),E[t].innerHTML=n;}}));}function W(){ge("update"+D.aria),me("update"+D.aria,(function(e,t,i,n,o){M.forEach((function(e){var t=f[e],n=ye(A,e,0,!0,!0,!0),a=ye(A,e,100,!0,!0,!0),s=o[e],l=String(r.ariaFormat.to(i[e]));n=k.fromStepping(n).toFixed(1),a=k.fromStepping(a).toFixed(1),s=k.fromStepping(s).toFixed(1),t.children[0].setAttribute("aria-valuemin",n),t.children[0].setAttribute("aria-valuemax",a),t.children[0].setAttribute("aria-valuenow",s),t.children[0].setAttribute("aria-valuetext",l);}));}));}function G(t){if(t.mode===e.PipsMode.Range||t.mode===e.PipsMode.Steps)return k.xVal;if(t.mode===e.PipsMode.Count){if(t.values<2)throw new Error("noUiSlider: 'values' (>= 2) required for mode 'count'.");for(var r=t.values-1,i=100/r,n=[];r--;)n[r]=r*i;return n.push(100),J(n,t.stepped)}return t.mode===e.PipsMode.Positions?J(t.values,t.stepped):t.mode===e.PipsMode.Values?t.stepped?t.values.map((function(e){return k.fromStepping(k.getStep(k.toStepping(e)))})):t.values:[]}function J(e,t){return e.map((function(e){return k.fromStepping(t?k.getStep(e):e)}))}function K(t){function r(e,t){return Number((e+t).toFixed(7))}var i=G(t),n={},o=k.xVal[0],s=k.xVal[k.xVal.length-1],l=!1,u=!1,c=0;return (i=a(i.slice().sort((function(e,t){return e-t}))))[0]!==o&&(i.unshift(o),l=!0),i[i.length-1]!==s&&(i.push(s),u=!0),i.forEach((function(o,a){var s,p,d,f,h,m,v,g,b,y,S=o,x=i[a+1],w=t.mode===e.PipsMode.Steps;for(w&&(s=k.xNumSteps[a]),s||(s=x-S),void 0===x&&(x=S),s=Math.max(s,1e-7),p=S;p<=x;p=r(p,s)){for(g=(h=(f=k.toStepping(p))-c)/(t.density||1),y=h/(b=Math.round(g)),d=1;d<=b;d+=1)n[(m=c+d*y).toFixed(5)]=[k.fromStepping(m),0];v=i.indexOf(p)>-1?e.PipsType.LargeValue:w?e.PipsType.SmallValue:e.PipsType.NoValue,!a&&l&&p!==x&&(v=0),p===x&&u||(n[f.toFixed(5)]=[p,v]),c=f;}})),n}function Q(t,i,n){var o,a,s=O.createElement("div"),l=((o={})[e.PipsType.None]="",o[e.PipsType.NoValue]=r.cssClasses.valueNormal,o[e.PipsType.LargeValue]=r.cssClasses.valueLarge,o[e.PipsType.SmallValue]=r.cssClasses.valueSub,o),u=((a={})[e.PipsType.None]="",a[e.PipsType.NoValue]=r.cssClasses.markerNormal,a[e.PipsType.LargeValue]=r.cssClasses.markerLarge,a[e.PipsType.SmallValue]=r.cssClasses.markerSub,a),c=[r.cssClasses.valueHorizontal,r.cssClasses.valueVertical],p=[r.cssClasses.markerHorizontal,r.cssClasses.markerVertical];function d(e,t){var i=t===r.cssClasses.value,n=i?l:u;return t+" "+(i?c:p)[r.ort]+" "+n[e]}function f(t,o,a){if((a=i?i(o,a):a)!==e.PipsType.None){var l=z(s,!1);l.className=d(a,r.cssClasses.marker),l.style[r.style]=t+"%",a>e.PipsType.NoValue&&((l=z(s,!1)).className=d(a,r.cssClasses.value),l.setAttribute("data-value",String(o)),l.style[r.style]=t+"%",l.innerHTML=String(n.to(o)));}}return h(s,r.cssClasses.pips),h(s,0===r.ort?r.cssClasses.pipsHorizontal:r.cssClasses.pipsVertical),Object.keys(t).forEach((function(e){f(e,t[e][0],t[e][1]);})),s}function Z(){w&&(i(w),w=null);}function ee(e){Z();var t=K(e),r=e.filter,i=e.format||{to:function(e){return String(Math.round(e))}};return w=C.appendChild(Q(t,r,i))}function te(){var e=u.getBoundingClientRect(),t="offset"+["Width","Height"][r.ort];return 0===r.ort?e.width||u[t]:e.height||u[t]}function re(e,t,i,n){var o=function(o){var a=ie(o,n.pageOffset,n.target||t);return !!a&&!($()&&!n.doNotReject)&&!(v(C,r.cssClasses.tap)&&!n.doNotReject)&&!(e===P.start&&void 0!==a.buttons&&a.buttons>1)&&(!n.hover||!a.buttons)&&(N||a.preventDefault(),a.calcPoint=a.points[r.ort],void i(a,n))},a=[];return e.split(" ").forEach((function(e){t.addEventListener(e,o,!!N&&{passive:!0}),a.push([e,o]);})),a}function ie(e,t,r){var i=0===e.type.indexOf("touch"),n=0===e.type.indexOf("mouse"),o=0===e.type.indexOf("pointer"),a=0,s=0;if(0===e.type.indexOf("MSPointer")&&(o=!0),"mousedown"===e.type&&!e.buttons&&!e.touches)return !1;if(i){var l=function(t){var i=t.target;return i===r||r.contains(i)||e.composed&&e.composedPath().shift()===r};if("touchstart"===e.type){var u=Array.prototype.filter.call(e.touches,l);if(u.length>1)return !1;a=u[0].pageX,s=u[0].pageY;}else {var c=Array.prototype.find.call(e.changedTouches,l);if(!c)return !1;a=c.pageX,s=c.pageY;}}return t=t||g(O),(n||o)&&(a=e.clientX+t.x,s=e.clientY+t.y),e.pageOffset=t,e.points=[a,s],e.cursor=n||o,e}function ne(e){var t=100*(e-l(u,r.ort))/te();return t=p(t),r.dir?100-t:t}function ae(e){var t=100,r=!1;return f.forEach((function(i,n){if(!X(n)){var o=A[n],a=Math.abs(o-e);(a<t||a<=t&&e>o||100===a&&100===t)&&(r=n,t=a);}})),r}function se(e,t){"mouseout"===e.type&&"HTML"===e.target.nodeName&&null===e.relatedTarget&&ue(e,t);}function le(e,t){if(-1===navigator.appVersion.indexOf("MSIE 9")&&0===e.buttons&&0!==t.buttonsProperty)return ue(e,t);var i=(r.dir?-1:1)*(e.calcPoint-t.startCalcPoint);xe(i>0,100*i/t.baseSize,t.locations,t.handleNumbers,t.connect);}function ue(e,t){t.handle&&(m(t.handle,r.cssClasses.active),L-=1),t.listeners.forEach((function(e){j.removeEventListener(e[0],e[1]);})),0===L&&(m(C,r.cssClasses.drag),Pe(),e.cursor&&(F.style.cursor="",F.removeEventListener("selectstart",o))),r.events.smoothSteps&&(t.handleNumbers.forEach((function(e){Ne(e,A[e],!0,!0,!1,!1);})),t.handleNumbers.forEach((function(e){be("update",e);}))),t.handleNumbers.forEach((function(e){be("change",e),be("set",e),be("end",e);}));}function ce(e,t){if(!t.handleNumbers.some(X)){var i;1===t.handleNumbers.length&&(i=f[t.handleNumbers[0]].children[0],L+=1,h(i,r.cssClasses.active)),e.stopPropagation();var n=[],a=re(P.move,j,le,{target:e.target,handle:i,connect:t.connect,listeners:n,startCalcPoint:e.calcPoint,baseSize:te(),pageOffset:e.pageOffset,handleNumbers:t.handleNumbers,buttonsProperty:e.buttons,locations:A.slice()}),s=re(P.end,j,ue,{target:e.target,handle:i,listeners:n,doNotReject:!0,handleNumbers:t.handleNumbers}),l=re("mouseout",j,se,{target:e.target,handle:i,listeners:n,doNotReject:!0,handleNumbers:t.handleNumbers});n.push.apply(n,a.concat(s,l)),e.cursor&&(F.style.cursor=getComputedStyle(e.target).cursor,f.length>1&&h(C,r.cssClasses.drag),F.addEventListener("selectstart",o,!1)),t.handleNumbers.forEach((function(e){be("start",e);}));}}function pe(e){e.stopPropagation();var t=ne(e.calcPoint),i=ae(t);!1!==i&&(r.events.snap||c(C,r.cssClasses.tap,r.animationDuration),Ne(i,t,!0,!0),Pe(),be("slide",i,!0),be("update",i,!0),r.events.snap?ce(e,{handleNumbers:[i]}):(be("change",i,!0),be("set",i,!0)));}function de(e){var t=ne(e.calcPoint),r=k.getStep(t),i=k.fromStepping(r);Object.keys(U).forEach((function(e){"hover"===e.split(".")[0]&&U[e].forEach((function(e){e.call(Te,i);}));}));}function fe(e,t){if($()||X(t))return !1;var i=["Left","Right"],n=["Down","Up"],o=["PageDown","PageUp"],a=["Home","End"];r.dir&&!r.ort?i.reverse():r.ort&&!r.dir&&(n.reverse(),o.reverse());var s,l=e.key.replace("Arrow",""),u=l===o[0],c=l===o[1],p=l===n[0]||l===i[0]||u,d=l===n[1]||l===i[1]||c,f=l===a[0],h=l===a[1];if(!(p||d||f||h))return !0;if(e.preventDefault(),d||p){var m=p?0:1,v=Oe(t)[m];if(null===v)return !1;!1===v&&(v=k.getDefaultStep(A[t],p,r.keyboardDefaultStep)),v*=c||u?r.keyboardPageMultiplier:r.keyboardMultiplier,v=Math.max(v,1e-7),v*=p?-1:1,s=V[t]+v;}else s=h?r.spectrum.xVal[r.spectrum.xVal.length-1]:r.spectrum.xVal[0];return Ne(t,k.toStepping(s),!0,!0),be("slide",t),be("update",t),be("change",t),be("set",t),!1}function he(e){e.fixed||f.forEach((function(e,t){re(P.start,e.children[0],ce,{handleNumbers:[t]});})),e.tap&&re(P.start,u,pe,{}),e.hover&&re(P.move,u,de,{hover:!0}),e.drag&&x.forEach((function(t,i){if(!1!==t&&0!==i&&i!==x.length-1){var n=f[i-1],o=f[i],a=[t],s=[n,o],l=[i-1,i];h(t,r.cssClasses.draggable),e.fixed&&(a.push(n.children[0]),a.push(o.children[0])),e.dragAll&&(s=f,l=M),a.forEach((function(e){re(P.start,e,ce,{handles:s,handleNumbers:l,connect:t});}));}}));}function me(e,t){U[e]=U[e]||[],U[e].push(t),"update"===e.split(".")[0]&&f.forEach((function(e,t){be("update",t);}));}function ve(e){return e===D.aria||e===D.tooltips}function ge(e){var t=e&&e.split(".")[0],r=t?e.substring(t.length):e;Object.keys(U).forEach((function(e){var i=e.split(".")[0],n=e.substring(i.length);t&&t!==i||r&&r!==n||ve(n)&&r!==n||delete U[e];}));}function be(e,t,i){Object.keys(U).forEach((function(n){var o=n.split(".")[0];e===o&&U[n].forEach((function(e){e.call(Te,V.map(r.format.to),t,V.slice(),i||!1,A.slice(),Te);}));}));}function ye(e,t,i,n,o,a,s){var l;return f.length>1&&!r.events.unconstrained&&(n&&t>0&&(l=k.getAbsoluteDistance(e[t-1],r.margin,!1),i=Math.max(i,l)),o&&t<f.length-1&&(l=k.getAbsoluteDistance(e[t+1],r.margin,!0),i=Math.min(i,l))),f.length>1&&r.limit&&(n&&t>0&&(l=k.getAbsoluteDistance(e[t-1],r.limit,!1),i=Math.min(i,l)),o&&t<f.length-1&&(l=k.getAbsoluteDistance(e[t+1],r.limit,!0),i=Math.max(i,l))),r.padding&&(0===t&&(l=k.getAbsoluteDistance(0,r.padding[0],!1),i=Math.max(i,l)),t===f.length-1&&(l=k.getAbsoluteDistance(100,r.padding[1],!0),i=Math.min(i,l))),s||(i=k.getStep(i)),!((i=p(i))===e[t]&&!a)&&i}function Se(e,t){var i=r.ort;return (i?t:e)+", "+(i?e:t)}function xe(e,t,i,n,o){var a=i.slice(),s=n[0],l=r.events.smoothSteps,u=[!e,e],c=[e,!e];n=n.slice(),e&&n.reverse(),n.length>1?n.forEach((function(e,r){var i=ye(a,e,a[e]+t,u[r],c[r],!1,l);!1===i?t=0:(t=i-a[e],a[e]=i);})):u=c=[!0];var p=!1;n.forEach((function(e,r){p=Ne(e,i[e]+t,u[r],c[r],!1,l)||p;})),p&&(n.forEach((function(e){be("update",e),be("slide",e);})),null!=o&&be("drag",s));}function we(e,t){return r.dir?100-e-t:e}function Ee(e,t){A[e]=t,V[e]=k.fromStepping(t);var i="translate("+Se(we(t,0)-T+"%","0")+")";f[e].style[r.transformRule]=i,Ce(e),Ce(e+1);}function Pe(){M.forEach((function(e){var t=A[e]>50?-1:1,r=3+(f.length+t*e);f[e].style.zIndex=String(r);}));}function Ne(e,t,r,i,n,o){return n||(t=ye(A,e,t,r,i,!1,o)),!1!==t&&(Ee(e,t),!0)}function Ce(e){if(x[e]){var t=0,i=100;0!==e&&(t=A[e-1]),e!==x.length-1&&(i=A[e]);var n=i-t,o="translate("+Se(we(t,n)+"%","0")+")",a="scale("+Se(n/100,"1")+")";x[e].style[r.transformRule]=o+" "+a;}}function ke(e,t){return null===e||!1===e||void 0===e?A[t]:("number"==typeof e&&(e=String(e)),!1!==(e=r.format.from(e))&&(e=k.toStepping(e)),!1===e||isNaN(e)?A[t]:e)}function Ve(e,t,i){var n=d(e),o=void 0===A[0];t=void 0===t||t,r.animate&&!o&&c(C,r.cssClasses.tap,r.animationDuration),M.forEach((function(e){Ne(e,ke(n[e],e),!0,!1,i);}));var a=1===M.length?0:1;if(o&&k.hasNoSize()&&(i=!0,A[0]=0,M.length>1)){var s=100/(M.length-1);M.forEach((function(e){A[e]=e*s;}));}for(;a<M.length;++a)M.forEach((function(e){Ne(e,A[e],!0,!0,i);}));Pe(),M.forEach((function(e){be("update",e),null!==n[e]&&t&&be("set",e);}));}function Ae(e){Ve(r.start,e);}function Me(e,t,r,i){if(!((e=Number(e))>=0&&e<M.length))throw new Error("noUiSlider: invalid handle number, got: "+e);Ne(e,ke(t,e),!0,!0,i),be("update",e),r&&be("set",e);}function Le(e){if(void 0===e&&(e=!1),e)return 1===V.length?V[0]:V.slice(0);var t=V.map(r.format.to);return 1===t.length?t[0]:t}function Ue(){for(ge(D.aria),ge(D.tooltips),Object.keys(r.cssClasses).forEach((function(e){m(C,r.cssClasses[e]);}));C.firstChild;)C.removeChild(C.firstChild);delete C.noUiSlider;}function Oe(e){var t=A[e],i=k.getNearbySteps(t),n=V[e],o=i.thisStep.step,a=null;if(r.snap)return [n-i.stepBefore.startValue||null,i.stepAfter.startValue-n||null];!1!==o&&n+o>i.stepAfter.startValue&&(o=i.stepAfter.startValue-n),a=n>i.thisStep.startValue?i.thisStep.step:!1!==i.stepBefore.step&&n-i.stepBefore.highestStep,100===t?o=null:0===t&&(a=null);var s=k.countStepDecimals();return null!==o&&!1!==o&&(o=Number(o.toFixed(s))),null!==a&&!1!==a&&(a=Number(a.toFixed(s))),[a,o]}function De(){return M.map(Oe)}function je(e,t){var i=Le(),o=["margin","limit","padding","range","animate","snap","step","format","pips","tooltips"];o.forEach((function(t){void 0!==e[t]&&(s[t]=e[t]);}));var a=oe(s);o.forEach((function(t){void 0!==e[t]&&(r[t]=a[t]);})),k=a.spectrum,r.margin=a.margin,r.limit=a.limit,r.padding=a.padding,r.pips?ee(r.pips):Z(),r.tooltips?I():Y(),A=[],Ve(n(e.start)?e.start:i,t);}function Fe(){u=B(C),R(r.connect,u),he(r.events),Ve(r.start),r.pips&&ee(r.pips),r.tooltips&&I(),W();}Fe();var Te={destroy:Ue,steps:De,on:me,off:ge,get:Le,set:Ve,setHandle:Me,reset:Ae,__moveHandles:function(e,t,r){xe(e,t,A,r);},options:s,updateOptions:je,target:C,removePips:Z,removeTooltips:Y,getPositions:function(){return A.slice()},getTooltips:function(){return E},getOrigins:function(){return f},pips:ee};return Te}function se(e,t){if(!e||!e.nodeName)throw new Error("noUiSlider: create requires a single element, got: "+e);if(e.noUiSlider)throw new Error("noUiSlider: Slider was already initialized.");var r=ae(e,oe(t),t);return e.noUiSlider=r,r}var le={__spectrum:L,cssClasses:O,create:se};e.create=se,e.cssClasses=O,e.default=le,Object.defineProperty(e,"__esModule",{value:!0});}(t);})));function h(e,t){if(!Array.isArray(e)||!Array.isArray(t))return !1;const r=t.slice().sort();return e.length===t.length&&e.slice().sort().every((function(e,t){return e===r[t]}))}var m={name:"Slider",emits:["input","update:modelValue","start","slide","drag","update","change","set","end"],props:{...{value:{validator:function(e){return e=>"number"==typeof e||e instanceof Array||null==e||!1===e},required:!1},modelValue:{validator:function(e){return e=>"number"==typeof e||e instanceof Array||null==e||!1===e},required:!1}},id:{type:[String,Number],required:!1},disabled:{type:Boolean,required:!1,default:!1},min:{type:Number,required:!1,default:0},max:{type:Number,required:!1,default:100},step:{type:Number,required:!1,default:1},orientation:{type:String,required:!1,default:"horizontal"},direction:{type:String,required:!1,default:"ltr"},tooltips:{type:Boolean,required:!1,default:!0},options:{type:Object,required:!1,default:()=>({})},merge:{type:Number,required:!1,default:-1},format:{type:[Object,Function,Boolean],required:!1,default:null},classes:{type:Object,required:!1,default:()=>({})},showTooltip:{type:String,required:!1,default:"always"},tooltipPosition:{type:String,required:!1,default:null},lazy:{type:Boolean,required:!1,default:!0},ariaLabelledby:{type:String,required:!1,default:void 0},aria:{required:!1,type:Object,default:()=>({})}},setup(a,s){const l=function(r,i,n){const{value:o,modelValue:a,min:s}=toRefs(r);let l=a&&void 0!==a.value?a:o;const c=ref(l.value);if(u(l.value)&&(l=ref(s.value)),Array.isArray(l.value)&&0==l.value.length)throw new Error("Slider v-model must not be an empty array");return {value:l,initialValue:c}}(a),c=function(t,i,n){const{classes:o,showTooltip:a,tooltipPosition:s,orientation:l}=toRefs(t),u=computed$1((()=>({target:"slider-target",focused:"slider-focused",tooltipFocus:"slider-tooltip-focus",tooltipDrag:"slider-tooltip-drag",ltr:"slider-ltr",rtl:"slider-rtl",horizontal:"slider-horizontal",vertical:"slider-vertical",textDirectionRtl:"slider-txt-dir-rtl",textDirectionLtr:"slider-txt-dir-ltr",base:"slider-base",connects:"slider-connects",connect:"slider-connect",origin:"slider-origin",handle:"slider-handle",handleLower:"slider-handle-lower",handleUpper:"slider-handle-upper",touchArea:"slider-touch-area",tooltip:"slider-tooltip",tooltipTop:"slider-tooltip-top",tooltipBottom:"slider-tooltip-bottom",tooltipLeft:"slider-tooltip-left",tooltipRight:"slider-tooltip-right",tooltipHidden:"slider-tooltip-hidden",active:"slider-active",draggable:"slider-draggable",tap:"slider-state-tap",drag:"slider-state-drag",pips:"slider-pips",pipsHorizontal:"slider-pips-horizontal",pipsVertical:"slider-pips-vertical",marker:"slider-marker",markerHorizontal:"slider-marker-horizontal",markerVertical:"slider-marker-vertical",markerNormal:"slider-marker-normal",markerLarge:"slider-marker-large",markerSub:"slider-marker-sub",value:"slider-value",valueHorizontal:"slider-value-horizontal",valueVertical:"slider-value-vertical",valueNormal:"slider-value-normal",valueLarge:"slider-value-large",valueSub:"slider-value-sub",...o.value})));return {classList:computed$1((()=>{const e={...u.value};return Object.keys(e).forEach((t=>{e[t]=Array.isArray(e[t])?e[t].filter((e=>null!==e)).join(" "):e[t];})),"always"!==a.value&&(e.target+=` ${"drag"===a.value?e.tooltipDrag:e.tooltipFocus}`),"horizontal"===l.value&&(e.tooltip+="bottom"===s.value?` ${e.tooltipBottom}`:` ${e.tooltipTop}`),"vertical"===l.value&&(e.tooltip+="right"===s.value?` ${e.tooltipRight}`:` ${e.tooltipLeft}`),e}))}}(a),p=function(t,i,n){const{format:o,step:a}=toRefs(t),s=n.value,l=n.classList,u=computed$1((()=>o&&o.value?"function"==typeof o.value?{to:o.value}:d({...o.value}):d({decimals:a.value>=0?0:2}))),c=computed$1((()=>Array.isArray(s.value)?s.value.map((e=>u.value)):u.value));return {tooltipFormat:u,tooltipsFormat:c,tooltipsMerge:(e,t,r)=>{var i="rtl"===getComputedStyle(e).direction,n="rtl"===e.noUiSlider.options.direction,o="vertical"===e.noUiSlider.options.orientation,a=e.noUiSlider.getTooltips(),s=e.noUiSlider.getOrigins();a.forEach((function(e,t){e&&s[t].appendChild(e);})),e.noUiSlider.on("update",(function(e,s,c,p,d){var f=[[]],h=[[]],m=[[]],v=0;a[0]&&(f[0][0]=0,h[0][0]=d[0],m[0][0]=u.value.to(parseFloat(e[0])));for(var g=1;g<e.length;g++)(!a[g]||e[g]-e[g-1]>t)&&(f[++v]=[],m[v]=[],h[v]=[]),a[g]&&(f[v].push(g),m[v].push(u.value.to(parseFloat(e[g]))),h[v].push(d[g]));f.forEach((function(e,t){for(var s=e.length,u=0;u<s;u++){var c=e[u];if(u===s-1){var p=0;h[t].forEach((function(e){p+=1e3-e;}));var d=o?"bottom":"right",f=n?0:s-1,v=1e3-h[t][f];p=(i&&!o?100:0)+p/s-v,a[c].innerHTML=m[t].join(r),a[c].style.display="block",a[c].style[d]=p+"%",l.value.tooltipHidden.split(" ").forEach((e=>{a[c].classList.contains(e)&&a[c].classList.remove(e);}));}else a[c].style.display="none",l.value.tooltipHidden.split(" ").forEach((e=>{a[c].classList.add(e);}));}}));}));}}}(a,0,{value:l.value,classList:c.classList}),m=function(a,s,l){const{orientation:c,direction:p,tooltips:d,step:m,min:v,max:g,merge:b,id:y,disabled:S,options:x,classes:w,format:E,lazy:P,ariaLabelledby:N,aria:C}=toRefs(a),k=l.value,V=l.initialValue,A=l.tooltipsFormat,M=l.tooltipsMerge,L=l.tooltipFormat,U=l.classList,O=ref(null),D=ref(null),j=ref(!1),F=computed$1((()=>{let e={cssPrefix:"",cssClasses:U.value,orientation:c.value,direction:p.value,tooltips:!!d.value&&A.value,connect:"lower",start:u(k.value)?v.value:k.value,range:{min:v.value,max:g.value}};if(m.value>0&&(e.step=m.value),Array.isArray(k.value)&&(e.connect=!0),N&&N.value||C&&Object.keys(C.value).length){let t=Array.isArray(k.value)?k.value:[k.value];e.handleAttributes=t.map((e=>Object.assign({},C.value,N&&N.value?{"aria-labelledby":N.value}:{})));}return E.value&&(e.ariaFormat=L.value),e})),T=computed$1((()=>{let e={id:y&&y.value?y.value:void 0};return S.value&&(e.disabled=!0),e})),z=computed$1((()=>Array.isArray(k.value))),H=()=>{let e=D.value.get();return Array.isArray(e)?e.map((e=>parseFloat(e))):parseFloat(e)},q=function(e){let t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];D.value.set(e,t);},R=e=>{s.emit("input",e),s.emit("update:modelValue",e),s.emit("update",e);},B=()=>{D.value=f.create(O.value,Object.assign({},F.value,x.value)),d.value&&z.value&&b.value>=0&&M(O.value,b.value," - "),D.value.on("set",(()=>{const e=H();s.emit("change",e),s.emit("set",e),P.value&&R(e);})),D.value.on("update",(()=>{if(!j.value)return;const e=H();z.value&&h(k.value,e)||!z.value&&k.value==e?s.emit("update",e):P.value||R(e);})),D.value.on("start",(()=>{s.emit("start",H());})),D.value.on("end",(()=>{s.emit("end",H());})),D.value.on("slide",(()=>{s.emit("slide",H());})),D.value.on("drag",(()=>{s.emit("drag",H());})),O.value.querySelectorAll("[data-handle]").forEach((e=>{e.onblur=()=>{O.value&&U.value.focused.split(" ").forEach((e=>{O.value.classList.remove(e);}));},e.onfocus=()=>{U.value.focused.split(" ").forEach((e=>{O.value.classList.add(e);}));};})),j.value=!0;},_=()=>{D.value.off(),D.value.destroy(),D.value=null;},$=(e,t)=>{j.value=!1,_(),B();};return onMounted(B),onUnmounted(_),watch(z,$,{immediate:!1}),watch(v,$,{immediate:!1}),watch(g,$,{immediate:!1}),watch(m,$,{immediate:!1}),watch(c,$,{immediate:!1}),watch(p,$,{immediate:!1}),watch(d,$,{immediate:!1}),watch(b,$,{immediate:!1}),watch(E,$,{immediate:!1,deep:!0}),watch(x,$,{immediate:!1,deep:!0}),watch(w,$,{immediate:!1,deep:!0}),watch(k,((e,t)=>{t&&("object"==typeof t&&"object"==typeof e&&e&&Object.keys(t)>Object.keys(e)||"object"==typeof t&&"object"!=typeof e||u(e))&&$();}),{immediate:!1}),watch(k,(e=>{if(u(e))return void q(v.value,!1);let t=H();z.value&&!Array.isArray(t)&&(t=[t]),(z.value&&!h(e,t)||!z.value&&e!=t)&&q(e,!1);}),{deep:!0}),{slider:O,slider$:D,isRange:z,sliderProps:T,init:B,destroy:_,refresh:$,update:q,reset:()=>{R(V.value);}}}(a,s,{value:l.value,initialValue:l.initialValue,tooltipFormat:p.tooltipFormat,tooltipsFormat:p.tooltipsFormat,tooltipsMerge:p.tooltipsMerge,classList:c.classList});return {...c,...p,...m}}};m.render=function(e,t,r,i,n,o){return openBlock(),createElementBlock("div",mergeProps(e.sliderProps,{ref:"slider"}),null,16)},m.__file="src/Slider.vue";

    const _hoisted_1$O = { class: "m-2 px-3" };


    var script$T = {
      __name: 'date-slider',
      props: {
        id: String,
        min_epoch: Number,
        disabled_date_picker: Boolean,
    },
      emits: ['epoch_change'],
      setup(__props, { emit: __emit }) {

    const props = __props;

    const emit = __emit;

    const timelineValue = ref(0);
    const timelineMin = ref(0);
    const timelineMax = ref(0);
    const timelineStep = ref(24 * 60 * 60); /* day */

    const format = function (value) {
        let today = formatterUtils.getMidnightEpoch(ntopng_utility$1.get_utc_seconds(Date.now()));
        if (value == today) {
            return i18n("live");
        }
        
        let str = formatterUtils.formatDateTime(value, 'date_only');
        return str;
    };

    /* ************************************** */

    onBeforeMount(() => {
        let min = props.min_epoch;
        let today = formatterUtils.getMidnightEpoch(getUTCSeconds(Date.now()));
        if (!min || formatterUtils.getMidnightEpoch(min) == today) {
            let yesterday = formatterUtils.getMidnightEpoch(today - timelineStep.value);
            min = yesterday; /* set begin time to yesterday at least */
        }
        setSliderMin(min);
    });

    /* ************************************** */

    onMounted(() => {
        let epoch_begin = ntopng_url_manager$1.get_url_entry("epoch_begin");
        let epoch_end = ntopng_url_manager$1.get_url_entry("epoch_end");

        if (epoch_begin != null && epoch_end != null) {
            // update the status
            epoch_begin = Number.parseInt(epoch_begin);
            epoch_end = Number.parseInt(epoch_end);
            epoch_begin = formatterUtils.getMidnightEpoch(epoch_begin); /* align to midnight */
            epoch_end = epoch_begin + timelineStep.value;

            emitEpochChange({ epoch_begin: epoch_begin, epoch_end: epoch_end }, props.id, true);
        }

        ntopng_events_manager$1.on_event_change(props.id, ntopng_events.EPOCH_CHANGE, (new_status) => onStatusUpdated(new_status), true);

        // notifies that component is ready
        ntopng_sync.ready(props.id);
    });

    /* ************************************** */

    function setSliderValue(val) {
        timelineValue.value = val;
    }

    /* ************************************** */

    function setSliderMin(val) {
        timelineMin.value = formatterUtils.getMidnightEpoch(val);
    }

    /* ************************************** */

    function setSliderMax(val) {
        timelineMax.value = val;
    }

    /* ************************************** */

    const getUTCSeconds = function (utc_ms) {
        return ntopng_utility$1.get_utc_seconds(utc_ms);
    };

    /* ************************************** */

    const emitEpochChange = function (epoch_status, id, emit_only_global_event) {
        if (epoch_status.epoch_end == null || epoch_status.epoch_begin == null) { return; }    if (id != props.id) {
            onStatusUpdated(epoch_status);
        }
        ntopng_events_manager$1.emit_event(ntopng_events.EPOCH_CHANGE, epoch_status, props.id);
        if (emit_only_global_event) {
            return;
        }
        let today = formatterUtils.getMidnightEpoch(ntopng_utility$1.get_utc_seconds(Date.now()));
        if (epoch_status.epoch_begin == today) {
            epoch_status.isToday = true;
        }
        emit("epoch_change", epoch_status);
    };

    /* ************************************** */

    function onSliderChange(newValue) {
        let epoch_begin = newValue;
        let epoch_end = newValue + timelineStep.value;
        let epoch_status = { epoch_begin: epoch_begin, epoch_end: epoch_end };
        ntopng_url_manager$1.add_obj_to_url(epoch_status);
        emitEpochChange(epoch_status, props.id);
    }

    /* ************************************** */

    const onStatusUpdated = function (status) {
        let now = getUTCSeconds(Date.now());
        now = formatterUtils.getMidnightEpoch(now);

        setSliderMax(now);

        if (status.epoch_end != null && status.epoch_begin != null
            && Number.parseInt(status.epoch_end) > Number.parseInt(status.epoch_begin)) {
            status.epoch_begin = Number.parseInt(status.epoch_begin);
            status.epoch_end = Number.parseInt(status.epoch_end);
        } else {
            // First iteration, set to Now
            let now = getUTCSeconds(Date.now());
            status.epoch_begin = now;
            status.epoch_end = now + timelineStep.value;

            ntopng_url_manager$1.add_obj_to_url(status);
            /* Skip the emit on the first */
            //emitEpochChange(status, props.id);
        }

        setSliderValue(status.epoch_begin);

        ntopng_url_manager$1.add_obj_to_url({ epoch_begin: status.epoch_begin, epoch_end: status.epoch_end });
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$O, [
        createVNode(unref(m), {
          modelValue: timelineValue.value,
          "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((timelineValue).value = $event)),
          format: format,
          min: timelineMin.value,
          max: timelineMax.value,
          step: timelineStep.value,
          disabled: __props.disabled_date_picker,
          tooltipPosition: "right",
          onChange: onSliderChange,
          class: "slider-blue"
        }, null, 8 /* PROPS */, ["modelValue", "min", "max", "step", "disabled"])
      ]))
    }
    }

    };

    var css_248z$f = "\n.slider-blue[data-v-334ce383] {\n  --slider-connect-bg: #3B82F6;\n  --slider-tooltip-bg: #3B82F6;\n  --slider-handle-ring-color: #3B82F630;\n}\n\n";
    styleInject(css_248z$f);

    script$T.__scopeId = "data-v-334ce383";
    script$T.__file = "http_src/vue/date-slider.vue";

    const _hoisted_1$N = { class: "button-group mb-2 d-flex align-items-center" };
    const _hoisted_2$F = { class: "dropdown me-2 d-flex" };
    const _hoisted_3$A = { class: "no-wrap d-flex align-items-center filters-label me-2" };
    const _hoisted_4$z = {
      key: 0,
      class: "w-100 d-flex align-items-center button-group"
    };
    const _hoisted_5$v = { class: "w-100 position-relative" };
    const _hoisted_6$p = { class: "m-2 mb-3" };
    const _hoisted_7$l = { class: "position-relative" };
    const _hoisted_8$j = {
      class: "mb-4 d-flex flex-column",
      style: {"height":"60vh"}
    };
    const _hoisted_9$h = { class: "card-footer" };


    var script$S = {
      __name: 'page-as-overview',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const _i18n = (t) => i18n(t);
    const sankey_url = `${http_prefix}/lua/rest/v2/get/asn/sankey.lua`;
    const pie_url = `${http_prefix}/lua/rest/v2/get/asn/pie.lua`;
    const sankey_chart = ref(null);
    const sankey_data = ref({});
    const pie_chart = ref(null);
    const pie_data = ref({});
    const show_table = ref(true);
    const show_pie = ref(true);
    const show_sankey = ref(true);
    const loading = ref(true);
    const no_data_message = _i18n("as_overview.no_data");
    let intervalId = null;
    const active_sankey_type = ref({});
    const table_as_stats = ref(null);
    const reRenderTable = ref(false);
    const reRenderPie = ref(false);
    const main_epoch_interval = ref(null);
    const first_date_epoch = ref(props.context.first_date_epoch);
    const toggle_slider = ref(false);
    const toggle_slider_label = ref(_i18n("db_search.time_range"));
    ref(true);
    const customerIcon = '<i class="fa-solid fa-house-flag" data-bs-toggle="tooltip" data-bs-placement="top" title="' + _i18n("asn_configuration.customer_asn_title") + '"></i>';
    const subCustomerIcon = '<i class="fa-solid fa-house-laptop" data-bs-toggle="tooltip" data-bs-placement="top" title="' + _i18n("asn_configuration.sub_customer_asn_title") + '"></i>';
    const remoteIcon = '<i class="fa-solid fa-house-fire" data-bs-toggle="tooltip" data-bs-placement="top" title="' + _i18n("asn_configuration.remote_asn_title") + '"></i>';
    const sankey_format_list = [
        { key: "criteria_as", value: 'ingress_egress_traffic_criteria', label: _i18n('as_overview.ingress_egress_traffic_criteria') },
        { key: "criteria_as", value: 'traffic_between_ases', label: _i18n('as_overview.as_traffic_criteria') },
    ];
    const time_preset_list = [
        { value: "live", label: i18n('live'), currently_active: true },
        { value: "2_hours", label: i18n('show_alerts.presets.2_hours'), currently_active: false },
        { value: "12_hours", label: i18n('show_alerts.presets.12_hours'), currently_active: false },
        { value: "day", label: i18n('show_alerts.presets.day'), currently_active: false },
        { value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
        { value: "month", label: i18n('show_alerts.presets.month'), currently_active: false },
        { value: "year", label: i18n('show_alerts.presets.year'), currently_active: false },
        { value: "custom", label: i18n('show_alerts.presets.custom'), currently_active: false, disabled: true, },
    ];

    const note_list = [
        _i18n("as_overview.note_ingress_egress"),
    ];

    /* ************************************** */

    onMounted(() => {
        updateSankeyData();
        updatePieData();
    });

    /* ************************************** */

    onBeforeMount(() => {
        defineDropdown();
        const criteria = ntopng_url_manager$1.get_url_entry("criteria_as");
        toggle_slider.value = localStorage.getItem("as-overview-slider") == "true";
        active_sankey_type.value = sankey_format_list[0];
        if (criteria) {
            sankey_format_list.forEach((element) => {
                if (element.value == criteria) {
                    active_sankey_type.value = element;
                }
            });
        }
        ntopng_url_manager$1.set_key_to_url("criteria_as", active_sankey_type.value.value);
        checkComponentsToShow();
    });

    /* ************************************** */

    function checkComponentsToShow() {
        /* First two entries of the dropdown have both sankey + table */
        if (active_sankey_type.value.value == "traffic_between_ases" ||
            active_sankey_type.value.value == "ingress_egress_traffic_criteria") {
            show_table.value = true;
            show_sankey.value = true;
            show_pie.value = false;
        } else if (active_sankey_type.value.value == "user_traffic_breakdown") {
            show_table.value = false;
            show_sankey.value = false;
            show_pie.value = true;
        }
    }

    /* ************************************** */

    const changeTime = (selectedTimeframe) => {
        let interval = 0; // Live
        if (selectedTimeframe !== 'live') {
            const timeframesDict = ntopng_utility.get_timeframes_dict();
            interval = timeframesDict[selectedTimeframe];
        }
        const epoch_end = ntopng_utility.get_utc_seconds(Date.now());
        const epoch_begin = epoch_end - interval;
        return [epoch_begin, epoch_end]
    };

    /* ************************************** */

    const onAutoRefreshToggle = (enabled) => {
        if (enabled) {
            intervalId = setInterval(() => {
                updateSankeyData();
                updatePieData();
            }, 10000 /* 10 sec refresh */);
        } else {
            clearInterval(intervalId);
        }
    };

    /* ************************************** */

    const table_id = computed$1(() => {
        if (active_sankey_type.value?.value === "ingress_egress_traffic_criteria") {
            return "ingress_egress_as_stats"
        } else if (active_sankey_type.value?.value === "traffic_between_ases") {
            return "traffic_between_ases"
        } else if (active_sankey_type.value?.value === "user_traffic_breakdown") {
            return "user_traffic_breakdown"
        }

        return props.context.tableId;
    });

    /* ************************************** */

    /* This function is called upon changing the selected option in the dropdown */
    const changeCriteria = async (opt) => {
        checkComponentsToShow();
        ntopng_url_manager$1.set_key_to_url(opt.key, `${opt.value}`);
        updateSankeyData();
        updatePieData();
    };

    /* ************************************** */

    function reloadTable() {
        table_as_stats.value.refresh_table();
    }

    /* ************************************** */

    function setTimeInterval(epoch_interval) {
        // Check if it's live
        if (epoch_interval.isToday) {
            ntopng_url_manager$1.delete_key_from_url("type");
        } else {
            ntopng_url_manager$1.set_key_to_url("type", "historical");
        }

        if (epoch_interval.timeframe_id === 'live' || (epoch_interval.epoch_begin === epoch_interval.epoch_end)) {
            ntopng_url_manager$1.delete_key_from_url("type");
            ntopng_url_manager$1.delete_key_from_url("timeframe_id");
        }
        main_epoch_interval.value = epoch_interval;
        updateSankeyData();
        updatePieData();
        reloadTable();
    }

    /* ************************************** */

    function saveSwitch() {
        localStorage.setItem("as-overview-slider", toggle_slider.value);
    }

    /* ************************************** */

    const updatePieData = async () => {
        if (show_pie.value) {
            reRenderPie.value = !reRenderPie.value;
            loading.value = true;
            const data = await getPieData();
            pie_data.value = data;
            loading.value = false;
        }
    };

    /* ************************************** */

    const getPieData = async () => {
        const url_request = getPieUrl();
        let graph = await ntopng_utility.http_request(url_request);
        return graph
    };

    /* ************************************** */

    const getPieUrl = () => {
        let params = {
            ifid: props.context.ifid,
            ...getExtraParameters()
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        let url_request = `${pie_url}?${url_params}`;
        return url_request;
    };

    /* ************************************** */

    const updateSankeyData = async () => {
        if (show_sankey.value) {
            loading.value = true;
            let data = await getSankeyData();
            sankey_data.value = data;
            loading.value = false;
        }
    };

    /* ************************************** */

    const getSankeyData = async () => {
        const url_request = getSankeyUrl();
        let graph = await ntopng_utility.http_request(url_request);
        graph.nodes.forEach((node, i) => {
            node.index = i;
        });
        graph.links.forEach((link, i) => {
            if (link.value === 0) {
                link.value = 1;
            }
            let node = graph.nodes.find((el) => el.node_id == link.source_node_id);
            link.source = node.index;
            node = graph.nodes.find((el) => el.node_id == link.target_node_id);
            link.target = node.index;
        });

        return graph
    };

    /* ************************************** */

    const getSankeyUrl = () => {
        let params = {
            ifid: props.context.ifid,
            ...getExtraParameters()
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        let url_request = `${sankey_url}?${url_params}`;
        return url_request;
    };

    /* ************************************** */

    function onNodeClick(_, node) {
        if (node.link) {
            ntopng_url_manager$1.go_to_url(node.link);
        }
    }

    /* ***************************************************** */

    const getExtraParameters = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function defineDropdown() {
        let asn_configuration = props.context.configuration;
        if (asn_configuration != "customer_asn" && asn_configuration != "sub_customer_asn") {
            sankey_format_list.push({
                key: "criteria_as",
                value: "user_traffic_breakdown",
                label: _i18n("as_overview.user_traffic_breakdown")
            });
        }
    }

    /* ************************************** */

    function clickButtonTimeseries(event) {
        const row = event.row;
        const asn = ntopng_url_manager$1.get_url_entry("asn");
        const url = `${http_prefix}/lua/as_overview.lua?asn=${asn}&page=historical&ts_schema=asn:exporter_traffic&ts_query=ifid:${props.context.ifid},asn:${asn},device:${row["device"]["id"]},if_index:${row["interface"]["id"]}`;
        window.location.href = url;
    }


    /* ************************************** */

    function onTableCustomEvent(event) {
        let events_managed = {
            "click_button_timeseries": clickButtonTimeseries,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    function columnsSorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "device") {
                return sortingFunctions$1.sortByName(r0.device.name, r1.device.name, col.sort);
            } else if (col.id == "interface") {
                return sortingFunctions$1.sortByName(r0.interface.name, r1.interface.name, col.sort);
            } else if (col.id == "customer") {
                return sortingFunctions$1.sortByName(r0.customer.name, r1.customer.name, col.sort);
            } else if (col.id == "as") {
                return sortingFunctions$1.sortByName(r0.as.name, r1.as.name, col.sort);
            } else if (col.id == "dst_as") {
                return sortingFunctions$1.sortByName(r0.dst_as.name, r1.dst_as.name, col.sort);
            } else if (col.id == "src_as") {
                return sortingFunctions$1.sortByName(r0.src_as.name, r1.src_as.name, col.sort);
            } else if (col.id == "src_transit_as") {
                return sortingFunctions$1.sortByName(r0.src_transit_as?.name || "", r1.src_transit_as?.name || "", col.sort);
            } else if (col.id == "dst_transit_as") {
                return sortingFunctions$1.sortByName(r0.dst_transit_as?.name || "", r1.dst_transit_as?.name || "", col.sort);
            } else if (col.id == "transit_as") {
                return sortingFunctions$1.sortByName(r0.transit_as?.name || "", r1.transit_as?.name || "", col.sort);
            } else if (col.id == "bytes_sent") {
                return sortingFunctions$1.sortByNumber(r0.bytes_sent, r1.bytes_sent, col.sort);
            } else if (col.id == "bytes_rcvd") {
                return sortingFunctions$1.sortByNumber(r0.bytes_rcvd, r1.bytes_rcvd, col.sort);
            } else if (col.id == "total_bytes") {
                return sortingFunctions$1.sortByNumber(r0.total_bytes, r1.total_bytes, col.sort);
            }
        }
        /* Default sorting */
        return sortingFunctions$1.sortByNumber(r0.total_bytes, r1.total_bytes, col.sort);
    }

    /* ************************************** */

    const formatAS = (value) => {
        if (!value) {
            return ''
        }
        let addTitle = true;
        let importantASNIcon = '';
        const title = `data-bs-toggle="tooltip" data-bs-placement="top" title="${value.id}"`;
        if (value.is_customer_asn) {
            importantASNIcon = customerIcon;
        } else if (value.is_sub_customer_asn) {
            importantASNIcon = subCustomerIcon;
        } else if (value.is_remote_asn) {
            importantASNIcon = remoteIcon;
        }
        if (dataUtils.isEmptyString(value.name)) {
            addTitle = false;
        }
        if (!dataUtils.isEmptyString(value.url)) {
            return `<a href="${value.url}" ${addTitle ? title : ""}>${value.name}</a> ${importantASNIcon}`;
        }
        return `<span ${addTitle ? title : ""}>${value.name}</span> ${importantASNIcon}`;
    };

    /* ************************************** */

    const mapTableColumns = (columns) => {
        let map_columns = {
            "device": (value, row) => {
                if (dataUtils.isEmptyString(value.name)) {
                    return value.id
                }
                if(!dataUtils.isEmptyString(value.url)){
                    return `<a href="${value.url}" title="${value.id}">${value.name}</a>`
                }
                return `<span data-bs-toggle="tooltip" data-bs-placement="top" title="${value.id}">${value.name}</span>`;
            },
            "interface": (value, row) => {
                if (dataUtils.isEmptyString(value.name)) {
                    return value.id
                }
                if(!dataUtils.isEmptyString(value.url)){
                    return `<a href="${value.url}" title="${value.id}">${value.name}</a>`
                }
                return `<span data-bs-toggle="tooltip" data-bs-placement="top" title="${value.id}">${value.name}</span>`;
            },
            "customer": (value, row) => {
                return formatAS(value);
            },
            "as": (value, row) => {
                return formatAS(value);
            },
            "dst_as": (value, row) => {
                return formatAS(value);
            },
            "src_as": (value, row) => {
                return formatAS(value);
            },
            "src_transit_as": (value, row) => {
                return formatAS(value);
            },
            "dst_transit_as": (value, row) => {
                return formatAS(value);
            },
            "bytes_sent": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
            "bytes_rcvd": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
            "total_bytes": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    timeseries: props.context.showTimeseries
                };
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        // if is not defined is enabled
                        if (!visible_dict[b.id]) {
                            current_class.push("disabled");
                        } else if (row.asn === 0 && (b.id === "exporters_stats" || b.id === "timeseries")) {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$N, [
          createBaseVNode("div", _hoisted_2$F, [
            createBaseVNode("span", _hoisted_3$A, [
              createBaseVNode("b", null, toDisplayString(_i18n("view_options")) + ": ", 1 /* TEXT */)
            ]),
            createVNode(script$2H, {
              selected_option: active_sankey_type.value,
              "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((active_sankey_type).value = $event)),
              options: sankey_format_list,
              onSelect_option: changeCriteria
            }, null, 8 /* PROPS */, ["selected_option"])
          ]),
          (props.context.isEnterpriseL)
            ? (openBlock(), createElementBlock("div", _hoisted_4$z, [
                createVNode(script$2k, {
                  value: toggle_slider.value,
                  "onUpdate:value": _cache[1] || (_cache[1] = $event => ((toggle_slider).value = $event)),
                  change_label_side: true,
                  label: toggle_slider_label.value,
                  style: {},
                  class: "me-1",
                  icon: "fa-calendar-days",
                  title: toggle_slider_label.value,
                  onChange_value: saveSwitch
                }, null, 8 /* PROPS */, ["value", "label", "title"]),
                createBaseVNode("div", _hoisted_5$v, [
                  createVNode(Transition, {
                    name: "add-effect",
                    mode: "out-in"
                  }, {
                    default: withCtx(() => [
                      (!toggle_slider.value)
                        ? (openBlock(), createBlock(script$2G, {
                            key: 0,
                            class: "dontprint",
                            id: "as-date-time-picker",
                            round_time: true,
                            custom_time_interval_list: time_preset_list,
                            min_time_interval_id: "live",
                            custom_change_select_time: changeTime,
                            onEpoch_change: setTimeInterval
                          }))
                        : createCommentVNode("v-if", true)
                    ]),
                    _: 1 /* STABLE */
                  }),
                  createVNode(Transition, {
                    name: "add-effect",
                    mode: "out-in"
                  }, {
                    default: withCtx(() => [
                      (toggle_slider.value)
                        ? (openBlock(), createBlock(script$T, {
                            key: 0,
                            id: "as-date-slider",
                            min_epoch: first_date_epoch.value,
                            onEpoch_change: setTimeInterval,
                            style: {"width":"100%"}
                          }, null, 8 /* PROPS */, ["min_epoch"]))
                        : createCommentVNode("v-if", true)
                    ]),
                    _: 1 /* STABLE */
                  })
                ])
              ]))
            : createCommentVNode("v-if", true)
        ]),
        createBaseVNode("div", _hoisted_6$p, [
          createVNode(Transition, {
            name: "add-effect",
            mode: "out-in"
          }, {
            default: withCtx(() => [
              createBaseVNode("div", _hoisted_7$l, [
                createBaseVNode("div", _hoisted_8$j, [
                  createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
                  (show_sankey.value)
                    ? (openBlock(), createBlock(script$1T, {
                        key: 0,
                        ref_key: "sankey_chart",
                        ref: sankey_chart,
                        no_data_message: unref(no_data_message),
                        sankey_data: sankey_data.value,
                        onNode_click: onNodeClick,
                        onAutorefresh_toggle: onAutoRefreshToggle
                      }, null, 8 /* PROPS */, ["no_data_message", "sankey_data"]))
                    : createCommentVNode("v-if", true),
                  (show_pie.value)
                    ? (openBlock(), createBlock(script$U, {
                        key: reRenderPie.value,
                        ref_key: "pie_chart",
                        ref: pie_chart,
                        no_data_message: unref(no_data_message),
                        pie_data: pie_data.value,
                        onAutorefresh_toggle: onAutoRefreshToggle
                      }, {
                        default: withCtx(() => [...(_cache[2] || (_cache[2] = [
                          createTextVNode("> ", -1 /* CACHED */)
                        ]))]),
                        _: 1 /* STABLE */
                      }, 8 /* PROPS */, ["no_data_message", "pie_data"]))
                    : createCommentVNode("v-if", true)
                ])
              ])
            ]),
            _: 1 /* STABLE */
          }),
          createVNode(Transition, {
            name: "add-effect",
            mode: "out-in"
          }, {
            default: withCtx(() => [
              (props.context.isEnterpriseL)
                ? (openBlock(), createElementBlock("div", {
                    class: "position-relative",
                    key: reRenderTable.value,
                    style: {"min-height":"614px"}
                  }, [
                    createVNode(script$2w, {
                      ref_key: "table_as_stats",
                      ref: table_as_stats,
                      table_id: table_id.value,
                      csrf: props.context.csrf,
                      showLoading: true,
                      f_map_columns: mapTableColumns,
                      f_sort_rows: columnsSorting,
                      get_extra_params_obj: getExtraParameters,
                      onCustom_event: onTableCustomEvent
                    }, null, 8 /* PROPS */, ["table_id", "csrf"])
                  ]))
                : createCommentVNode("v-if", true)
            ]),
            _: 1 /* STABLE */
          }),
          createBaseVNode("div", _hoisted_9$h, [
            createVNode(script$2u, { note_list: note_list })
          ])
        ])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    var css_248z$e = "\n.add-effect-move[data-v-69e7083a],\n\n.add-effect-enter-active[data-v-69e7083a],\n.add-effect-leave-active[data-v-69e7083a] {\n    transition: all 0.35s ease;\n}\n\n/* Transform: positive pixels, the effects let enters the component\n * from the right, negative pixels from the left\n */\n.add-effect-enter-from[data-v-69e7083a] {\n    opacity: 0;\n    transform: translateX(-60px);\n}\n.add-effect-leave-to[data-v-69e7083a] {\n    opacity: 0;\n    transform: translateX(0px);\n}\n\n/* ensure leaving items are taken out of layout flow so that moving\n   animations can be calculated correctly. */\n.add-effect-leave-active[data-v-69e7083a] {\n    position: absolute;\n}\n.slider-connect[data-v-69e7083a] {\n    background: none !important;\n}\n";
    styleInject(css_248z$e);

    script$S.__scopeId = "data-v-69e7083a";
    script$S.__file = "http_src/vue/page-as-overview.vue";

    const _hoisted_1$M = { class: "m-2 mb-3" };
    const _hoisted_2$E = {
      class: "mb-3 d-flex flex-column",
      style: {"height":"30vh"}
    };
    const _hoisted_3$z = { class: "d-flex align-items-center mb-2" };
    const _hoisted_4$y = { class: "d-flex no-wrap" };
    const _hoisted_5$u = { class: "m-1" };
    const _hoisted_6$o = { style: {"min-width":"16rem"} };
    const _hoisted_7$k = { class: "me-1" };
    const _hoisted_8$i = { class: "card-footer" };


    var script$R = {
      __name: 'page-probes',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const table_id = ref('probes');
    const table_probes = ref(null);
    const csrf = props.context.csrf;
    const _i18n = (t) => i18n(t);
    const first_open = ref(true);
    const exporter_url = `${http_prefix}/lua/pro/enterprise/exporters.lua?`;
    const sankey_url = `${http_prefix}/lua/pro/rest/v2/get/exporters/sankey.lua`;
    const host_url = `${http_prefix}/lua/host_details.lua?`;
    const sankey_chart = ref(null);
    const sankey_data = ref({});
    const loading = ref(false);
    const no_data_message = i18n("no_nprobes_message");
    const active_sankey_type = ref({});
    const sankey_format_list = [
        { key: "criteria", value: 'flow_volume_criteria', label: _i18n('exporters_page.flow_volume_criteria') },
        { key: "criteria", value: 'flow_drops_criteria', label: _i18n('exporters_page.flow_drops_criteria') },
    ];
    const note_list = [
    _i18n("exporters_page.failed_exports_descr"),
    _i18n("exporters_page.dropped_packets_descr"),
    ];

    /* ************************************** */

    onBeforeMount(() => {
        loading.value = true;
        const criteria = ntopng_url_manager.get_url_entry("criteria");
        active_sankey_type.value = sankey_format_list[0];
        if (criteria) {
            sankey_format_list.forEach((element) => {
                if (element.value == criteria) {
                    active_sankey_type.value = element;
                }
            });
        }
    });

    onMounted(() => {
        update_sankey_data();
        setInterval(() => {
            first_open.value = false;
            table_probes.value.refresh_table();
            update_sankey_data();
        }, 10000 /* 10 sec refresh */);
        loading.value = false;
    });

    /* ************************************** */

    const add_sankey_filter = async (opt) => {
        ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
        update_sankey_data();
    };

    const update_sankey_data = async () => {
        let data = await get_sankey_data();
        sankey_data.value = data;
    };

    const get_sankey_data = async () => {
        const url_request = get_sankey_url();
        let graph = await ntopng_utility.http_request(url_request);
        graph.nodes.forEach((node, i) => {
            node.index = i;
        });
        graph.links.forEach((link, i) => {
            if (link.value === 0) {
                link.value = 1;
            }
            let node = graph.nodes.find((el) => el.node_id == link.source_node_id);
            link.source = node.index;
            node = graph.nodes.find((el) => el.node_id == link.target_node_id);
            link.target = node.index;
        });
        return graph
    };

    const get_sankey_url = () => {
        let params = {
            ifid: props.context.ifid,
            ...get_extra_params_obj()
        };
        let url_params = ntopng_url_manager.obj_to_url_params(params);
        let url_request = `${sankey_url}?${url_params}`;
        return url_request;
    };

    function on_node_click(_, node) {
        if (node.link) {
            ntopng_url_manager.go_to_url(node.link);
        }
    }

    /* ***************************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "probe_ip": (value, row) => {
                let probe_ip = value;
                if (!dataUtils.isEmptyOrNull(row['probe_name'])) {
                    probe_ip = `${row['probe_name']}`;
                }

                let host_link = "";
                if (row.is_in_memory) {
                    host_link = ` <a href=${host_url}host=${value}> <i class="fas fa-laptop"></i></a>`;
                }
                return `<a href=${exporter_url}probe_uuid=${row.probe_source_id}>${probe_ip}</a>${host_link}`
            },
            "probe_public_ip": (value, row) => {
                return value
            },
            "probe_uuid": (value, row) => {
                return value
            },
            "probe_interface": (value, row) => {
                return value
            },
            "ntopng_interface": (value, row) => {
                return value
            },
            "flow_exporters": (value, row) => {
                if (!value) {
                    return '';
                } else {
                    return formatterUtils.getFormatter("number")(value)
                }
            },
            "exporters_interfaces": (value, row) => {
                if (!value) {
                    return '';
                } else {
                    return formatterUtils.getFormatter("number")(value)
                }
            },
            "dropped_flows": (value, row) => {
                let diff_value = value;
                if (!first_open.value) {
                    const old_value = localStorage.getItem("probe_dropped_flows." + row.probe_source_id);
                    diff_value = (value - Number(old_value)) / 10;
                }
                localStorage.setItem("probe_dropped_flows." + row.probe_source_id, value);
                if (!value) {
                    return '';
                } else {
                    let formatted_value = formatterUtils.formatAccounting(value);
                    if (!first_open.value) {
                        let updated_counter = '';
                        if (diff_value > 0) {
                            updated_counter = '<i class="fas fa-arrow-up"></i>';
                        } else {
                            updated_counter = "<i class='fas fa-minus'></i>";
                        }
                        formatted_value = `${formatted_value} ${updated_counter}`;
                    }
                    return formatted_value
                }
            },
            "captured_packets": (value, row) => {
                let diff_value = value;
                if (!first_open.value) {
                    const old_value = localStorage.getItem("exporter_captured_packets." + row.exporter_uuid + row.ip);
                    diff_value = (value - Number(old_value)) / 10;
                }
                localStorage.setItem("exporter_captured_packets." + row.exporter_uuid + row.ip, value);
                if (!value)
                    return '';
                let formatted_value = formatterUtils.formatAccounting(Math.abs(value));
                if (!first_open.value) {
                    let updated_counter = '';
                    if (diff_value > 0) {
                        updated_counter = '<i class="fas fa-arrow-up"></i>';
                    } else {
                        updated_counter = "<i class='fas fa-minus'></i>";
                    }
                    formatted_value = `${formatted_value} ${updated_counter}`;
                }
                return formatted_value
            },
            "dropped_packets": (value, row) => {
                let diff_value = value;
                if (!first_open.value) {
                    const old_value = localStorage.getItem("exporter_dropped_packets." + row.exporter_uuid + row.ip);
                    diff_value = (value - Number(old_value)) / 10;
                }
                localStorage.setItem("exporter_dropped_packets." + row.exporter_uuid + row.ip, value);
                if (!value)
                    return '';
                let formatted_value = formatterUtils.formatAccounting(Math.abs(value));
                if (!first_open.value) {
                    let updated_counter = '';
                    if (diff_value > 0) {
                        updated_counter = '<i class="fas fa-arrow-up"></i>';
                    } else {
                        updated_counter = "<i class='fas fa-minus'></i>";
                    }
                    formatted_value = `${formatted_value} ${updated_counter}`;
                }
                return formatted_value
            },
            "exported_flows": (value, row) => {
                let diff_value = value;
                if (!first_open.value) {
                    const old_value = localStorage.getItem("probe_exported_flows." + row.probe_source_id);
                    diff_value = (value - Number(old_value)) / 10;
                }
                localStorage.setItem("probe_exported_flows." + row.probe_source_id, value);
                if (!value) {
                    return '';
                } else {
                    let formatted_value = formatterUtils.formatAccounting(value);
                    if (!first_open.value) {
                        let updated_counter = '';
                        if (diff_value > 0) {
                            updated_counter = '<i class="fas fa-arrow-up"></i>';
                        } else {
                            updated_counter = "<i class='fas fa-minus'></i>";
                        }
                        formatted_value = `${formatted_value} ${updated_counter}`;
                    }
                    return formatted_value
                }
            },
            "probe_edition": (value, row) => {
                return value
            },
            "probe_last_update": (value, row) => {
                if (!value)
                    return '';
                else
                    return (NtopUtils.secondsToTime((Math.round(new Date().getTime() / 1000)) - value) + " ago");
            },
            "probe_license": (value, row) => {
                return value
            },
            "probe_maintenance": (value, row) => {
                return value
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });

        return columns;
    };


    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "probe_ip") {
                return sortingFunctions$1.sortByIP(r0.probe_ip, r1.probe_ip, col.sort);
            } else if (col.id == "probe_public_ip") {
                return sortingFunctions$1.sortByIP(r0.probe_public_ip, r1.probe_public_ip, col.sort);
            } else if (col.id == "probe_uuid") {
                return sortingFunctions$1.sortByName(r0.probe_uuid, r1.probe_uuid, col.sort);
            } else if (col.id == "probe_interface") {
                return sortingFunctions$1.sortByName(r0.probe_interface, r1.probe_interface, col.sort);
            } else if (col.id == "ntopng_interface") {
                return sortingFunctions$1.sortByName(r0.ntopng_interface, r1.ntopng_interface, col.sort);
            } else if (col.id == "flow_exporters") {
                return sortingFunctions$1.sortByNumber(r0.flow_exporters, r1.flow_exporters, col.sort);
            } else if (col.id == "exporters_interfaces") {
                return sortingFunctions$1.sortByNumber(r0.exporters_interfaces, r1.exporters_interfaces, col.sort);
            } else if (col.id == "dropped_flows") {
                return sortingFunctions$1.sortByNumber(r0.dropped_flows, r1.dropped_flows, col.sort);
            } else if (col.id == "exported_flows") {
                return sortingFunctions$1.sortByNumber(r0.exported_flows, r1.exported_flows, col.sort);
            } else if (col.id == "captured_packets") {
                return sortingFunctions$1.sortByNumber(r0.captured_packets, r1.captured_packets, col.sort);
            } else if (col.id == "dropped_packets") {
                return sortingFunctions$1.sortByNumber(r0.dropped_packets, r1.dropped_packets, col.sort);
            } else if (col.id == "probe_edition") {
                return sortingFunctions$1.sortByName(r0.probe_edition, r1.probe_edition, col.sort);
            } else if (col.id == "probe_license") {
                return sortingFunctions$1.sortByName(r0.probe_license, r1.probe_license, col.sort);
            } else if (col.id == "probe_maintenance") {
                return sortingFunctions$1.sortByName(r0.probe_maintenance, r1.probe_maintenance, col.sort);
            } else if (col.id == "probe_last_update") {
                return sortingFunctions$1.sortByNumber(r0.probe_last_update, r1.probe_last_update, col.sort);
            }
        }
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$M, [
        createBaseVNode("div", _hoisted_2$E, [
          createBaseVNode("div", _hoisted_3$z, [
            createBaseVNode("div", _hoisted_4$y, [
              createBaseVNode("div", _hoisted_5$u, [
                createBaseVNode("div", _hoisted_6$o, [
                  createBaseVNode("label", _hoisted_7$k, toDisplayString(_i18n('criteria')) + ": ", 1 /* TEXT */),
                  createVNode(script$2H, {
                    selected_option: active_sankey_type.value,
                    "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((active_sankey_type).value = $event)),
                    options: sankey_format_list,
                    onSelect_option: add_sankey_filter
                  }, null, 8 /* PROPS */, ["selected_option"])
                ])
              ])
            ])
          ]),
          createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
          createVNode(script$1T, {
            ref_key: "sankey_chart",
            ref: sankey_chart,
            no_data_message: unref(no_data_message),
            sankey_data: sankey_data.value,
            onNode_click: on_node_click
          }, null, 8 /* PROPS */, ["no_data_message", "sankey_data"])
        ]),
        createVNode(script$2w, {
          ref_key: "table_probes",
          ref: table_probes,
          table_id: table_id.value,
          csrf: unref(csrf),
          f_map_columns: map_table_def_columns,
          f_sort_rows: columns_sorting,
          get_extra_params_obj: get_extra_params_obj
        }, null, 8 /* PROPS */, ["table_id", "csrf"]),
        createBaseVNode("div", _hoisted_8$i, [
          createVNode(script$2u, { note_list: note_list })
        ])
      ]))
    }
    }

    };

    script$R.__file = "http_src/vue/page-probes.vue";

    const _hoisted_1$L = { class: "m-2 mb-3" };
    const _hoisted_2$D = {
      class: "mb-3 d-flex flex-column",
      style: {"height":"30vh"}
    };
    const _hoisted_3$y = { class: "d-flex align-items-center mb-2" };
    const _hoisted_4$x = { class: "d-flex no-wrap" };
    const _hoisted_5$t = { class: "m-1" };
    const _hoisted_6$n = { style: {"min-width":"16rem"} };
    const _hoisted_7$j = { class: "me-1" };


    var script$Q = {
      __name: 'page-exporters',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    let note_snmp_i18n = i18n("flow_devices.note_snmp_device");

    const _i18n = (t) => i18n(t);
    const sankey_url = `${http_prefix}/lua/pro/rest/v2/get/exporters/sankey.lua`;
    const sankey_chart = ref(null);
    const sankey_data = ref({});
    const loading = ref(true);
    const no_data_message = i18n("no_nprobes_message");
    const active_sankey_type = ref({});
    const sankey_format_list = [
        { key: "criteria", value: 'flow_volume_criteria', label: _i18n('exporters_page.flow_volume_criteria') },
        { key: "criteria", value: 'flow_drops_criteria', label: _i18n('exporters_page.flow_drops_criteria') },
    ];
    let note_snmp_device_url = note_snmp_i18n.replace("%{url}", `${http_prefix}/lua/pro/enterprise/snmpdevices_stats.lua`);


    const note_list = [
        note_snmp_device_url,
        _i18n("exporters_page.failed_exports_descr")
    ];

    const first_open = ref(true);
    const table_id = ref('exporters');
    const table_probes = ref(null);
    const csrf = props.context.csrf;

    const chart_url = `${http_prefix}/lua/pro/enterprise/exporters.lua?`;
    const exporter_url = `${http_prefix}/lua/pro/enterprise/exporters.lua?`;
    const exporter_interfaces_url = `${http_prefix}/lua/pro/enterprise/exporter_interfaces.lua?`;

    /* ************************************** */

    onBeforeMount(() => {
        const criteria = ntopng_url_manager.get_url_entry("criteria");
        active_sankey_type.value = sankey_format_list[0];
        if (criteria) {
            sankey_format_list.forEach((element) => {
                if (element.value == criteria) {
                    active_sankey_type.value = element;
                }
            });
        }
    });

    onMounted(() => {
        update_sankey_data();
        setInterval(() => {
            first_open.value = false;
            table_probes.value.refresh_table();
            update_sankey_data();
        }, 60000 /* 60 sec refresh */);
    });

    /* ************************************** */

    const add_sankey_filter = async (opt) => {
        ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
        update_sankey_data();
    };

    const update_sankey_data = async () => {
        loading.value = true;
        let data = await get_sankey_data();
        sankey_data.value = data;
        loading.value = false;
    };

    const get_sankey_data = async () => {
        const url_request = get_sankey_url();
        let graph = await ntopng_utility.http_request(url_request);
        graph.nodes.forEach((node, i) => {
            node.index = i;
        });
        graph.links.forEach((link, i) => {
            if (link.value === 0) {
                link.value = 1;
            }
            let node = graph.nodes.find((el) => el.node_id == link.source_node_id);
            link.source = node.index;
            node = graph.nodes.find((el) => el.node_id == link.target_node_id);
            link.target = node.index;
        });
        return graph
    };

    const get_sankey_url = () => {
        let params = {
            ifid: props.context.ifid,
            ...get_extra_params_obj()
        };
        let url_params = ntopng_url_manager.obj_to_url_params(params);
        let url_request = `${sankey_url}?${url_params}`;
        return url_request;
    };

    function on_node_click(_, node) {
        if (node.link) {
            ntopng_url_manager.go_to_url(node.link);
        }
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "ip": (value, row) => {
                let exporter_ip = value;
                if (!dataUtils.isEmptyOrNull(row['name'])) {
                    exporter_ip = `${row['name']}`;
                }

                let timeseries_link = "";
                // Add timeseries icon if timeseries are enabled
                if (row['timeseries_enabled']) {
                    let timeseriesUrl = `${chart_url}ip=${value}&page=historical&ifid=${row['ifid']}`;
                    timeseries_link = ` <a href="${timeseriesUrl}"><i class="fas fa-chart-area fa-lg"></i></a>`;
                }

                let host_link = "";
                if (row.is_in_memory) {
                    const host_details_url = `${http_prefix}/lua/host_details.lua?`;
                    host_link = ` <a href=${host_details_url}host=${value}><i class="fas fa-laptop"></i></a>`;
                }

                return `<a href="${exporter_interfaces_url}ip=${value}&exporter_uuid=${row.exporter_uuid}&probe_uuid=${row.probe_uuid}&probe_ip=${row.probe_ip}">${exporter_ip}</a>${host_link}${timeseries_link}`;
            },
            "probe_ip": (value, row) => {
                let probe_ip = value;
                if (!dataUtils.isEmptyOrNull(row['probe_name'])) {
                    probe_ip = `${row['probe_name']}`;
                }

                return probe_ip;
            },
            "name": (value, row) => {
                return value
            },
            "ntopng_interface": (value, row) => {
                return value
            },
            "exporter_interfaces": (value, row) => {
                if (!value) {
                    return '';
                } else {
                    return formatterUtils.getFormatter("number")(value)
                }
            },
            "time_last_used": (value, row) => {
                if (!value)
                    return '';
                else
                    return (NtopUtils.secondsToTime((Math.round(new Date().getTime() / 1000)) - value) + " ago");
            },
            "exported_flows": (value, row) => {
                let diff_value = value;
                if (!first_open.value) {
                    const old_value = localStorage.getItem("exporter_exported_flows." + row.exporter_uuid + row.ip);
                    diff_value = (value - Number(old_value)) / 60; // keep in sync with table refresh
                }
                localStorage.setItem("exporter_exported_flows." + row.exporter_uuid + row.ip, value);
                if (!value)
                    return '';
                let formatted_value = formatterUtils.formatAccounting(value);
                if (!first_open.value) {
                    let updated_counter = '';
                    if (diff_value > 0) {
                        updated_counter = '<i class="fas fa-arrow-up"></i>';
                    } else {
                        updated_counter = "<i class='fas fa-minus'></i>";
                    }
                    // change in exported flows since the last refresh
                    formatted_value = `${formatted_value} [ ${formatterUtils.getFormatter("fps_short")(diff_value)} fps] ${updated_counter}`;
                }
                return formatted_value
            },
            "dropped_flows": (value, row) => {
                let diff_value = value;
                if (!first_open.value) {
                    const old_value = localStorage.getItem("exporter_dropped_flows." + row.exporter_uuid + row.ip);
                    diff_value = (value - Number(old_value)) / 10;
                }
                localStorage.setItem("exporter_dropped_flows." + row.exporter_uuid + row.ip, value);
                if (!value)
                    return '';
                let formatted_value = formatterUtils.formatAccounting(Math.abs(value));
                if (!first_open.value) {
                    let updated_counter = '';
                    if (diff_value > 0) {
                        updated_counter = '<i class="fas fa-arrow-up"></i>';
                    } else {
                        updated_counter = "<i class='fas fa-minus'></i>";
                    }
                    formatted_value = `${formatted_value} [ ${formatterUtils.formatAccounting(diff_value)} ] ${updated_counter}`;
                }
                return formatted_value
            },
            "dropped_packets": (value, row) => {
                let diff_value = value;
                if (!first_open.value) {
                    const old_value = localStorage.getItem("exporter_dropped_packets." + row.exporter_uuid + row.ip);
                    diff_value = (value - Number(old_value)) / 10;
                }
                localStorage.setItem("exporter_dropped_packets." + row.exporter_uuid + row.ip, value);
                if (!value)
                    return '';
                let formatted_value = formatterUtils.formatAccounting(Math.abs(value));
                if (!first_open.value) {
                    let updated_counter = '';
                    if (diff_value > 0) {
                        updated_counter = '<i class="fas fa-arrow-up"></i>';
                    } else {
                        updated_counter = "<i class='fas fa-minus'></i>";
                    }
                    formatted_value = `${formatted_value} [ ${formatterUtils.formatAccounting(diff_value)} ] ${updated_counter}`;
                }
                return formatted_value
            },
            "flow_exporters": (value, row) => {
                if (!value) {
                    return '';
                } else {
                    return `<a href="${exporter_url}&ifid=${row.ifid}&ip=${row.probe_ip}"><i class="fas fa-file-export"></i> ${formatterUtils.formatAccounting(value)}</a>`
                }
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });

        return columns;
    };

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "ip") {
                return sortingFunctions$1.sortByIP(r0.ip, r1.ip, col.sort);
            } else if (col.id == "name") {
                return sortingFunctions$1.sortByName(r0.name, r1.name, col.sort);
            } else if (col.id == "ntopng_interface") {
                return sortingFunctions$1.sortByIP(r0.ntopng_interface, r1.ntopng_interface, col.sort);
            } else if (col.id == "probe_ip") {
                return sortingFunctions$1.sortByIP(r0.probe_ip, r1.probe_ip, col.sort);
            } else if (col.id == "export_type") {
                return sortingFunctions$1.sortByName(r0.export_type, r1.export_type, col.sort);
            } else if (col.id == "exporter_interfaces") {
                return sortingFunctions$1.sortByNumber(r0.exporter_interfaces, r1.exporter_interfaces, col.sort);
            } else if (col.id == "time_last_used") {
                return sortingFunctions$1.sortByName(r0.name, r1.name, col.sort);
            } else if (col.id == "exported_flows") {
                return sortingFunctions$1.sortByNumber(r0.exported_flows, r1.exported_flows, col.sort);
            } else if (col.id == "dropped_flows") {
                return sortingFunctions$1.sortByNumber(r0.dropped_flows, r1.dropped_flows, col.sort);
            } else if (col.id == "interface_name") {
                return sortingFunctions$1.sortByName(r0.interface_name, r1.interface_name, col.sort);
            }
        }
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$L, [
        createBaseVNode("div", _hoisted_2$D, [
          createBaseVNode("div", _hoisted_3$y, [
            createBaseVNode("div", _hoisted_4$x, [
              createBaseVNode("div", _hoisted_5$t, [
                createBaseVNode("div", _hoisted_6$n, [
                  createBaseVNode("label", _hoisted_7$j, toDisplayString(_i18n('criteria')) + ": ", 1 /* TEXT */),
                  createVNode(script$2H, {
                    selected_option: active_sankey_type.value,
                    "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((active_sankey_type).value = $event)),
                    options: sankey_format_list,
                    onSelect_option: add_sankey_filter
                  }, null, 8 /* PROPS */, ["selected_option"])
                ])
              ])
            ])
          ]),
          createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
          createVNode(script$1T, {
            ref_key: "sankey_chart",
            ref: sankey_chart,
            no_data_message: unref(no_data_message),
            sankey_data: sankey_data.value,
            onNode_click: on_node_click
          }, null, 8 /* PROPS */, ["no_data_message", "sankey_data"])
        ]),
        createVNode(script$2w, {
          ref_key: "table_probes",
          ref: table_probes,
          table_id: table_id.value,
          csrf: unref(csrf),
          f_map_columns: map_table_def_columns,
          f_sort_rows: columns_sorting,
          get_extra_params_obj: get_extra_params_obj
        }, null, 8 /* PROPS */, ["table_id", "csrf"]),
        createVNode(script$2u, { note_list: note_list })
      ]))
    }
    }

    };

    script$Q.__file = "http_src/vue/page-exporters.vue";

    const _hoisted_1$K = { class: "d-flex align-items-center justify-content-between" };
    const _hoisted_2$C = ["href"];
    const _hoisted_3$x = { class: "fw-normal text-white" };
    const _hoisted_4$w = ["title"];
    const _hoisted_5$s = ["title"];
    const _hoisted_6$m = { class: "flex-shrink-0 ms-3" };


    var script$P = {
      __name: 'dashboard-badge',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        set_component_attr: Function, /* Callback to set component attributes (e.g. Box active color) */
        filters: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const counter = ref('');
    const secondary_counter = ref('');
    const counter_title = ref('');
    const secondary_counter_title = ref('');
    const name = ref('');
    const icon = ref('');
    const link_url = ref('#');
    const label_size = ref('h5');

    const props = __props;

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], (cur_value, old_value) => {
      refresh_component();
    }, { flush: 'pre', deep: true });

    onBeforeMount(() => {
      init();
    });

    onMounted(() => {
    });

    function init() {
      if (props.params.i18n_name) {
        name.value = _i18n(props.params.i18n_name);
      }

      if (props.params.icon) {
        icon.value = props.params.icon + ' fa-2xl';
      }

      if (props.max_width && props.max_width <= 2) {
        label_size.value = 'h6';
      }

      refresh_component();
    }

    async function refresh_component() {
      /* Refresh component */

      if (props.params.url) {

        const url_params = {
          ifid: props.ifid,
          epoch_begin: props.epoch_begin,
          epoch_end: props.epoch_end,
          ...props.params.url_params,
          ...props.filters
        };

        let data = await props.get_component_data(`${http_prefix}${props.params.url}`, url_params, undefined, props.epoch_begin);

        /* TODO handle dot-separated path for non-flat json */
        let counter_value = data[props.params.counter_path];
        if (props.params.i18n_counter_title) {
          counter_title.value = _i18n(props.params.i18n_counter_title);
        }

        let has_secondary_counter = false;
        let secondary_counter_value = '';
        if (props.params.secondary_counter_path) {
          has_secondary_counter = true;
          secondary_counter_value = data[props.params.secondary_counter_path];
          if (props.params.i18n_secondary_counter_title) {
            secondary_counter_title.value = _i18n(props.params.i18n_secondary_counter_title);
          }
        }

        if(props.params.counter_formatter == "no_formatting") {
           counter.value = counter_value;
           if (has_secondary_counter) {
             secondary_counter.value = secondary_counter_value;
           }
        } else {
          let counter_formatter = props.params.counter_formatter;
          if (!counter_formatter) {
            counter_formatter = "number";
          }

          let formatCounter = formatterUtils.getFormatter(counter_formatter);
          counter.value = formatCounter(counter_value);
          if (has_secondary_counter) {
            secondary_counter.value = formatCounter(secondary_counter_value);
          }

          if (counter_value) {
            props.set_component_attr('active', true);
          }

          if (props.params.link) {
            const link_url_params = {
              ifid: props.ifid,
              epoch_begin: props.epoch_begin,
              epoch_end: props.epoch_end,
              ...props.params.link.url_params
            };

            const link_query_params = ntopng_url_manager.obj_to_url_params(link_url_params);
            link_url.value = `${http_prefix}${props.params.link.url}?${link_query_params}`;
          }
        }
      }
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$K, [
        createBaseVNode("div", null, [
          createBaseVNode("a", { href: link_url.value }, [
            createBaseVNode("h4", _hoisted_3$x, [
              createBaseVNode("span", { title: counter_title.value }, toDisplayString(counter.value), 9 /* TEXT, PROPS */, _hoisted_4$w),
              (secondary_counter.value)
                ? (openBlock(), createElementBlock("span", {
                    key: 0,
                    title: secondary_counter_title.value
                  }, " / " + toDisplayString(secondary_counter.value), 9 /* TEXT, PROPS */, _hoisted_5$s))
                : createCommentVNode("v-if", true)
            ]),
            createBaseVNode("p", {
              class: normalizeClass(["subtitle text-white text-sm text mb-0", label_size.value])
            }, toDisplayString(name.value), 3 /* TEXT, CLASS */)
          ], 8 /* PROPS */, _hoisted_2$C)
        ]),
        createBaseVNode("div", _hoisted_6$m, [
          createBaseVNode("i", {
            class: normalizeClass(["text-white", icon.value])
          }, null, 2 /* CLASS */)
        ])
      ]))
    }
    }

    };

    script$P.__file = "http_src/vue/dashboard-badge.vue";

    const _hoisted_1$J = { class: "container-fluid p-3" };
    const _hoisted_2$B = { class: "row" };
    const _hoisted_3$w = { class: "col-md-4 mb-2" };
    const _hoisted_4$v = { class: "col-md-4 mb-2" };
    const _hoisted_5$r = { class: "col-md-4 mb-2" };

    // used for dashboard badges
    const exporters_counter_url = "/lua/pro/rest/v2/get/exporters/exporters_count.lua";

    const interfaces_counter_str = "interfaces_count";
    const exporters_counter_str = "exporters_count";
    const probes_counter_str = "probes_count";

    const interfaces_limit_str = "interfaces_limit";
    const exporters_limit_str = "exporters_limit";


    var script$O = {
      __name: 'page-exporters-interfaces',
      props: {
        context: Object
    },
      setup(__props) {

    const badgeParams = {
      "i18n_name": "",
      "counter_formatter": "no_formatting",
      "component_resp_field": "",
      "counter_path": "",
      "url_params": {},
      "url": ""
    };

    const exporter_notes_url = `${http_prefix}/lua/pro/rest/v2/get/exporters/exporter_notes.lua?`;
`${http_prefix}/lua/pro/enterprise/flowdevice_interface_details.lua?`;
    const snmp_interface_details_url = `${http_prefix}/lua/pro/enterprise/snmp_interface_details.lua?`;
`${http_prefix}/lua/pro/enterprise/flowdevice_interface_details.lua?`;
    const components_info = reactive({});

    const exporterComponent = ref(null);
    const interfaceComponent = ref(null);
    const nprobeComponent = ref(null);

    const probesCounterParams = reactive({ ...badgeParams, componentRef: nprobeComponent, url: exporters_counter_url, current_value: probes_counter_str, i18n_name: create_18n_str(probes_counter_str), counter_path: probes_counter_str });
    const exportersCounterParams = reactive({ ...badgeParams, componentRef: exporterComponent, url: exporters_counter_url, current_value: exporters_counter_str, limit_value: exporters_limit_str, i18n_name: create_18n_str(exporters_counter_str), counter_path: exporters_counter_str });
    const interfacesCounterParams = reactive({ ...badgeParams, componentRef: interfaceComponent, url: exporters_counter_url, current_value: interfaces_counter_str, limit_value: interfaces_limit_str, i18n_name: create_18n_str(interfaces_counter_str), counter_path: interfaces_counter_str });
    const loading = ref(false);

    const props = __props;

    const note_list = ref([]);
    ref(null);
    const table_id = ref(props.context.table_id);
    const table_probes = ref(null);
    const csrf = props.context.csrf;

    function create_18n_str(i18n_name) {
      return "flow_devices." + i18n_name
    }

    // Function to display notes on the footer of the table
    async function get_notes(snmp_port_idx) {
        let url = exporter_notes_url + `ip=${get_ip_from_url()}&snmp_port_idx=${snmp_port_idx}`;
        const rsp = await ntopng_utility.http_request(url);

        note_list.value = rsp.map(el => el.content);
    }

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    function get_ip_from_url() {
        return ntopng_url_manager$1.get_url_entry('ip')
    }

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "ifindex": (value, row) => {
                get_notes(value);
                const snmp_interface_url = `${snmp_interface_details_url}ip=${get_ip_from_url()}&page=config&snmp_port_idx=${value}&ifid=${props.context.ifid}`;
                if (row.snmp_interface_available)
                    return `<a href=${snmp_interface_url}>${value}</i></a>`
                else
                    return value
            },
            "probe_name": (value, row) => {
                if (row.probe_uuid) {
                    const probe_url = `${http_prefix}/lua/pro/enterprise/exporters.lua?probe_uuid=${row.probe_uuid}`;
                    return `<a href="${probe_url}">${value}</a>`
                }
                return value
            },
            "exporter_name": (value, row) => {
                if (row.exporter_uuid && row.probe_uuid && row.exporter_ip && row.probe_ip) {
                    const exporter_url = `${http_prefix}/lua/pro/enterprise/exporter_interfaces.lua?ip=${row.exporter_ip}&exporter_uuid=${row.exporter_uuid}&probe_uuid=${row.probe_uuid}&probe_ip=${row.probe_ip}`;
                    return `<a href="${exporter_url}">${value}</a>`
                }
                return value
            },
            "snmp_ifname": (value, row) => {
                const url = linksUtils.getFlowExporterInterfaceOverviewPageURL(row.exporter_ip, row.ifindex, http_prefix);
                return formatterUtils.formatHTMLaTagNameValue(row.ifindex, value, url, false)
            },
            "in_bytes": (value, row) => {
                if (!value)
                    return '';
                return formatterUtils.getFormatter("bytes")(value);
            },
            "out_bytes": (value, row) => {
                if (!value)
                    return '';
                return formatterUtils.getFormatter("bytes")(value);
            },
            "ratio": (value, row) => {
                if (!value.value || value.value == -1)
                    return '';
                return formatterUtils.getFormatter("ratio")(value.value);
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    jump_to_snmp: props.context.isSNMPAvailable,
                    timeseries: props.context.showTimeseries,
                };
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        // if is not defined is enabled
                        if (!visible_dict[b.id]) {
                            current_class.push("disabled");
                        } else if (!(row.snmp_interface_available) && (b.id === "jump_to_snmp")) {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "ip") {
                return sortingFunctions$1.sortByIP(r0.probe_ip, r1.probe_ip, col.sort);
            } else if (col.id == "name") {
                return sortingFunctions$1.sortByName(r0.probe_public_ip, r1.probe_public_ip, col.sort);
            } else if (col.id == "probe_name") {
                return sortingFunctions$1.sortByName(r0.probe_name, r1.probe_name, col.sort);
            } else if (col.id == "exporter_name") {
                return sortingFunctions$1.sortByName(r0.exporter_name, r1.exporter_name, col.sort);
            } else if (col.id == "snmp_ifname") {
                return sortingFunctions$1.sortByName(r0.snmp_ifname, r1.snmp_ifname, col.sort);
            } else if (col.id == "exported_flows") {
                return sortingFunctions$1.sortByNumber(r0.probe_uuid, r1.probe_uuid, col.sort);
            } else if (col.id == "interface_name") {
                return sortingFunctions$1.sortByName(r0.probe_interface, r1.probe_interface, col.sort);
            } else if (col.id == "in_bytes") {
                return sortingFunctions$1.sortByNumber(r0.in_bytes, r1.in_bytes, col.sort);
            } else if (col.id == "out_bytes") {
                return sortingFunctions$1.sortByNumber(r0.out_bytes, r1.out_bytes, col.sort);
            } else if (col.id == "throughput") {
                return sortingFunctions$1.sortByNumber(r0.throughput, r1.throughput, col.sort);
            } else if (col.id == "ratio") {
                return sortingFunctions$1.sortByNumber(r0.ratio?.value, r1.ratio?.value, col.sort);
            }
        }
    }

    /* ************************************** */

    function click_button_jump_to_snmp(event) {
        const row = event.row;
        const url = linksUtils.getSNMPInterfaceDetailsPageURL(row.exporter_ip, row.ifindex, http_prefix);
        window.location.href = url;
    }

    /* ************************************** */

    function click_button_timeseries(event) {
        const row = event.row;
        const url = linksUtils.getExporterInterfaceDetailsPageURL(row.exporter_ip, row.ifindex, row.ifid, http_prefix);
        window.location.href = url;
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_jump_to_snmp": click_button_jump_to_snmp,
            "click_button_timeseries": click_button_timeseries,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    // used by dashboard badges
    function get_component_data_func(component) {
      const get_component_data = async (url, url_params, post_params) => {
        let info = {};
        if (!components_info[component.url]) {
          components_info[component.url] = {};
        }
        info = components_info[component.url];

        if (info.data) {
          await info.data;
        }

        const data_url = `${component.url}${url_params ? '?' + url_params : ''}`;

        loading.value = true;
        if (post_params) {
          info.data = ntopng_utility.http_post_request(data_url, post_params);
        } else {
          info.data = ntopng_utility.http_request(data_url);
        }

        info.data = info.data.then((response) => {
          loading.value = false;
          if (response.are_limits_exceeded) {
            if (response[component.current_value] === response[component.limit_value]) {
              component.componentRef.classList.add('bg-danger');
              component.componentRef.classList.remove('bg-success');
            }
          } else if (response[component.current_value] === response[component.limit_value]) {
            component.componentRef.classList.add('bg-warning');
            component.componentRef.classList.remove('bg-success');
          }
          const value = `${response[component.current_value]}${component.limit_value ? " / " + response[component.limit_value] : ""}`;
          const resKey = component.counter_path;

          return { [resKey]: value }
        });

        return info.data;
      };
      return get_component_data;
    }

    function set_component_attr_func(component) {
      const set_component_attr = async (attr, value) => {
        component[attr] = value;
      };
      return set_component_attr;
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$J, [
        createBaseVNode("div", _hoisted_2$B, [
          createBaseVNode("div", _hoisted_3$w, [
            createBaseVNode("div", {
              ref_key: "nprobeComponent",
              ref: nprobeComponent,
              class: "bg-success text-white p-3 d-flex justify-content-between align-items-center"
            }, [
              createVNode(script$P, {
                id: "probesCounter",
                params: probesCounterParams,
                ifid: props.context.ifid.toString(),
                get_component_data: get_component_data_func(probesCounterParams),
                set_component_attr: set_component_attr_func(probesCounterParams),
                filters: {}
              }, null, 8 /* PROPS */, ["params", "ifid", "get_component_data", "set_component_attr"])
            ], 512 /* NEED_PATCH */)
          ]),
          createBaseVNode("div", _hoisted_4$v, [
            createBaseVNode("div", {
              ref_key: "exporterComponent",
              ref: exporterComponent,
              class: "bg-success text-white p-3 d-flex justify-content-between align-items-center"
            }, [
              createVNode(script$P, {
                id: "exportersCounter",
                params: exportersCounterParams,
                ifid: props.context.ifid.toString(),
                get_component_data: get_component_data_func(exportersCounterParams),
                set_component_attr: set_component_attr_func(exportersCounterParams),
                filters: {}
              }, null, 8 /* PROPS */, ["params", "ifid", "get_component_data", "set_component_attr"])
            ], 512 /* NEED_PATCH */)
          ]),
          createBaseVNode("div", _hoisted_5$r, [
            createBaseVNode("div", {
              ref_key: "interfaceComponent",
              ref: interfaceComponent,
              class: "bg-success text-white p-3 d-flex justify-content-between align-items-center"
            }, [
              createVNode(script$P, {
                id: "interfacesCounter",
                params: interfacesCounterParams,
                ifid: props.context.ifid.toString(),
                get_component_data: get_component_data_func(interfacesCounterParams),
                set_component_attr: set_component_attr_func(interfacesCounterParams),
                filters: {}
              }, null, 8 /* PROPS */, ["params", "ifid", "get_component_data", "set_component_attr"])
            ], 512 /* NEED_PATCH */)
          ])
        ]),
        createVNode(script$2w, {
          ref_key: "table_probes",
          ref: table_probes,
          table_id: table_id.value,
          csrf: unref(csrf),
          f_map_columns: map_table_def_columns,
          f_sort_rows: columns_sorting,
          get_extra_params_obj: get_extra_params_obj,
          onCustom_event: on_table_custom_event
        }, null, 8 /* PROPS */, ["table_id", "csrf"]),
        createVNode(script$2u, { note_list: note_list.value }, null, 8 /* PROPS */, ["note_list"])
      ]))
    }
    }

    };

    script$O.__file = "http_src/vue/page-exporters-interfaces.vue";

    const _hoisted_1$I = { class: "m-3" };
    const _hoisted_2$A = ["innerHTML"];
    const _hoisted_3$v = { class: "card-body" };
    const _hoisted_4$u = { class: "table table-striped table-bordered col-sm-12" };
    const _hoisted_5$q = { class: "table_length" };
    const _hoisted_6$l = { class: "mb-2" };
    const _hoisted_7$i = { class: "ms-4 me-4" };
    const _hoisted_8$h = ["onUpdate:modelValue", "placeholder", "onInput"];
    const _hoisted_9$g = {
      key: 0,
      class: "text-danger mt-1"
    };
    const _hoisted_10$f = { class: "d-flex justify-content-end me-1" };
    const _hoisted_11$d = ["disabled"];


    var script$N = {
      __name: 'page-network-configuration',
      props: {
        context: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const notes = [
        _i18n("network_configuration.allowed_servers_description"),
        _i18n("network_configuration.uses_of_servers")
    ];

    const loading = ref(true);
    const ipAddresses = reactive({});
    const validationErrors = reactive({});
    const set_config_url = `${http_prefix}/lua/rest/v2/set/network/config.lua`;
    const get_config_url = `${http_prefix}/lua/rest/v2/get/network/config.lua`;
    const modifiedInputs = ref([]);
    const disable_save = ref(true);

    const error_message = ref(_i18n('network_configuration.alert_note'));
    const isSaving = ref(false);
    const saveSuccess = ref(false);

    computed$1(() => {
        if (isSaving.value) return 'Saving...';
        if (saveSuccess.value) return 'Saved!';
        return _i18n("flow_checks.save_configuration");
    });

    computed$1(() => {
        if (saveSuccess.value) return 'btn btn-success';
        return 'btn btn-primary';
    });

    const check_name = {
        "dns_list": { "i18n_title": "network_configuration.dns_servers_title", "device_type": "DNS Server", "reques_param": "dns_list", "i18n_description": "network_configuration.dns_servers_description" },
        "ntp_list": { "i18n_title": "network_configuration.ntp_servers_title", "device_type": "NTP Server", "reques_param": "ntp_list", "i18n_description": "network_configuration.ntp_servers_description" },
        "dhcp_list": { "i18n_title": "network_configuration.dhcp_servers_title", "device_type": "DHCP Server", "reques_param": "dhcp_list", "i18n_description": "network_configuration.dhcp_servers_description" },
        "smtp_list": { "i18n_title": "network_configuration.smtp_servers_title", "device_type": "SMTP Server", "reques_param": "smtp_list", "i18n_description": "network_configuration.smtp_servers_description" },
        "gateway_list": { "i18n_title": "network_configuration.gateway_servers_title", "device_type": "Gateway", "reques_param": "gateway_list", "i18n_description": "network_configuration.gateway_servers_description" },
    };

    Object.keys(check_name).forEach(key => {
        ipAddresses[key] = '';
    });

    onMounted(() => {
        getConfig();
    });

    // Function used to populate text area with data received from the backend at page initialization
    const getConfig = async () => {
        loading.value = true;
        const data = await ntopng_utility$1.http_request(get_config_url);

        data.forEach(item => {
            const key = Object.keys(check_name).find(k => k === item.key);
            if (key) {
                ipAddresses[key] = Array.isArray(item.value_description)
                    ? item.value_description.join(', ')
                    : item.value_description;
            }
        });
        loading.value = false;
    };

    /* ************************************** */

    // Used to mark a text area as modified so that only modified text areas are sent to the backend to be stored in redis
    const markAsModified = (key) => {
        if (!modifiedInputs.value.includes(key)) {
            modifiedInputs.value.push(key);
        }
        disable_save.value = false;
    };

    /* ************************************** */

    // Function to validate IP addresses inserted in text area
    const validateIpAddresses = () => {
        let isValid = true;
        Object.keys(ipAddresses).forEach(key => {
            const ips = ipAddresses[key].split(',').map(ip => ip.trim()).filter(ip => ip !== '');
            if (ips.length === 0) {
                validationErrors[key] = '';
            } else if (!ips.every(regexValidation.validateIP)) {
                validationErrors[key] = 'Invalid IP address format';
                isValid = false;
            } else {
                validationErrors[key] = '';
            }
        });
        return isValid;
    };

    const reloadIp = function () {
        saveConfig();
    };

    // Function used to post data to the backend and save the values in
    const saveConfig = async () => {
        if (validateIpAddresses()) {
            isSaving.value = true;
            let data = { csrf: props.context.csrf };

            for (const server of modifiedInputs.value) {
                const value = ipAddresses[server];
                const key = check_name[server].reques_param;
                data = {
                    [key]: value,
                    ...data
                };
            }

            await ntopng_utility$1.http_post_request(set_config_url, data);
            modifiedInputs.value = [];
            loading.value = true;
            // Show success when saved
            saveSuccess.value = true;
            setTimeout(() => {
                getConfig();
                saveSuccess.value = false;
            }, 1500);
        }
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$I, [
        ((!props.context.is_check_enabled))
          ? (openBlock(), createElementBlock("div", {
              key: 0,
              class: "alert alert-warning",
              role: "alert",
              id: "error-alert",
              innerHTML: error_message.value
            }, null, 8 /* PROPS */, _hoisted_2$A))
          : createCommentVNode("v-if", true),
        createBaseVNode("div", {
          class: normalizeClass(["card card-shadow", [(!props.context.is_check_enabled) ? 'ntopng-gray-out' : '']])
        }, [
          createBaseVNode("div", _hoisted_3$v, [
            createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
            createBaseVNode("table", _hoisted_4$u, [
              createBaseVNode("tbody", _hoisted_5$q, [
                (openBlock(), createElementBlock(Fragment, null, renderList(check_name, (value, key) => {
                  return createBaseVNode("tr", {
                    key: key,
                    class: "mb-4"
                  }, [
                    createBaseVNode("td", null, [
                      createBaseVNode("div", _hoisted_6$l, [
                        createBaseVNode("b", null, toDisplayString(_i18n(value.i18n_title)), 1 /* TEXT */)
                      ]),
                      createBaseVNode("div", _hoisted_7$i, [
                        withDirectives(createBaseVNode("textarea", {
                          "onUpdate:modelValue": $event => ((ipAddresses[key]) = $event),
                          class: "form-control rounded",
                          placeholder: `Enter ${value.device_type} IPs (Comma Separated)`,
                          onInput: $event => (markAsModified(key)),
                          rows: "2"
                        }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_8$h), [
                          [vModelText, ipAddresses[key]]
                        ]),
                        createBaseVNode("small", null, toDisplayString(_i18n(value.i18n_description)), 1 /* TEXT */),
                        (validationErrors[key])
                          ? (openBlock(), createElementBlock("div", _hoisted_9$g, toDisplayString(validationErrors[key]), 1 /* TEXT */))
                          : createCommentVNode("v-if", true)
                      ])
                    ])
                  ])
                }), 64 /* STABLE_FRAGMENT */))
              ])
            ]),
            createBaseVNode("div", _hoisted_10$f, [
              createBaseVNode("button", {
                class: "btn btn-primary",
                disabled: disable_save.value,
                onClick: reloadIp
              }, toDisplayString(_i18n('save_settings')), 9 /* TEXT, PROPS */, _hoisted_11$d)
            ])
          ])
        ], 2 /* CLASS */),
        createVNode(script$2u, { note_list: notes })
      ]))
    }
    }

    };

    script$N.__file = "http_src/vue/page-network-configuration.vue";

    const _hoisted_1$H = { class: "m-3" };
    const _hoisted_2$z = ["innerHTML"];
    const _hoisted_3$u = { class: "card-body" };
    const _hoisted_4$t = { class: "table table-striped table-bordered col-sm-12" };
    const _hoisted_5$p = { class: "table_length" };
    const _hoisted_6$k = { class: "mb-2" };
    const _hoisted_7$h = { class: "ms-4 me-4" };
    const _hoisted_8$g = ["onUpdate:modelValue", "onInput"];
    const _hoisted_9$f = {
      key: 0,
      class: "text-danger mt-1"
    };
    const _hoisted_10$e = { class: "d-flex justify-content-end me-1" };
    const _hoisted_11$c = ["disabled"];


    var script$M = {
      __name: 'page-asn-configuration',
      props: {
        context: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const notes = [
        i18n('asn_configuration.notes')
    ];

    const loading = ref(true);
    const asnList = reactive({});
    const validationErrors = reactive({});
    const set_config_url = `${http_prefix}/lua/rest/v2/set/asn/config.lua`;
    const get_config_url = `${http_prefix}/lua/rest/v2/get/asn/config.lua`;
    const modifiedInputs = ref([]);
    const disable_save = ref(true);

    const error_message = ref(_i18n('asn_configuration.alert_note'));
    const isSaving = ref(false);
    const saveSuccess = ref(false);

    const check_name = {
        "customer_asn": { "i18n_title": "asn_configuration.customer_asn_title", "request_param": "customer_asn", "i18n_description": "asn_configuration.customer_asn_description" },
        "sub_customer_asn": { "i18n_title": "asn_configuration.sub_customer_asn_title", "request_param": "sub_customer_asn", "i18n_description": "asn_configuration.sub_customer_asn_description" },
        "remote_asn": { "i18n_title": "asn_configuration.remote_asn_title", "request_param": "remote_asn", "i18n_description": "asn_configuration.remote_asn_description" },
    };

    Object.keys(check_name).forEach(key => {
        asnList[key] = '';
    });

    onMounted(() => {
        getConfig();
    });

    // Function used to populate text area with data received from the backend at page initialization
    const getConfig = async () => {
        loading.value = true;
        const data = await ntopng_utility$1.http_request(get_config_url);

        data.forEach(item => {
            const key = Object.keys(check_name).find(k => k === item.key);
            if (key) {
                asnList[key] = Array.isArray(item.value_description)
                    ? item.value_description.join(', ')
                    : item.value_description;
            }
        });
        loading.value = false;
    };

    /* ************************************** */

    // Used to mark a text area as modified so that only modified text areas are sent to the backend to be stored in redis
    const markAsModified = (key) => {
        if (!modifiedInputs.value.includes(key)) {
            modifiedInputs.value.push(key);
        }
        disable_save.value = false;
    };

    /* ************************************** */

    // Function to validate ASN inserted in text area
    const validateASN = () => {
        let isValid = true;
        Object.keys(asnList).forEach(key => {
            const all_asn = asnList[key].split(',').map(asn => asn.trim()).filter(asn => asn !== '');
            if (all_asn.length === 0) {
                validationErrors[key] = '';
            } else if (!all_asn.every(regexValidation.validateUInt32)) {
                validationErrors[key] = 'Invalid ASN format';
                isValid = false;
            } else {
                validationErrors[key] = '';
            }
        });
        return isValid;
    };

    const reloadASN = function () {
        saveConfig();
    };

    // Function used to post data to the backend and save the values in
    const saveConfig = async () => {
        if (validateASN()) {
            isSaving.value = true;
            let data = { csrf: props.context.csrf };

            for (const asn of modifiedInputs.value) {
                const value = asnList[asn];
                const key = check_name[asn].request_param;
                const cleaned_value = Array.from(new Set(
                    value.split(',').map(s => s.trim())
                )).join(',').replace(/\s*,\s*/g, ',');
                data = {
                    [key]: cleaned_value,
                    ...data
                };
            }

            await ntopng_utility$1.http_post_request(set_config_url, data);
            modifiedInputs.value = [];
            loading.value = true;
            // Show success when saved
            saveSuccess.value = true;
            setTimeout(() => {
                getConfig();
                saveSuccess.value = false;
            }, 1500);
        }
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$H, [
        ((!props.context.is_check_enabled))
          ? (openBlock(), createElementBlock("div", {
              key: 0,
              class: "alert alert-warning",
              role: "alert",
              id: "error-alert",
              innerHTML: error_message.value
            }, null, 8 /* PROPS */, _hoisted_2$z))
          : createCommentVNode("v-if", true),
        createBaseVNode("div", {
          class: normalizeClass(["card card-shadow", [(!props.context.is_check_enabled) ? 'ntopng-gray-out' : '']])
        }, [
          createBaseVNode("div", _hoisted_3$u, [
            createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
            createBaseVNode("table", _hoisted_4$t, [
              createBaseVNode("tbody", _hoisted_5$p, [
                (openBlock(), createElementBlock(Fragment, null, renderList(check_name, (value, key) => {
                  return createBaseVNode("tr", {
                    key: key,
                    class: "mb-4"
                  }, [
                    createBaseVNode("td", null, [
                      createBaseVNode("div", _hoisted_6$k, [
                        createBaseVNode("b", null, toDisplayString(_i18n(value.i18n_title)), 1 /* TEXT */)
                      ]),
                      createBaseVNode("div", _hoisted_7$h, [
                        withDirectives(createBaseVNode("textarea", {
                          "onUpdate:modelValue": $event => ((asnList[key]) = $event),
                          class: "form-control rounded",
                          placeholder: `Enter a comma separated list of ASNs`,
                          onInput: $event => (markAsModified(key)),
                          rows: "2"
                        }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_8$g), [
                          [vModelText, asnList[key]]
                        ]),
                        createBaseVNode("small", null, toDisplayString(_i18n(value.i18n_description)), 1 /* TEXT */),
                        (validationErrors[key])
                          ? (openBlock(), createElementBlock("div", _hoisted_9$f, toDisplayString(validationErrors[key]), 1 /* TEXT */))
                          : createCommentVNode("v-if", true)
                      ])
                    ])
                  ])
                }), 64 /* STABLE_FRAGMENT */))
              ])
            ]),
            createBaseVNode("div", _hoisted_10$e, [
              createBaseVNode("button", {
                class: "btn btn-primary",
                disabled: disable_save.value,
                onClick: reloadASN
              }, toDisplayString(_i18n('save_settings')), 9 /* TEXT, PROPS */, _hoisted_11$c)
            ])
          ])
        ], 2 /* CLASS */),
        createVNode(script$2u, { note_list: notes })
      ]))
    }
    }

    };

    script$M.__file = "http_src/vue/page-asn-configuration.vue";

    const _hoisted_1$G = { class: "custom-file" };
    const _hoisted_2$y = {
      class: "form-label",
      for: "import-input"
    };
    const _hoisted_3$t = { class: "w-100" };
    const _hoisted_4$s = {
      key: 0,
      class: "me-auto text-danger d-inline"
    };
    const _hoisted_5$o = ["disabled"];

    const max_size = 131072;

    var script$L = {
      __name: 'modal-import-network-policies',
      props: {
      ifid_list: Array,
      frequency_list: Array,
      init_func: Function,
      page_csrf: String,
    },
      emits: ['add', 'edit'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    const activate_import_spinner = ref(false);
    const is_data_not_ok = ref(false);
    const is_not_empty_file = ref(false);
    let title = _i18n('network_configuration.import_policies');
    let message = "";
    const json_file = ref(null);
    const import_input = ref(null);

    const note_list = [
      i18n("network_configuration.import_network_policies_notes")
    ];

    const showed = () => { };

    /**
     * 
     * Reset fields in modal form 
     */
    const reset_modal_form = function () {
      json_file.value = null;
      activate_import_spinner.value = false;
      is_data_not_ok.value = false;
      is_not_empty_file.value = false;
      import_input.value.value = null;
    };

    const show = (row) => {
      reset_modal_form();
      modal_id.value.show();
    };

    const handleFileUpload = (event) => {
      json_file.value = event.target.files[0];
      const size = json_file.value.size;
      is_not_empty_file.value = json_file.value != null && size < max_size; //128KB
      if (size > max_size) {
        is_data_not_ok.value = true;
        message = _i18n("file_to_large");
      } else {
        is_data_not_ok.value = false;
        message = "";
      }
    };

    /**
     * Function to add rule to rules list
     */
    const _import = (is_edit) => {
      activate_import_spinner.value = true;
      is_data_not_ok.value = false;
      const fileReader = new FileReader();
      fileReader.readAsText(json_file.value, "UTF-8");

      fileReader.onload = () => {
        // Set file content to data property
        const is_json = fileReader.result.contains('\"');

        if (is_json) {
          emit('add', {
            devices: fileReader.result
          });
        } else {
          emit('add', {
            devices_csv: fileReader.result
          });
        }


      };
    };

    const show_bad_feedback = (bad_message) => {
      message = bad_message;
      is_data_not_ok.value = true;
      activate_import_spinner.value = false;
    };

    const close = () => {
      activate_import_spinner.value = false;
      is_data_not_ok.value = false;
      modal_id.value.close();
    };

    __expose({ show, close, show_bad_feedback });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[0] || (_cache[0] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Target information, here an IP is put "),
          createBaseVNode("div", _hoisted_1$G, [
            createBaseVNode("label", _hoisted_2$y, toDisplayString(_i18n("network_configuration.browse_network_policies")), 1 /* TEXT */),
            createBaseVNode("input", {
              required: "",
              class: "custom-file-input form-control",
              ref_key: "import_input",
              ref: import_input,
              id: "import-input",
              name: "JSON",
              type: "file",
              onChange: handleFileUpload,
              accept: ".json"
            }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", _hoisted_3$t, [
            createVNode(script$2u, { note_list: note_list })
          ]),
          (is_data_not_ok.value)
            ? (openBlock(), createElementBlock("div", _hoisted_4$s, toDisplayString(unref(message)), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createVNode(script$2v, {
              show: activate_import_spinner.value,
              size: "1rem",
              class: "me-2"
            }, null, 8 /* PROPS */, ["show"]),
            createBaseVNode("button", {
              type: "button",
              disabled: !is_not_empty_file.value,
              onClick: _import,
              class: "btn btn-primary"
            }, toDisplayString(_i18n('import')), 9 /* TEXT, PROPS */, _hoisted_5$o)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$L.__file = "http_src/vue/modal-import-network-policies.vue";

    const _hoisted_1$F = { class: "m-3" };
    const _hoisted_2$x = ["innerHTML"];
    const _hoisted_3$s = { class: "card-body" };
    const _hoisted_4$r = { class: "table table-striped table-bordered col-sm-12" };
    const _hoisted_5$n = { class: "table_length" };
    const _hoisted_6$j = { class: "mb-2" };
    const _hoisted_7$g = { class: "ms-4 me-4" };
    const _hoisted_8$f = ["onUpdate:modelValue", "placeholder", "onInput"];
    const _hoisted_9$e = {
      key: 0,
      class: "text-danger mt-1"
    };
    const _hoisted_10$d = {
      key: 0,
      class: "text-danger"
    };
    const _hoisted_11$b = ["innerHTML"];
    const _hoisted_12$a = { class: "d-flex justify-content-between" };
    const _hoisted_13$9 = { clas: "align-items-center ms-auto" };
    const _hoisted_14$9 = ["title"];
    const _hoisted_15$9 = ["title"];
    const _hoisted_16$9 = { clas: "align-items-center ms-auto" };
    const _hoisted_17$9 = ["disabled"];


    var script$K = {
      __name: 'page-network-policy',
      props: {
        context: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const notes = [
        _i18n("network_configuration.allowed_servers_description"),
        _i18n("network_configuration.uses_of_servers")
    ];

    const networks = reactive({});
    const validationErrors = reactive({});
    const set_config_url = `${http_prefix}/lua/pro/rest/v2/set/network/policy.lua`;
    const get_config_url = `${http_prefix}/lua/pro/rest/v2/get/network/policy.lua`;
    const import_network_policies_url = `${http_prefix}/lua/pro/rest/v2/add/network/policy.lua`;
    const export_network_policies_url = `${http_prefix}/lua/pro/rest/v2/export/network/policy.lua`;
    const modifiedInputs = ref([]);
    const modal_import_network_policies = ref();

    const error_message = ref(_i18n('network_configuration.policy_note'));
    const configuration_error = ref('');
    const show_configuration_error = ref(false);
    const isSaving = ref(false);
    const saveSuccess = ref(false);
    const disable_save = ref(true);
    const show_border_error = ref(false);
    const isLoading = ref(true);

    computed$1(() => {
        if (isSaving.value) return 'Saving...';
        if (saveSuccess.value) return 'Saved!';
        return _i18n("flow_checks.save_configuration");
    });

    computed$1(() => {
        if (saveSuccess.value) return 'btn btn-success';
        return 'btn btn-primary';
    });

    const reloadNetworks = function () {
        /* Save the configuration and reload the configured confs */
        saveConfig();
    };

    const check_name = {
        "local_devices": { "i18n_title": "network_configuration.local_devices_title", "device_type": "Local Devices", "reques_param": "local_devices", "i18n_description": "network_configuration.local_devices_description" },
        "corporate_devices": { "i18n_title": "network_configuration.corporate_devices_title", "device_type": "Corporate Devices", "reques_param": "corporate_devices", "i18n_description": "network_configuration.corporate_devices_description" },
        "whitelisted_networks": { "i18n_title": "network_configuration.whitelisted_networks_title", "device_type": "Whitelisted", "reques_param": "whitelisted_networks", "i18n_description": "network_configuration.whitelisted_networks_description" },
    };

    Object.keys(check_name).forEach(key => {
        networks[key] = '';
    });

    onMounted(() => {
        getConfig();
    });

    /* ************************************** */

    function import_policies() {
        modal_import_network_policies.value.show();
    }

    const import_network_policies_rest = async function (params) {
        const url = import_network_policies_url;
        const result = await ntopng_utility$1.http_post_request(url, { ...{ csrf: props.context.csrf }, ...params }, false, true);

        if (result == null) {
            modal_import_network_policies.value.show_bad_feedback(_i18n("import_network_policies_error"));
        } else if (result.rc < 0) {
            modal_import_network_policies.value.show_bad_feedback(result.rsp.feedback);
            table_network_policies.value.refresh_table();
        } else {
            setTimeout(() => { modal_import_network_policies.value.close(); }, 1000);

            getConfig();
        }
    };

    /* ************************************** */

    // Function used to populate text area with data received from the backend at page initialization
    const getConfig = async () => {
        isLoading.value = true;
        const data = await ntopng_utility$1.http_request(get_config_url);

        data.forEach(item => {
            const key = Object.keys(check_name).find(k => k === item.key);
            if (key) {
                networks[key] = Array.isArray(item.value_description)
                    ? item.value_description.join(', ')
                    : item.value_description;
            }
        });
        isLoading.value = false;
    };

    // Used to mark a text area as modified so that only modified text areas are sent to the backend to be stored in redis
    const markAsModified = (key) => {
        if (!modifiedInputs.value.includes(key)) {
            modifiedInputs.value.push(key);
        }
        validationErrors[key] = '';
        disable_save.value = false;
    };

    // Function to validate Network addresses inserted in text area
    const validateNetworkAddresses = () => {
        let isValid = true;
        Object.keys(networks).forEach((key) => {
            const fixed_networks = [];
            const network_list = networks[key].split(',').map(net => net.trim()).filter(net => net !== '');
            network_list.forEach((net) => {
                if (regexValidation.validateCIDR(net)) {
                    fixed_networks.push(net);
                    return;
                } else if (regexValidation.validateIPv4(net)) {
                    fixed_networks.push(net + "/32");
                    return;
                } else if (regexValidation.validateIPv6(net)) {
                    fixed_networks.push(net + "/128");
                    return;
                } else if (key === "whitelisted_networks") {
                    if (regexValidation.validateMAC(net)) {
                        fixed_networks.push(net);
                        return;
                    }
                }
                validationErrors[key] = 'Invalid Network format';
                isValid = false;
            });
            if (isValid) {
                networks[key] = fixed_networks.join(",");
            }
        });
        return isValid;
    };

    // Function used to post data to the backend and save the values in
    const saveConfig = async () => {
        if (validateNetworkAddresses()) {
            isSaving.value = true;
            let data = { csrf: props.context.csrf };

            for (const server of modifiedInputs.value) {
                const value = networks[server];
                const key = check_name[server].reques_param;
                data = {
                    [key]: value,
                    ...data
                };
            }

            const res = await ntopng_utility$1.http_post_request(set_config_url, data);
            modifiedInputs.value = [];
            if (!res.error) {
                // Show success when saved
                saveSuccess.value = true;
                configuration_error.value = '';
                show_configuration_error.value = false;
                disable_save.value = true;
                show_border_error.value = false;
                setTimeout(() => {
                    saveSuccess.value = false;
                    getConfig();
                }, 500);
            } else {
                configuration_error.value = 'Error: ' + res.error_msg;
                show_configuration_error.value = true;
                show_border_error.value = true;
            }
            return true;
        }

        return false;
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$F, [
          ((!props.context.is_check_enabled))
            ? (openBlock(), createElementBlock("div", {
                key: 0,
                class: "alert alert-warning",
                role: "alert",
                id: "error-alert",
                innerHTML: error_message.value
              }, null, 8 /* PROPS */, _hoisted_2$x))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", {
            class: normalizeClass(["card card-shadow", [(!props.context.is_check_enabled) ? 'ntopng-gray-out' : '']])
          }, [
            createVNode(script$2y, { isLoading: isLoading.value }, null, 8 /* PROPS */, ["isLoading"]),
            createBaseVNode("div", _hoisted_3$s, [
              createBaseVNode("table", _hoisted_4$r, [
                createBaseVNode("tbody", _hoisted_5$n, [
                  (openBlock(), createElementBlock(Fragment, null, renderList(check_name, (value, key) => {
                    return createBaseVNode("tr", {
                      key: key,
                      class: "mb-4"
                    }, [
                      createBaseVNode("td", null, [
                        createBaseVNode("div", _hoisted_6$j, [
                          createBaseVNode("b", null, toDisplayString(_i18n(value.i18n_title)), 1 /* TEXT */)
                        ]),
                        createBaseVNode("div", _hoisted_7$g, [
                          withDirectives(createBaseVNode("textarea", {
                            "onUpdate:modelValue": $event => ((networks[key]) = $event),
                            class: normalizeClass(["form-control rounded", (show_border_error.value) ? 'border-danger' : '']),
                            placeholder: `Enter ${value.device_type} Networks (Comma Separated)`,
                            onInput: $event => (markAsModified(key)),
                            rows: "2"
                          }, null, 42 /* CLASS, PROPS, NEED_HYDRATION */, _hoisted_8$f), [
                            [vModelText, networks[key]]
                          ]),
                          createBaseVNode("small", null, toDisplayString(_i18n(value.i18n_description)), 1 /* TEXT */),
                          (validationErrors[key])
                            ? (openBlock(), createElementBlock("div", _hoisted_9$e, toDisplayString(validationErrors[key]), 1 /* TEXT */))
                            : createCommentVNode("v-if", true)
                        ])
                      ])
                    ])
                  }), 64 /* STABLE_FRAGMENT */))
                ])
              ]),
              (show_configuration_error.value)
                ? (openBlock(), createElementBlock("div", _hoisted_10$d, [
                    createBaseVNode("span", { innerHTML: configuration_error.value }, null, 8 /* PROPS */, _hoisted_11$b)
                  ]))
                : createCommentVNode("v-if", true),
              createBaseVNode("div", _hoisted_12$a, [
                createBaseVNode("div", _hoisted_13$9, [
                  createBaseVNode("button", {
                    class: "btn btn-primary me-2",
                    type: "button",
                    onClick: import_policies
                  }, [
                    createBaseVNode("i", {
                      class: "fa-solid fa-file-arrow-down",
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      title: _i18n('network_configuration.import_policies')
                    }, null, 8 /* PROPS */, _hoisted_14$9),
                    createTextVNode(" " + toDisplayString(_i18n('network_configuration.import_policies')), 1 /* TEXT */)
                  ]),
                  createBaseVNode("a", {
                    class: "btn btn-primary",
                    download: "network_policies.json",
                    href: export_network_policies_url
                  }, [
                    createBaseVNode("i", {
                      class: "fa-solid fa-file-arrow-up",
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      title: _i18n('network_configuration.export_policies')
                    }, null, 8 /* PROPS */, _hoisted_15$9),
                    createTextVNode(" " + toDisplayString(_i18n('network_configuration.export_policies')), 1 /* TEXT */)
                  ])
                ]),
                createBaseVNode("div", _hoisted_16$9, [
                  createBaseVNode("button", {
                    class: "btn btn-primary",
                    disabled: disable_save.value,
                    onClick: reloadNetworks
                  }, toDisplayString(_i18n('save_settings')), 9 /* TEXT, PROPS */, _hoisted_17$9)
                ])
              ])
            ])
          ], 2 /* CLASS */),
          createVNode(script$2u, { note_list: notes })
        ]),
        createVNode(script$L, {
          ref_key: "modal_import_network_policies",
          ref: modal_import_network_policies,
          context: __props.context,
          onAdd: import_network_policies_rest
        }, null, 8 /* PROPS */, ["context"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$K.__file = "http_src/vue/page-network-policy.vue";

    const _hoisted_1$E = { class: "card h-100 overflow-hidden" };
    const _hoisted_2$w = { class: "m-2 mb-3" };


    var script$J = {
      __name: 'page-limits',
      setup(__props) {

    const table_id = ref('limits_table');

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "limit": (value, row) => {
                return (i18n("limits_page." + value) || value);
            },
            "current": (value, row) => {
                if (!row.max || value < row.max) {
                    return `<span class="text-success">${formatterUtils.getFormatter("full_number")(value)}</span>`;
                } else {
                    return `<span class="text-danger">! ${formatterUtils.getFormatter("full_number")(value)}</span>`;
                }
            },
            "max": (value, row) => {
                if (!value) {
                    return ``;
                } else if (row.current < value) {
                    return `<span class="text-success">${formatterUtils.getFormatter("full_number")(value)}</span>`;
                } else {
                    return `<span class="text-danger">! ${formatterUtils.getFormatter("full_number")(value)}</span>`;
                }
            },
        };
        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });

        return columns;
    };

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            const r0_col = r0[col.data.data_field];
            const r1_col = r1[col.data.data_field];

            /* In case the values are the same, sort by Name */
            if (r0_col == r1_col) {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col ? col.sort : null);
            } else if (col.id == "limit") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col ? col.sort : null);
            } else if (col.id == "current") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "max") {
                const lower_value = -1;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            }
        }

        return sortingFunctions$1.sortByName(r0.device, r1.device, col ? col.sort : null);
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$E, [
        createBaseVNode("div", _hoisted_2$w, [
          createVNode(script$2w, {
            table_id: table_id.value,
            f_map_columns: map_table_def_columns,
            get_extra_params_obj: get_extra_params_obj,
            f_sort_rows: columns_sorting
          }, null, 8 /* PROPS */, ["table_id"])
        ])
      ]))
    }
    }

    };

    script$J.__file = "http_src/vue/page-limits.vue";

    const _hoisted_1$D = { class: "card h-100 overflow-hidden" };
    const _hoisted_2$v = { class: "m-2 mb-3" };
    const _hoisted_3$r = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_4$q = { class: "no-wrap d-flex align-items-center filters-label" };

    /* ************************************** */


    var script$I = {
      __name: 'page-local-hosts-report',
      props: {
      context: Object,
    },
      setup(__props) {

    const props = __props;

    /* ************************************** */

    const table_id = ref('local_hosts_report');
    const date_time_picker = ref(null);
    const table_local_hosts_report = ref(null);
    let id_date_time_picker = "date_time_picker";
    const note_list = ref([i18n('local_hosts_report_description')]);
    const date_format = ref(null);

    /* ************************************** */

    const time_preset_list = [
      { value: "hour", label: i18n('show_alerts.presets.hour'), currently_active: false },
      { value: "day", label: i18n('show_alerts.presets.day'), currently_active: true },
      { value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
      { value: "month", label: i18n('show_alerts.presets.month'), currently_active: false },
      { value: "year", label: i18n('show_alerts.presets.year'), currently_active: false },
      { value: "custom", label: i18n('show_alerts.presets.custom'), currently_active: false, disabled: true, },
    ];

    const filters = ref([{
      id: "host_visibility",
      label: i18n('hosts'),
      title: i18n('hosts'),
      basic_label: i18n('hosts_to_visualize'),
      options: [
        { key: "host_visibility", value: "only_traffic", label: i18n('only_traffic'), currently_active: true },
        { key: "host_visibility", value: "no_traffic", label: i18n('no_traffic'), currently_active: false },
        { key: "host_visibility", value: "visible", label: i18n('visible'), currently_active: false },
        { key: "host_visibility", value: "hidden", label: i18n('hidden'), currently_active: false },
      ],
    }]);

    const gradient_color = [
      "#d3d3d3",
      "#39d353",
      "#26a641",
      "#006d32",
    ];

    /* ************************************** */

    async function click_button_hide_host(event) {
      const row = event.row;
      const params = {
        host: row.host,
        hide: true
      };
      const url_params = ntopng_url_manager.obj_to_url_params(params);
      const url = `${http_prefix}/lua/pro/rest/v2/get/host/hide_host.lua?${url_params}`;

      await ntopng_utility.http_request(url);
      epoch_change(); // reload
    }

    /* ************************************** */

    async function click_button_show_host(event) {
      const row = event.row;
      const params = {
        host: row.ip,
        hide: false
      };
      const url_params = ntopng_url_manager.obj_to_url_params(params);
      const url = `${http_prefix}/lua/pro/rest/v2/get/host/hide_host.lua?${url_params}`;

      await ntopng_utility.http_request(url);
      epoch_change(); // reload
    }

    /* ************************************** */

    function on_table_custom_event(event) {
      let events_managed = {
        "click_button_hide_host": click_button_hide_host,
        "click_button_show_host": click_button_show_host
      };
      if (events_managed[event.event_id] == null) {
        return;
      }
      events_managed[event.event_id](event);
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager.get_url_object();
      return extra_params;
    };

    /* ************************************** */

    async function epoch_change(status) {
      if (status && status.timeframe_id) {
        ntopng_url_manager.set_key_to_url("preset", status.timeframe_id);
      }
      if (table_local_hosts_report.value) {
        table_local_hosts_report.value.refresh_table(false);
      }
    }

    /* ************************************** */

    function calculate_gradient(value, max_value) {
      if (!value || value == 0) {
        return gradient_color[0]
      } else if (value > (max_value / 3 * 2)) {
        return gradient_color[3]
      } else if (value > (max_value / 3 * 2)) {
        return gradient_color[2]
      } else {
        return gradient_color[1]
      }
    }

    /* ************************************** */

    const map_table_def_columns = (columns) => {
      const formatter = formatterUtils.getFormatter("bytes");
      let map_columns = {
        "ip": (value, row) => {
          if (row.vlan && row.vlan != 0) {
            value = value + "@" + row.vlan;
          }
          const url = `${http_prefix}/lua/host_details.lua?host=${value}`;
          if (row.online) {
            return `<a href=${url}>${value}</a>`
          }
          return value;
        },
        "name": (value, row) => {
          return value;
        },
        "availability": (value, row) => {
          const epoch_begin = Number(ntopng_url_manager.get_url_entry("epoch_begin"));
          const epoch_end = Number(ntopng_url_manager.get_url_entry("epoch_end"));
          const squareLength = 7, squareHeight = 20;
          let svg = `<svg width='${squareLength * value.length + value.length * 2}' height='20' viewBox='0 0 ${squareLength * value.length + value.length * 2} 20'>`;
          const epoch_diff = (epoch_end - epoch_begin) / value.length;
          value.forEach((el, index) => {
            const current_time = epoch_begin + (epoch_diff * index);
            const rect = $(document.createElementNS("http://www.w3.org/2000/svg", "rect"));
            rect.attr('x', index * (squareLength + 2)).attr('y', 0).attr('width', squareLength).attr('height', squareHeight);
            rect.attr('fill', calculate_gradient(el, row.max_value_per_point[index]));
            rect.attr('data-bs-toggle', "tooltip");
            rect.attr('data-bs-placement', "top");
            rect.attr('data-bs-original-title', ntopng_utility.from_utc_to_server_date_format(new Date(current_time * 1000), date_format.value) + ` \n Activity: ${formatter(el)}`);
            svg = `${svg}${rect[0].outerHTML}`;
          });
          svg = `${svg}</svg>`;
          return svg;
        },
        "bytes_sent": (value, row) => {
          return formatter(value);
        },
        "bytes_rcvd": (value, row) => {
          return formatter(value);
        },
        "total_traffic": (value, row) => {
          return formatter(value);
        },
      };

      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];
        if (c.id == "actions") {
          c.button_def_array.forEach((b, i) => {
            b.f_map_class = (current_class, row) => {
              const visibility = ntopng_url_manager.get_url_entry("host_visibility");
              if (visibility == "visible") {
                if (b.id != "hide_host") {
                  current_class.push("disabled");
                }
              } else if (visibility == "hidden") {
                if (b.id != "show_host") {
                  current_class.push("disabled");
                }
              } else {
                /* By default only show Hide Host */
                if (b.id != "hide_host") {
                  current_class.push("disabled");
                }
              }
              return current_class;
            };
          });
        }
      });

      return columns;
    };

    /* ************************************** */

    function add_table_filter(opt) {
      ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
      table_local_hosts_report.value.refresh_table();
    }

    /* ************************************** */

    onBeforeMount(async () => {
      const visibility = ntopng_url_manager.get_url_entry("host_visibility");
      const epoch_begin = ntopng_url_manager.get_url_entry("epoch_begin");
      const epoch_end = ntopng_url_manager.get_url_entry("epoch_end");
      if (!epoch_end || !epoch_begin) {
        const now = ntopng_utility.get_utc_seconds(Date.now());
        const day_ago = now - 86400;
        ntopng_url_manager.set_key_to_url("epoch_begin", `${day_ago}`);
        ntopng_url_manager.set_key_to_url("epoch_end", `${now}`);
        ntopng_url_manager.set_key_to_url("preset", 'day');
      }
      filters.value.forEach((item) => {
        let selected_option;
        item.options.forEach((item2) => {
          if (visibility == item2.value) {
            item2.currently_active = true;
            selected_option = item2;
          } else {
            item2.currently_active = false;
          }
        });
        if (!selected_option) {
          item.options[0].currently_active = true;
          selected_option = item.options[0];
        }
        item.current_option = selected_option;
      });
      date_format.value = await ntopng_utility.get_date_format(false, props.context.csrf, http_prefix);
    });

    /* ************************************** */

    onMounted(async () => {
      await Promise.all([
        ntopng_sync.on_ready(id_date_time_picker),
      ]);
    });

    /* ************************************** */


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createVNode(script$2G, {
          class: "mt-1 mb-2",
          id: unref(id_date_time_picker),
          enable_refresh: false,
          ref_key: "date_time_picker",
          ref: date_time_picker,
          onEpoch_change: epoch_change,
          custom_time_interval_list: time_preset_list,
          min_time_interval_id: "5_min",
          round_time: true
        }, null, 8 /* PROPS */, ["id"]),
        createBaseVNode("div", _hoisted_1$D, [
          createBaseVNode("div", _hoisted_2$v, [
            createVNode(script$2w, {
              ref_key: "table_local_hosts_report",
              ref: table_local_hosts_report,
              table_id: table_id.value,
              f_map_columns: map_table_def_columns,
              get_extra_params_obj: get_extra_params_obj,
              onCustom_event: on_table_custom_event
            }, {
              custom_header: withCtx(() => [
                (openBlock(true), createElementBlock(Fragment, null, renderList(filters.value, (item) => {
                  return (openBlock(), createElementBlock("div", _hoisted_3$r, [
                    createBaseVNode("span", _hoisted_4$q, [
                      createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                    ]),
                    createVNode(script$2H, {
                      selected_option: item['current_option'],
                      "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                      theme: "bootstrap-5",
                      dropdown_size: "small",
                      options: item['options'],
                      onSelect_option: add_table_filter
                    }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options"])
                  ]))
                }), 256 /* UNKEYED_FRAGMENT */))
              ]),
              _: 1 /* STABLE */
            }, 8 /* PROPS */, ["table_id"])
          ]),
          createVNode(script$2u, { note_list: note_list.value }, null, 8 /* PROPS */, ["note_list"])
        ])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$I.__file = "http_src/vue/page-local-hosts-report.vue";

    const _hoisted_1$C = {
      key: 0,
      class: "text-left"
    };


    var script$H = {
      __name: 'modal-delete-assets',
      props: {
      context: Object,
    },
      emits: ["delete"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);
    const message = ref('');
    const return_message = ref('');
    const show_return_msg = ref(false);
    const title = ref('');
    const err = ref(false);
    const row = ref(null);

    const emit = __emit;

    const props = __props;

    onMounted(() => { });

    /* ****************************************** */

    /* This function simply reset the modal to factory values */
    async function resetModal() {
      row.value = null;
      message.value = '';
      return_message.value = '';
      show_return_msg.value = false;
      err.value = false;
      title.value = '';
    }

    /* ****************************************** */

    /* This function formats the delete message */
    async function formatMessage(_row) {
      title.value = i18n("asset_details.delete_asset_title");
      if (_row) {
        const host = _row.host;
        let ip_address = host.ip;
        if (!dataUtils.isEmptyOrNull(host.vlan.name)) {
          ip_address = `${ip_address}@${host.vlan.name}`;
        }
        title.value = title.value + ": " + ip_address;
        message.value = i18n('asset_details.delete_asset');
      } else {
        title.value = title.value + ": " + i18n('all');
        message.value = i18n('asset_details.delete_all_assets');
      }
    }

    /* ****************************************** */

    /* This function runs the delete code, called when apply is clicked */
    async function delete_asset() {
      const url = `${http_prefix}/lua/pro/rest/v2/delete/host/asset.lua`;
      let serial_key = 'all';
      if (row.value && row.value.key) {
        serial_key = row.value.key;
      }
      let params = {
        csrf: props.context.csrf,
        ifid: props.context.ifid,
        serial_key: serial_key,
      };

      const res = await ntopng_utility$1.http_post_request(url, params);
      if (res) {
        close();
      }
    }

    const show = (_row) => {
      resetModal();
      row.value = _row;
      formatMessage(_row);
      modal_id.value.show();
    };

    const close = () => {
      setTimeout(() => {
        modal_id.value.close();
        emit("delete");
      }, 500 /* 0.5 seconds */);
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(message.value) + " ", 1 /* TEXT */),
          (show_return_msg.value)
            ? (openBlock(), createElementBlock("div", _hoisted_1$C))
            : createCommentVNode("v-if", true)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: delete_asset,
            class: "btn btn-danger"
          }, toDisplayString(_i18n("delete")), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$H.__file = "http_src/vue/modal-delete-assets.vue";

    const _hoisted_1$B = { class: "btn-group ms-2 mt-3 mb-3" };
    const _hoisted_2$u = ["disabled"];


    var script$G = {
      __name: 'modal-delete-assets-epoch',
      props: {
      context: Object,
    },
      emits: ["delete_host"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);
    ref('csv');
    ref();
    ref('');
    ref(false);
    ref(false);
    const begin_date = ref();
    const disable_delete = ref(true);

    const emit = __emit;
    const modal_id = ref();
    const flat_begin_date = ref();

    const props = __props;

    onMounted(() => {
      let f_set_picker = (picker, var_name) => {
        return flatpickr(begin_date.value, {
          enableTime: true,
          dateFormat: "d/m/Y H:i",
          time_24hr: true,
          clickOpens: true,
          onChange: function (selectedDates, dateStr, instance) {
            disable_delete.value = false;
          }
        });
      };
      flat_begin_date.value = f_set_picker();
    });


    function server_date_to_date(date, format) {
      let utc = date.getTime();
      let local_offset = date.getTimezoneOffset();
      let server_offset = moment.tz(utc, ntop_zoneinfo)._offset;
      let offset_minutes = server_offset + local_offset;
      let offset_ms = offset_minutes * 1000 * 60;
      var d_local = new Date(utc - offset_ms);
      return d_local;
    }

    async function delete_host() {
      const url = `${http_prefix}/lua/pro/rest/v2/delete/host/asset.lua`;
      const begin_date = server_date_to_date(flat_begin_date.value.selectedDates[0]);
      const epoch_begin = ntopng_utility$1.get_utc_seconds(begin_date.getTime());
      const params = {
        csrf: props.context.csrf,
        ifid: props.context.ifid,
        serial_key: epoch_begin,
      };

      const res = await ntopng_utility$1.http_post_request(url, params);
      if (res) {
        close();
      }
    }

    const show = () => {
      modal_id.value.show();
    };

    const close = () => {
      setTimeout(() => {
        modal_id.value.close();
        emit("delete");
      }, 500 /* .5 seconds */);
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("asset_details.delete_asset_title")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("asset_details.delete_asset_older")) + " ", 1 /* TEXT */),
          _cache[1] || (_cache[1] = createBaseVNode("br", null, null, -1 /* CACHED */)),
          createTextVNode(" " + toDisplayString(_i18n("asset_details.last_time_seen") + ": ") + " ", 1 /* TEXT */),
          createBaseVNode("div", _hoisted_1$B, [
            _cache[0] || (_cache[0] = createBaseVNode("span", { class: "input-group-text" }, [
              createBaseVNode("i", { class: "fas fa-calendar-alt" })
            ], -1 /* CACHED */)),
            createBaseVNode("input", {
              class: "flatpickr flatpickr-input form-control",
              type: "text",
              placeholder: "Choose a date..",
              "data-id": "datetime",
              ref_key: "begin_date",
              ref: begin_date,
              style: {"width":"10rem"}
            }, null, 512 /* NEED_PATCH */)
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: delete_host,
            class: "btn btn-danger",
            disabled: disable_delete.value
          }, toDisplayString(_i18n("delete")), 9 /* TEXT, PROPS */, _hoisted_2$u)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$G.__file = "http_src/vue/modal-delete-assets-epoch.vue";

    const _hoisted_1$A = { class: "custom-file" };
    const _hoisted_2$t = {
      class: "form-label",
      for: "import-input"
    };
    const _hoisted_3$q = { class: "w-100" };
    const _hoisted_4$p = {
      key: 0,
      class: "me-auto text-danger d-inline"
    };
    const _hoisted_5$m = ["disabled"];

    const max_size_mb = 2000000; // keep in sync with en.lua -> assets_file_too_large

    var script$F = {
      __name: 'modal-import-assets',
      props: {
        ifid_list: Array,
        frequency_list: Array,
        init_func: Function,
        page_csrf: String,
    },
      emits: ['add', 'edit'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    const activate_import_spinner = ref(false);
    const is_data_not_ok = ref(false);
    const is_not_empty_file = ref(false);

    let title = _i18n('asset_details.import');
    let message = "";

    const json_file = ref(null);
    const import_input = ref(null);

    const note_list = [
        i18n("asset_details.import_assets_notes")
    ];

    const showed = () => { };

    /**
     * 
     * Reset fields in modal form 
     */
    const reset_modal_form = function () {
        json_file.value = null;
        activate_import_spinner.value = false;
        is_data_not_ok.value = false;
        is_not_empty_file.value = false;
        import_input.value.value = null;
    };

    const show = (row) => {
        reset_modal_form();
        modal_id.value.show();
    };

    const handleFileUpload = (event) => {
        json_file.value = event.target.files[0];
        const size_MB = json_file.value.size / 1000;

        is_not_empty_file.value = json_file.value != null && size_MB < max_size_mb; //2MB

        if (size_MB > max_size_mb) {
            is_data_not_ok.value = true;
            message = _i18n("assets_file_too_large");
        } else {
            is_data_not_ok.value = false;
            message = "";
        }
    };

    const _import = (is_edit) => {
        activate_import_spinner.value = true;
        is_data_not_ok.value = false;
        const fileReader = new FileReader();
        fileReader.readAsText(json_file.value, "UTF-8");

        fileReader.onload = () => {
            emit('add', {
                assets: fileReader.result
            });
        };
    };

    const show_bad_feedback = (bad_message) => {
        message = bad_message;
        is_data_not_ok.value = true;
        activate_import_spinner.value = false;
    };

    const close = () => {
        activate_import_spinner.value = false;
        is_data_not_ok.value = false;
        modal_id.value.close();
    };

    __expose({ show, close, show_bad_feedback });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[0] || (_cache[0] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(unref(title)), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Target information, here an IP is put "),
          createBaseVNode("div", _hoisted_1$A, [
            createBaseVNode("label", _hoisted_2$t, toDisplayString(_i18n("asset_details.browse_assets")), 1 /* TEXT */),
            createBaseVNode("input", {
              required: "",
              class: "custom-file-input form-control",
              ref_key: "import_input",
              ref: import_input,
              id: "import-input",
              name: "CSV",
              type: "file",
              onChange: handleFileUpload,
              accept: ".csv"
            }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", _hoisted_3$q, [
            createVNode(script$2u, { note_list: note_list })
          ]),
          (is_data_not_ok.value)
            ? (openBlock(), createElementBlock("div", _hoisted_4$p, toDisplayString(unref(message)), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createVNode(script$2v, {
              show: activate_import_spinner.value,
              size: "1rem",
              class: "me-2"
            }, null, 8 /* PROPS */, ["show"]),
            createBaseVNode("button", {
              type: "button",
              disabled: !is_not_empty_file.value,
              onClick: _import,
              class: "btn btn-primary"
            }, toDisplayString(_i18n('import')), 9 /* TEXT, PROPS */, _hoisted_5$m)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$F.__file = "http_src/vue/modal-import-assets.vue";

    const _hoisted_1$z = { class: "m-2 mb-3" };
    const _hoisted_2$s = { class: "" };
    const _hoisted_3$p = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_4$o = { class: "no-wrap d-flex align-items-center my-auto me-2 filters-label" };
    const _hoisted_5$l = { class: "d-flex justify-content-center align-items-center" };
    const _hoisted_6$i = { class: "card-footer mt-3" };
    const _hoisted_7$f = ["title"];
    const _hoisted_8$e = ["title"];

    /* ************************************** */

    const dhcp_host_icon = '<i class="fa-solid fa-bolt" title="DHCP Host"></i>';

    var script$E = {
      __name: 'page-assets',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const _i18n = (t) => i18n(t);

    /* ************************************** */

    const import_assets_url = `${http_prefix}/lua/pro/rest/v2/add/assets/assets.lua`;
    // For the export, it’s necessary to know if the page is the SNMP page
    const export_assets_url = `${http_prefix}/lua/pro/rest/v2/export/assets/assets.lua?is_snmp=${props.context.is_system_interface}`;
    const modal_import_assets = ref();

    const host_filters_key = ref(0);
    const table_id = ref(props.context.is_system_interface ? 'assets_snmp' : 'assets');
    const filter_table_array = ref([]);
    const table_assets = ref();
    const modal_delete_assets = ref();
    const modal_delete_assets_epoch = ref();
    const child_safe_icon = "<font color='#5cb85c'><i class='fas fa-lg fa-child' aria-hidden='true' title='" + i18n("host_pools.children_safe") + "'></i></font>";
    const system_host_icon = "<i class='fas fa-flag' title='" + i18n("system_host") + "'></i>";
    const hidden_from_top_icon = "<i class='fas fa-eye-slash' title='" + i18n("hidden_from_top_talkers") + "'></i>";
    const blacklisted_icon = "<i class='fas fa-ban fa-sm' title='" + i18n("hosts_stats.blacklisted") + "'></i>";
    const crawler_bot_scanner_host_icon = "<i class='fas fa-spider fa-sm' title='" + i18n("hosts_stats.crawler_bot_scanner") + "'></i>";
    const multicast_icon = "<abbr title='" + i18n("multicast") + "'><span class='badge bg-primary'>" + i18n("short_multicast") + "</span></abbr>";
    "<abbr data-bs-toggle='tooltip' data-bs-placement='top' data-bs-original-title='" + i18n("details.label_local_host") + "'><span class='badge bg-success'>" + i18n("details.label_short_local_host") + "</span></abbr>";
    const remotehost_icon = "<abbr title='" + i18n("details.label_remote") + "'><span class='badge bg-secondary'>" + i18n("details.label_short_remote") + "</span></abbr>";
    const blackhole_icon = "<abbr title='" + i18n("details.label_blackhole") + "'><span class='badge bg-info'>" + i18n("details.label_short_blackhole") + "</span></abbr>";
    const blocking_quota_icon = "<i class='fas fa-hourglass' title='" + i18n("hosts_stats.blocking_traffic_policy_popup_msg") + "'></i>";

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "ip_address": (value, row) => {
                const host = row.host;
                let ip_address = host.ip;
                let icons = '';

                if (dataUtils.isEmptyOrNull(ip_address)) {
                    return ''
                }
                let is_asset_online = row.online;

                if (!dataUtils.isEmptyOrNull(host.vlan.name)) {
                    ip_address = `${ip_address}@${host.vlan.name}`;
                }
                if (!dataUtils.isEmptyOrNull(host.system_host)) {
                    icons = `${icons} ${system_host_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.os)) {
                    const os_icon = osUtils$1.getOS(host.os);
                    icons = `${icons} ${os_icon.icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.device_type)) {
                    icons = `${icons} ${osUtils$1.getAssetIcon(host.device_type) || ''}`;
                }
                if (!dataUtils.isEmptyOrNull(host.hidden_from_top)) {
                    icons = `${icons} ${hidden_from_top_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.child_safe)) {
                    icons = `${icons} ${child_safe_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.dhcp_host)) {
                    icons = `${icons} ${dhcp_host_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.blocking_traffic_policy)) {
                    icons = `${icons} ${blocking_quota_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.country)) {
                    icons = `${icons} <a href='${http_prefix}/lua/hosts_stats.lua?country=${host.country}'><img src='${http_prefix}/dist/images/blank.gif' class='flag flag-${host.country.toLowerCase()}'></a>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_blacklisted)) {
                    icons = `${icons} ${blacklisted_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.crawler_bot_scanner_host)) {
                    icons = `${icons} ${crawler_bot_scanner_host_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_multicast)) {
                    icons = `${icons} ${multicast_icon}`;
                }

                if (!dataUtils.isEmptyOrNull(host.remotehost)) {
                    icons = `${icons} ${remotehost_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_blackhole)) {
                    icons = `${icons} ${blackhole_icon}`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_dhcp_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_dhcp_server")}</span>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_dns_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_dns_server")}</span>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_smtp_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_smtp_server")}</span>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_ntp_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_ntp_server")}</span>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_imap_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_imap_server")}</span>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_pop_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_pop_server")}</span>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_ssh_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_ssh_server")}</span>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_http_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_http_server")}</span>`;
                }
                if (!dataUtils.isEmptyOrNull(host.is_rdp_server)) {
                    icons = `${icons} <span class="badge bg-success">${i18n("details.label_rdp_server")}</span>`;
                }

                const host_url = create_button_host_details(row);

                // If host is online show host details icon fas-laptop to jump to host details page
                let host_icon = `<a href='/lua/host_details.lua?host=${row.host.ip}&vlan=${row.host.vlan.id}' data-bs-toggle='tooltip' data-bs-placement='top' title='Host Details'><i class='fas fa-laptop'></i></a>`;

                if (is_asset_online) {
                    return `<a href="${host_url}">${ip_address}</a> ${host_icon} ${icons}`
                }

                return `<a href="${host_url}">${ip_address}</a> ${icons}`

            },
            "host_name": (value, row) => {
                let name = value.name;
                if (!dataUtils.isEmptyOrNull(value.alt_name)) {
                    name = value.alt_name;
                    if (value.alt_name != value.name && !dataUtils.isEmptyOrNull(value.name)) {
                        name = `${name} [${value.name}]`;
                    }
                }

                return name
            },
            "switch_ip": (value, row) => {
                const url = http_prefix + "/lua/pro/enterprise/snmp_device_details.lua?host=" + value.value;
                if (!dataUtils.isEmptyOrNull(value.name) && value.name != value.value) {
                    return `<span data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title='${value.value}'><a href='${url}'>${value.name}</a></span>`
                }
                return `<a href='${url}'>${value.value}</a>`
            },
            "switch_port": (value, row) => {
                const url = http_prefix + "/lua/pro/enterprise/snmp_interface_details.lua?host=" + row.switch_ip.value + "&snmp_port_idx=" + value.value;
                if (!dataUtils.isEmptyOrNull(value.name) && value.name != value.value) {
                    return `<span data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title='${value.value}'><a href='${url}'>${value.name}</a></span>`
                }
                return `<a href='${url}'>${value.value}</a>`
            },
            "mac": (value, row) => {
                let result = value.value;
                if (!dataUtils.isEmptyOrNull(value.name)) {
                    result = `<span data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title='${value.value}'>${value.name}</span>`;
                }
                if (value.is_in_memory) {
                    result = `<a href='${http_prefix}/lua/mac_details.lua?host=${value.value}'>${result}</a>`;
                }

                return result;
            },
            "manufacturer": (value, row) => {
                return value;
            },
            "model": (value, row) => {
                return value;
            },
            "status": (value, row) => {
                let badge = `<span class="badge bg-secondary">${i18n('asset_details.offline')}</span>`;

                if (row.online) {
                    badge = `<span class="badge bg-success">${i18n('asset_details.online')}</span>`;
                }
                return badge
            },
            "first_seen": (value, row) => {
                return formatterUtils.formatDateTime(value.timestamp)
            },
            "last_seen": (value, row) => {
                if (row.online) {
                    return ""
                }
                return formatterUtils.formatDateTime(value.timestamp)
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    historical_flows: props.context.historical_available,
                    details: true,
                    delete: props.context.is_admin
                };
                c.button_def_array.forEach((b) => {
                    if (!visible_dict[b.id]) {
                        b.class.push("disabled");
                    }
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function delete_assets_epoch() {
        modal_delete_assets_epoch.value.show();
    }

    /* ************************************** */

    function delete_assets(row) {
        modal_delete_assets.value.show(row);
    }

    /* ************************************** */

    function reset_filters() {
        filter_table_array.value.forEach((el, index) => {
            /* Getting the currently selected filter */
            ntopng_url_manager$1.set_key_to_url(el.id, ``);
        });
        load_table_filters_array();
        refresh_table();
    }

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            /* Setting the basic label */
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            /* Getting the currently selected filter */
            const url_entry = ntopng_url_manager$1.get_url_entry(el.id);
            el.options.forEach((option) => {
                if (option.value.toString() === url_entry) {
                    el.current_option = option;
                }
            });
        });
    }

    /* ************************************** */

    async function add_table_filter(opt) {
        ntopng_url_manager$1.set_key_to_url(opt.key, `${opt.value}`);
        set_filter_array_label();
        table_assets.value.refresh_table();
    }

    /* ************************************** */

    async function load_table_filters_array() {
        let extra_params = get_extra_params_obj();
        let url_params = ntopng_url_manager$1.obj_to_url_params(extra_params);
        const url = `${http_prefix}/lua/pro/rest/v2/get/host/assets_filters.lua?${url_params}`;
        let res = await ntopng_utility.http_request(url);
        host_filters_key.value = host_filters_key.value + 1;

        filter_table_array.value = res.map((t) => {
            const key_in_url = ntopng_url_manager$1.get_url_entry(t.name);
            if (dataUtils.isEmptyOrNull(key_in_url)) {
                ntopng_url_manager$1.set_key_to_url(t.name, ``);
            }
            return {
                id: t.name,
                label: t.label,
                title: t.tooltip,
                options: t.value,
                hidden: (t.value.length == 1)
            };
        });
        set_filter_array_label();
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        extra_params = {
            ...{ ifid: props.context.ifid },
            ...extra_params,
        };
        return extra_params;
    };

    /* ************************************** */

    function create_historical_flows_url_link(row) {
        return `${http_prefix}/lua/pro/db_search.lua?ip=${row.host.ip};eq&vlan_id=${row.host.vlan.id};eq&epoch_begin=${row.first_seen.timestamp}&epoch_end=${row.last_seen.timestamp}`
    }

    /* ************************************** */

    function create_button_host_details(row) {
        let url = `/lua/pro/asset_details.lua?ifid=${props.context.ifid}&serial_key=${row.key}`;

        return `${http_prefix}${url}`
    }

    /* ************************************** */

    function click_button_delete(event) {
        const row = event.row;
        delete_assets(row);
    }

    /* ************************************** */

    function click_button_historical_flows(event) {
        const row = event.row;
        window.open(create_historical_flows_url_link(row));
    }

    /* ************************************** */

    function click_button_host_details(event) {
        const row = event.row;
        window.open(create_button_host_details(row));
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_historical_flows": click_button_historical_flows,
            "click_button_host_details": click_button_host_details,
            "click_button_delete": click_button_delete,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    function refresh_table() {
        table_assets.value.refresh_table();
    }

    /* ************************************** */

    function import_assets() {
        modal_import_assets.value.show();
    }

    /* ************************************** */

    const import_assets_rest = async function (params) {
        const url = import_assets_url;
        const result = await ntopng_utility.http_post_request(url, { ...{ csrf: props.context.csrf }, ...params }, false, true);

        if (result == null) {
            modal_import_assets.value.show_bad_feedback(_i18n("asset_details.import_error"));
        } else if (result.rc < 0) {
            modal_import_assets.value.show_bad_feedback(result.rsp.feedback);
            refresh_table();
        } else {
            setTimeout(() => { modal_import_assets.value.close(); refresh_table(); }, 1000);
        }
    };

    /* ************************************** */

    onMounted(async () => {
        load_table_filters_array();
    });

    /* ************************************** */


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$z, [
          createBaseVNode("div", _hoisted_2$s, [
            createVNode(script$2w, {
              ref_key: "table_assets",
              ref: table_assets,
              table_id: table_id.value,
              csrf: __props.context.csrf,
              showLoading: true,
              f_map_columns: map_table_def_columns,
              get_extra_params_obj: get_extra_params_obj,
              onCustom_event: on_table_custom_event
            }, {
              custom_header: withCtx(() => [
                (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
                  return (openBlock(), createElementBlock("div", _hoisted_3$p, [
                    createBaseVNode("span", _hoisted_4$o, [
                      createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                    ]),
                    createCommentVNode(" :key=\"host_filters_key\" "),
                    createVNode(script$2H, {
                      selected_option: item['current_option'],
                      "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                      theme: "bootstrap-5",
                      dropdown_size: "small",
                      options: item['options'],
                      onSelect_option: add_table_filter
                    }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options"])
                  ]))
                }), 256 /* UNKEYED_FRAGMENT */)),
                createBaseVNode("div", _hoisted_5$l, [
                  createBaseVNode("div", {
                    class: "btn btn-sm btn-primary mt-2 me-3",
                    type: "button",
                    onClick: reset_filters
                  }, toDisplayString(_i18n('reset')), 1 /* TEXT */)
                ])
              ]),
              _: 1 /* STABLE */
            }, 8 /* PROPS */, ["table_id", "csrf"]),
            createBaseVNode("div", _hoisted_6$i, [
              createBaseVNode("button", {
                class: "btn btn-primary me-1",
                type: "button",
                onClick: import_assets
              }, [
                createBaseVNode("i", {
                  class: "fa-solid fa-file-arrow-down",
                  "data-bs-toggle": "tooltip",
                  "data-bs-placement": "top",
                  title: _i18n('asset_details.import')
                }, null, 8 /* PROPS */, _hoisted_7$f),
                createTextVNode(" " + toDisplayString(_i18n('asset_details.import')), 1 /* TEXT */)
              ]),
              createBaseVNode("a", {
                class: "btn btn-primary",
                download: "assets.csv",
                href: export_assets_url
              }, [
                createBaseVNode("i", {
                  class: "fa-solid fa-file-arrow-up",
                  "data-bs-toggle": "tooltip",
                  "data-bs-placement": "top",
                  title: _i18n('asset_details.export')
                }, null, 8 /* PROPS */, _hoisted_8$e),
                createTextVNode(" " + toDisplayString(_i18n('asset_details.export')), 1 /* TEXT */)
              ]),
              (props.context.is_admin)
                ? (openBlock(), createElementBlock("button", {
                    key: 0,
                    type: "button",
                    onClick: _cache[0] || (_cache[0] = $event => (delete_assets())),
                    class: "btn btn-danger ms-1"
                  }, [
                    _cache[1] || (_cache[1] = createBaseVNode("i", { class: "fas fa fa-trash" }, null, -1 /* CACHED */)),
                    createTextVNode(" " + toDisplayString(_i18n("delete_all_entries")), 1 /* TEXT */)
                  ]))
                : createCommentVNode("v-if", true),
              (props.context.is_admin)
                ? (openBlock(), createElementBlock("button", {
                    key: 1,
                    type: "button",
                    onClick: delete_assets_epoch,
                    class: "btn btn-danger ms-1"
                  }, [
                    _cache[2] || (_cache[2] = createBaseVNode("i", { class: "fas fa fa-trash" }, null, -1 /* CACHED */)),
                    createTextVNode(" " + toDisplayString(_i18n("asset_details.delete_asset_older_title")), 1 /* TEXT */)
                  ]))
                : createCommentVNode("v-if", true)
            ])
          ])
        ]),
        createVNode(script$F, {
          ref_key: "modal_import_assets",
          ref: modal_import_assets,
          context: __props.context,
          onAdd: import_assets_rest
        }, null, 8 /* PROPS */, ["context"]),
        createVNode(script$H, {
          ref_key: "modal_delete_assets",
          ref: modal_delete_assets,
          context: __props.context,
          onDelete: refresh_table
        }, null, 8 /* PROPS */, ["context"]),
        createVNode(script$G, {
          ref_key: "modal_delete_assets_epoch",
          ref: modal_delete_assets_epoch,
          context: __props.context,
          onDelete: refresh_table
        }, null, 8 /* PROPS */, ["context"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$E.__file = "http_src/vue/page-assets.vue";

    const _hoisted_1$y = { class: "row" };
    const _hoisted_2$r = { class: "col-md-12 col-lg-12" };
    const _hoisted_3$o = { class: "mt-4 card card-shadow" };
    const _hoisted_4$n = { class: "card-body" };

    const url = "/lua/pro/rest/v2/get/host/asset_details.lua";

    var script$D = {
      __name: 'page-asset-details',
      props: {
        ifid: Number,
        csrf: String
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const table_id = ref('asset_details');

    const stats_rows = ref([]);

    const print_html_title = function (name) {
        return (name || "");
    };

    const print_stats_row = function (value) {
        let label = value.name || '';
        if (value.url && value.url != '')
            label = `<a href="${http_prefix}${value.url}">${label}</>`;

        return label;
    };

    onMounted(async () => {
        const extra_params = ntopng_url_manager.get_url_object();
        const url_params = ntopng_url_manager.obj_to_url_params(extra_params);
        const host_stats = await ntopng_utility.http_request(`${http_prefix}${url}?${url_params}`);
        stats_rows.value = host_stats.host_info;
        $('#navbar_title').html("<i class='fas fa-laptop'></i> " + _i18n('asset_details.assets') + ": " + host_stats.host_name);
    });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$y, [
        createBaseVNode("div", _hoisted_2$r, [
          createBaseVNode("div", _hoisted_3$o, [
            createBaseVNode("div", _hoisted_4$n, [
              createVNode(script$2d, {
                horizontal: true,
                id: table_id.value,
                rows: stats_rows.value,
                print_html_title: print_html_title,
                print_html_row: print_stats_row
              }, null, 8 /* PROPS */, ["id", "rows"])
            ])
          ])
        ])
      ]))
    }
    }

    };

    script$D.__file = "http_src/vue/page-asset-details.vue";

    const _hoisted_1$x = { class: "vh-100 d-flex flex-column" };
    const _hoisted_2$q = { class: "flex-grow-1 p-3" };
    const _hoisted_3$n = {
      key: 0,
      class: "d-flex justify-content-center py-5"
    };
    const _hoisted_4$m = {
      class: "alert alert-danger",
      role: "alert"
    };
    const _hoisted_5$k = { class: "h-100 d-flex flex-column" };
    const _hoisted_6$h = { class: "mb-3 flex-shrink-0" };
    const _hoisted_7$e = { class: "card-body p-4 p-lg-5" };
    const _hoisted_8$d = { class: "row align-items-start" };
    const _hoisted_9$d = { class: "col-lg-8" };
    const _hoisted_10$c = { class: "row g-4 mb-4" };
    const _hoisted_11$a = { class: "col-md-4" };
    const _hoisted_12$9 = { class: "d-flex align-items-center gap-3" };
    const _hoisted_13$8 = ["href"];
    const _hoisted_14$8 = { class: "col-md-4" };
    const _hoisted_15$8 = { class: "d-flex align-items-center gap-3" };
    const _hoisted_16$8 = { class: "d-flex align-items-center gap-2" };
    const _hoisted_17$8 = { class: "position-relative" };
    const _hoisted_18$7 = { class: "col-md-4" };
    const _hoisted_19$7 = { class: "d-flex align-items-center gap-3" };
    const _hoisted_20$7 = { class: "fw-semibold" };
    const _hoisted_21$7 = ["innerHTML"];
    const _hoisted_22$7 = ["innerHTML"];
    const _hoisted_23$7 = { class: "col-lg-4" };
    const _hoisted_24$7 = { class: "d-flex gap-2" };
    const _hoisted_25$6 = { class: "text-bg-primary border rounded-3 px-3 py-2 text-center flex-fill" };
    const _hoisted_26$5 = { class: "h5 fw-bold mb-0" };
    const _hoisted_27$5 = { class: "h5 fw-bold mb-0" };
    const _hoisted_28$4 = { class: "h5 fw-bold mb-0" };
    const _hoisted_29$4 = { class: "row g-4" };
    const _hoisted_30$4 = { class: "col-lg-4" };
    const _hoisted_31$3 = { class: "card-body p-4" };
    const _hoisted_32$3 = { class: "vstack gap-4" };
    const _hoisted_33$3 = {
      key: 0,
      class: "info-item"
    };
    const _hoisted_34$3 = { class: "mb-0" };
    const _hoisted_35$3 = {
      key: 1,
      class: "info-item"
    };
    const _hoisted_36$3 = { class: "d-flex align-items-center gap-2" };
    const _hoisted_37$3 = { class: "px-2 py-1 rounded mb-0" };
    const _hoisted_38$3 = ["href"];
    const _hoisted_39$2 = {
      key: 2,
      class: "info-item"
    };
    const _hoisted_40$2 = { class: "mb-0" };
    const _hoisted_41$2 = { class: "info-item" };
    const _hoisted_42$2 = { class: "mb-0" };
    const _hoisted_43$2 = { class: "info-item" };
    const _hoisted_44$2 = { class: "mb-0" };
    const _hoisted_45$2 = {
      key: 0,
      class: "badge bg-success"
    };
    const _hoisted_46$2 = { key: 1 };
    const _hoisted_47$1 = { class: "col-lg-4" };
    const _hoisted_48$1 = { class: "card-body p-4" };
    const _hoisted_49$1 = { class: "vstack gap-4" };
    const _hoisted_50$1 = {
      key: 0,
      class: "info-item"
    };
    const _hoisted_51$1 = { class: "d-flex align-items-center gap-2" };
    const _hoisted_52$1 = { class: "px-2 py-1 rounded" };
    const _hoisted_53$1 = { key: 0 };
    const _hoisted_54$1 = {
      key: 1,
      class: "info-item"
    };
    const _hoisted_55$1 = { class: "d-flex align-items-center gap-2" };
    const _hoisted_56$1 = ["href"];
    const _hoisted_57$1 = {
      key: 2,
      class: "info-item"
    };
    const _hoisted_58$1 = ["innerHTML"];
    const _hoisted_59$1 = {
      key: 3,
      class: "info-item"
    };
    const _hoisted_60$1 = ["innerHTML"];
    const _hoisted_61$1 = {
      key: 4,
      class: "info-item"
    };
    const _hoisted_62$1 = ["innerHTML"];
    const _hoisted_63$1 = {
      key: 5,
      class: "info-item"
    };
    const _hoisted_64$1 = { class: "badge bg-primary rounded-pill" };
    const _hoisted_65$1 = { class: "col-lg-4" };
    const _hoisted_66$1 = { class: "card-body p-4" };
    const _hoisted_67$1 = { class: "vstack gap-4" };
    const _hoisted_68$1 = {
      key: 0,
      class: "info-item"
    };
    const _hoisted_69$1 = { class: "d-flex flex-wrap gap-2" };
    const _hoisted_70$1 = {
      key: 1,
      class: "info-item"
    };
    const _hoisted_71$1 = {
      key: 2,
      class: "info-item"
    };
    const _hoisted_72$1 = {
      key: 3,
      class: "info-item"
    };
    const _hoisted_73$1 = { class: "vstack gap-3" };
    const _hoisted_74$1 = { class: "row g-2" };
    const _hoisted_75$1 = { class: "d-flex align-items-center justify-content-between" };
    const _hoisted_76$1 = ["href"];
    const _hoisted_77$1 = ["title"];
    const _hoisted_78$1 = { class: "row g-2" };
    const _hoisted_79$1 = { class: "d-flex align-items-center justify-content-between" };
    const _hoisted_80$1 = ["href"];
    const _hoisted_81$1 = ["title"];
    const _hoisted_82$1 = {
      key: 0,
      class: "row g-4 mt-2"
    };
    const _hoisted_83$1 = { class: "col-12" };
    const _hoisted_84$1 = { class: "card-body p-4" };
    const _hoisted_85$1 = { class: "row g-3" };
    const _hoisted_86$1 = { class: "fw-semibold mb-1 d-block" };
    const _hoisted_87$1 = { class: "mb-0" };
    // Props

    var script$C = {
      __name: 'page-asset-details-new',
      props: {
        ifid: Number,
        csrf: String
    },
      setup(__props) {

    // Reactive data
    const loading = ref(true);
    const error = ref(null);
    const deviceData = ref(null);
    const highlightMode = ref(null);
    const mac_details_url = `${http_prefix}/lua/mac_details.lua?host=`;
    const network_details_url = `${http_prefix}/lua/hosts_stats.lua?network=`;

    // Data fetching
    const fetchData = async () => {
        loading.value = true;
        error.value = null;

        try {
            const url = "/lua/pro/rest/v2/get/host/asset_details_new.lua";
            const extra_params = ntopng_url_manager$1.get_url_object();
            const url_params = ntopng_url_manager$1.obj_to_url_params(extra_params);

            const response = await ntopng_utility.http_request(`${http_prefix}${url}?${url_params}`);

            if (response) {
                // Take the first host from the array (assuming single host lookup)
                deviceData.value = response[0];
                console.log(deviceData.value);
                // Update navbar title if needed
                if (typeof $ !== 'undefined') {
                    $('#navbar_title').html("<i class='fas fa-laptop'></i> Asset Details: " + deviceData.value.host_name);
                }
            } else {
                error.value = response?.rsp?.message || "No asset data found";
            }

        } catch (err) {
            console.error('Error fetching device data:', err);
            error.value = `Failed to fetch asset data: ${err.message}`;
        } finally {
            loading.value = false;
        }
    };

    // Lifecycle
    onMounted(fetchData);

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$x, [
        createBaseVNode("div", _hoisted_2$q, [
          createCommentVNode(" Loading State "),
          (loading.value)
            ? (openBlock(), createElementBlock("div", _hoisted_3$n, [...(_cache[4] || (_cache[4] = [
                createBaseVNode("div", {
                  class: "spinner-border",
                  role: "status"
                }, [
                  createBaseVNode("span", { class: "visually-hidden" }, "Loading...")
                ], -1 /* CACHED */)
              ]))]))
            : (error.value)
              ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                  createCommentVNode(" Error State "),
                  createBaseVNode("div", _hoisted_4$m, [
                    _cache[5] || (_cache[5] = createBaseVNode("h4", { class: "alert-heading" }, "Error Loading Asset Details", -1 /* CACHED */)),
                    createBaseVNode("p", null, toDisplayString(error.value), 1 /* TEXT */)
                  ])
                ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
              : (!deviceData.value)
                ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [
                    createCommentVNode(" No Data State "),
                    _cache[6] || (_cache[6] = createBaseVNode("div", {
                      class: "alert alert-warning",
                      role: "alert"
                    }, [
                      createBaseVNode("h4", { class: "alert-heading" }, "No Asset Found"),
                      createBaseVNode("p", null, "No asset information found for the provided serial key.")
                    ], -1 /* CACHED */))
                  ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
                : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
                    createCommentVNode(" Main Content "),
                    createBaseVNode("div", _hoisted_5$k, [
                      createCommentVNode(" Modern Header with Enhanced Information "),
                      createBaseVNode("div", _hoisted_6$h, [
                        createBaseVNode("div", {
                          class: normalizeClass(["card shadow-sm border rounded-4", { 'dimmed': highlightMode.value }])
                        }, [
                          createBaseVNode("div", _hoisted_7$e, [
                            createBaseVNode("div", _hoisted_8$d, [
                              createCommentVNode(" Left Section - Device Info "),
                              createBaseVNode("div", _hoisted_9$d, [
                                createCommentVNode(" Device Identity "),
                                createBaseVNode("div", _hoisted_10$c, [
                                  createBaseVNode("div", _hoisted_11$a, [
                                    createBaseVNode("div", _hoisted_12$9, [
                                      _cache[8] || (_cache[8] = createBaseVNode("div", { class: "p-2 bg-primary bg-opacity-10 rounded-3" }, [
                                        createBaseVNode("i", { class: "fas fa-server" })
                                      ], -1 /* CACHED */)),
                                      createBaseVNode("div", null, [
                                        _cache[7] || (_cache[7] = createBaseVNode("p", { class: "fw-semibold mb-1" }, "Device Name", -1 /* CACHED */)),
                                        createBaseVNode("a", {
                                          class: "mb-0",
                                          href: `${deviceData.value.host_url}`
                                        }, toDisplayString(deviceData.value.host_name), 9 /* TEXT, PROPS */, _hoisted_13$8)
                                      ])
                                    ])
                                  ]),
                                  createBaseVNode("div", _hoisted_14$8, [
                                    createBaseVNode("div", _hoisted_15$8, [
                                      createBaseVNode("div", _hoisted_16$8, [
                                        createBaseVNode("div", _hoisted_17$8, [
                                          createBaseVNode("div", {
                                            class: normalizeClass(["rounded-circle", deviceData.value.is_online ? 'bg-success pulse-animation' : 'bg-secondary']),
                                            style: {"width":"16px","height":"16px"}
                                          }, null, 2 /* CLASS */)
                                        ]),
                                        createBaseVNode("div", null, [
                                          _cache[9] || (_cache[9] = createBaseVNode("p", { class: "fw-semibold mb-1" }, "Status", -1 /* CACHED */)),
                                          createBaseVNode("p", {
                                            class: normalizeClass(["fw-semibold mb-0", deviceData.value.is_online ? 'text-success' : 'text-secondary'])
                                          }, toDisplayString(deviceData.value.is_online ? 'Online' : 'Offline'), 3 /* TEXT, CLASS */)
                                        ])
                                      ])
                                    ])
                                  ]),
                                  createBaseVNode("div", _hoisted_18$7, [
                                    createBaseVNode("div", _hoisted_19$7, [
                                      _cache[11] || (_cache[11] = createBaseVNode("div", { class: "p-2 bg-warning bg-opacity-10 rounded-3" }, [
                                        createBaseVNode("i", { class: "fas fa-bolt text-warning" })
                                      ], -1 /* CACHED */)),
                                      createBaseVNode("div", null, [
                                        _cache[10] || (_cache[10] = createBaseVNode("p", { class: "fw-semibold mb-1" }, "Device Type", -1 /* CACHED */)),
                                        createBaseVNode("div", _hoisted_20$7, [
                                          (deviceData.value.device_icon)
                                            ? (openBlock(), createElementBlock("span", {
                                                key: 0,
                                                innerHTML: deviceData.value.device_icon
                                              }, null, 8 /* PROPS */, _hoisted_21$7))
                                            : createCommentVNode("v-if", true),
                                          createTextVNode(" " + toDisplayString(deviceData.value.device_type_name || 'Unknown') + " ", 1 /* TEXT */),
                                          (deviceData.value.os_icon)
                                            ? (openBlock(), createElementBlock("span", {
                                                key: 1,
                                                innerHTML: deviceData.value.os_icon
                                              }, null, 8 /* PROPS */, _hoisted_22$7))
                                            : createCommentVNode("v-if", true)
                                        ])
                                      ])
                                    ])
                                  ])
                                ])
                              ]),
                              createCommentVNode(" Right Section - Services Info "),
                              createBaseVNode("div", _hoisted_23$7, [
                                createCommentVNode(" Quick Stats "),
                                createBaseVNode("div", _hoisted_24$7, [
                                  createBaseVNode("div", _hoisted_25$6, [
                                    _cache[12] || (_cache[12] = createBaseVNode("p", { class: "mb-1" }, "Services", -1 /* CACHED */)),
                                    createBaseVNode("p", _hoisted_26$5, toDisplayString(deviceData.value.server_services?.length || 0), 1 /* TEXT */)
                                  ]),
                                  createBaseVNode("div", {
                                    class: normalizeClass(["hover-highlight rounded-3 px-3 py-2 text-center flex-fill transition-all", [
                                                    highlightMode.value === 'tcp' ? 'tcp-highlighted' : 'bg-success bg-opacity-10 border border-success',
                                                    highlightMode.value && highlightMode.value !== 'tcp' ? 'dimmed' : ''
                                                ]]),
                                    onMouseenter: _cache[0] || (_cache[0] = $event => (highlightMode.value = 'tcp')),
                                    onMouseleave: _cache[1] || (_cache[1] = $event => (highlightMode.value = null)),
                                    style: {"cursor":"pointer"}
                                  }, [
                                    _cache[13] || (_cache[13] = createBaseVNode("p", { class: "mb-1" }, "TCP Ports", -1 /* CACHED */)),
                                    createBaseVNode("p", _hoisted_27$5, toDisplayString(deviceData.value.tcp_ports?.length || 0), 1 /* TEXT */)
                                  ], 34 /* CLASS, NEED_HYDRATION */),
                                  createBaseVNode("div", {
                                    class: normalizeClass(["hover-highlight rounded-3 px-3 py-2 text-center flex-fill transition-all", [
                                                    highlightMode.value === 'udp' ? 'udp-highlighted' : 'bg-warning bg-opacity-10 border border-warning',
                                                    highlightMode.value && highlightMode.value !== 'udp' ? 'dimmed' : ''
                                                ]]),
                                    onMouseenter: _cache[2] || (_cache[2] = $event => (highlightMode.value = 'udp')),
                                    onMouseleave: _cache[3] || (_cache[3] = $event => (highlightMode.value = null)),
                                    style: {"cursor":"pointer"}
                                  }, [
                                    _cache[14] || (_cache[14] = createBaseVNode("p", { class: "mb-1" }, "UDP Ports", -1 /* CACHED */)),
                                    createBaseVNode("p", _hoisted_28$4, toDisplayString(deviceData.value.udp_ports?.length || 0), 1 /* TEXT */)
                                  ], 34 /* CLASS, NEED_HYDRATION */)
                                ])
                              ])
                            ])
                          ])
                        ], 2 /* CLASS */)
                      ]),
                      createCommentVNode(" Main Grid "),
                      createBaseVNode("div", _hoisted_29$4, [
                        createCommentVNode(" Device Information Card "),
                        createBaseVNode("div", _hoisted_30$4, [
                          createBaseVNode("div", {
                            class: normalizeClass(["card shadow-sm border rounded-3 h-100", { 'dimmed': highlightMode.value }])
                          }, [
                            createBaseVNode("div", _hoisted_31$3, [
                              _cache[20] || (_cache[20] = createBaseVNode("div", { class: "d-flex align-items-center gap-3 mb-4" }, [
                                createBaseVNode("div", { class: "p-2 bg-primary bg-opacity-10 rounded-3" }, [
                                  createBaseVNode("i", { class: "fas fa-desktop" })
                                ]),
                                createBaseVNode("h2", { class: "h5 fw-semibold mb-0" }, "Device Information")
                              ], -1 /* CACHED */)),
                              createBaseVNode("div", _hoisted_32$3, [
                                (deviceData.value.device_type_name)
                                  ? (openBlock(), createElementBlock("div", _hoisted_33$3, [
                                      _cache[15] || (_cache[15] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "Device Type", -1 /* CACHED */)),
                                      createBaseVNode("p", _hoisted_34$3, toDisplayString(deviceData.value.device_type_name), 1 /* TEXT */)
                                    ]))
                                  : createCommentVNode("v-if", true),
                                (deviceData.value.mac_address)
                                  ? (openBlock(), createElementBlock("div", _hoisted_35$3, [
                                      _cache[16] || (_cache[16] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "MAC Address", -1 /* CACHED */)),
                                      createBaseVNode("div", _hoisted_36$3, [
                                        createBaseVNode("p", _hoisted_37$3, [
                                          (deviceData.value.symbolic_mac)
                                            ? (openBlock(), createElementBlock("a", {
                                                key: 0,
                                                href: `${mac_details_url}${deviceData.value.symbolic_mac}`
                                              }, toDisplayString(deviceData.value.symbolic_mac), 9 /* TEXT, PROPS */, _hoisted_38$3))
                                            : createCommentVNode("v-if", true)
                                        ])
                                      ])
                                    ]))
                                  : createCommentVNode("v-if", true),
                                (deviceData.value.model)
                                  ? (openBlock(), createElementBlock("div", _hoisted_39$2, [
                                      _cache[17] || (_cache[17] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "Model", -1 /* CACHED */)),
                                      createBaseVNode("p", _hoisted_40$2, toDisplayString(deviceData.value.model), 1 /* TEXT */)
                                    ]))
                                  : createCommentVNode("v-if", true),
                                createBaseVNode("div", _hoisted_41$2, [
                                  _cache[18] || (_cache[18] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "First Seen", -1 /* CACHED */)),
                                  createBaseVNode("p", _hoisted_42$2, toDisplayString(deviceData.value.first_seen_formatted), 1 /* TEXT */)
                                ]),
                                createBaseVNode("div", _hoisted_43$2, [
                                  _cache[19] || (_cache[19] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "Last Seen", -1 /* CACHED */)),
                                  createBaseVNode("p", _hoisted_44$2, [
                                    (deviceData.value.last_seen_formatted === 'Online')
                                      ? (openBlock(), createElementBlock("span", _hoisted_45$2, " Online "))
                                      : (openBlock(), createElementBlock("span", _hoisted_46$2, toDisplayString(deviceData.value.last_seen_formatted), 1 /* TEXT */))
                                  ])
                                ])
                              ])
                            ])
                          ], 2 /* CLASS */)
                        ]),
                        createCommentVNode(" Network Information Card "),
                        createBaseVNode("div", _hoisted_47$1, [
                          createBaseVNode("div", {
                            class: normalizeClass(["card shadow-sm border rounded-3 h-100", { 'dimmed': highlightMode.value }])
                          }, [
                            createBaseVNode("div", _hoisted_48$1, [
                              _cache[27] || (_cache[27] = createBaseVNode("div", { class: "d-flex align-items-center gap-3 mb-4" }, [
                                createBaseVNode("div", { class: "p-2 bg-success bg-opacity-10 rounded-3" }, [
                                  createBaseVNode("i", { class: "fas fa-network-wired text-success" })
                                ]),
                                createBaseVNode("h2", { class: "h5 fw-semibold mb-0" }, "Network Information")
                              ], -1 /* CACHED */)),
                              createBaseVNode("div", _hoisted_49$1, [
                                (deviceData.value.ip_address)
                                  ? (openBlock(), createElementBlock("div", _hoisted_50$1, [
                                      _cache[21] || (_cache[21] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "IP Address", -1 /* CACHED */)),
                                      createBaseVNode("div", _hoisted_51$1, [
                                        createBaseVNode("code", _hoisted_52$1, [
                                          createTextVNode(toDisplayString(deviceData.value.ip_address) + " ", 1 /* TEXT */),
                                          (deviceData.value.vlan && deviceData.value.vlan !== '0')
                                            ? (openBlock(), createElementBlock("span", _hoisted_53$1, "@" + toDisplayString(deviceData.value.vlan), 1 /* TEXT */))
                                            : createCommentVNode("v-if", true)
                                        ])
                                      ])
                                    ]))
                                  : createCommentVNode("v-if", true),
                                (deviceData.value.network_name)
                                  ? (openBlock(), createElementBlock("div", _hoisted_54$1, [
                                      _cache[22] || (_cache[22] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "Network", -1 /* CACHED */)),
                                      createBaseVNode("div", _hoisted_55$1, [
                                        createBaseVNode("a", {
                                          class: "mb-0",
                                          href: `${network_details_url}${deviceData.value.network}`
                                        }, toDisplayString(deviceData.value.network_name), 9 /* TEXT, PROPS */, _hoisted_56$1)
                                      ])
                                    ]))
                                  : createCommentVNode("v-if", true),
                                (deviceData.value.snmp_location_description)
                                  ? (openBlock(), createElementBlock("div", _hoisted_57$1, [
                                      _cache[23] || (_cache[23] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "SNMP Location", -1 /* CACHED */)),
                                      createBaseVNode("p", {
                                        class: "mb-0",
                                        innerHTML: deviceData.value.snmp_location_description
                                      }, null, 8 /* PROPS */, _hoisted_58$1)
                                    ]))
                                  : createCommentVNode("v-if", true),
                                (deviceData.value.switch_ip)
                                  ? (openBlock(), createElementBlock("div", _hoisted_59$1, [
                                      _cache[24] || (_cache[24] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "Switch IP", -1 /* CACHED */)),
                                      createBaseVNode("p", {
                                        class: "mb-0",
                                        innerHTML: deviceData.value.switch_ip_url || deviceData.value.switch_ip_name || deviceData.value.switch_ip
                                      }, null, 8 /* PROPS */, _hoisted_60$1)
                                    ]))
                                  : createCommentVNode("v-if", true),
                                (deviceData.value.switch_port)
                                  ? (openBlock(), createElementBlock("div", _hoisted_61$1, [
                                      _cache[25] || (_cache[25] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "Switch Port", -1 /* CACHED */)),
                                      createBaseVNode("p", {
                                        class: "mb-0",
                                        innerHTML: deviceData.value.switch_port_formatted || deviceData.value.switch_port
                                      }, null, 8 /* PROPS */, _hoisted_62$1)
                                    ]))
                                  : createCommentVNode("v-if", true),
                                (deviceData.value.additional_names && deviceData.value.additional_names.length > 0)
                                  ? (openBlock(), createElementBlock("div", _hoisted_63$1, [
                                      _cache[26] || (_cache[26] = createBaseVNode("label", { class: "fw-semibold mb-1 d-block" }, "Additional Names", -1 /* CACHED */)),
                                      (openBlock(true), createElementBlock(Fragment, null, renderList(deviceData.value.additional_names, (host, index) => {
                                        return (openBlock(), createElementBlock("div", {
                                          key: index,
                                          class: "d-flex align-items-center justify-content-between px-3 py-2 rounded-3 mt-2"
                                        }, [
                                          createBaseVNode("span", null, toDisplayString(host.name), 1 /* TEXT */),
                                          createBaseVNode("span", _hoisted_64$1, toDisplayString(host.source), 1 /* TEXT */)
                                        ]))
                                      }), 128 /* KEYED_FRAGMENT */))
                                    ]))
                                  : createCommentVNode("v-if", true)
                              ])
                            ])
                          ], 2 /* CLASS */)
                        ]),
                        createCommentVNode(" Services & Status Card "),
                        createBaseVNode("div", _hoisted_65$1, [
                          createBaseVNode("div", {
                            class: normalizeClass(["card shadow-sm border rounded-3 h-100", { 'dimmed': highlightMode.value && highlightMode.value !== 'tcp' && highlightMode.value !== 'udp' }])
                          }, [
                            createBaseVNode("div", _hoisted_66$1, [
                              _cache[34] || (_cache[34] = createBaseVNode("div", { class: "d-flex align-items-center gap-3 mb-4" }, [
                                createBaseVNode("div", { class: "p-2 bg-info bg-opacity-10 rounded-3" }, [
                                  createBaseVNode("i", { class: "fas fa-chart-line text-info" })
                                ]),
                                createBaseVNode("h2", { class: "h5 fw-semibold mb-0" }, "Services & Status")
                              ], -1 /* CACHED */)),
                              createBaseVNode("div", _hoisted_67$1, [
                                createCommentVNode(" Running Services "),
                                (deviceData.value.server_services && deviceData.value.server_services.length > 0)
                                  ? (openBlock(), createElementBlock("div", _hoisted_68$1, [
                                      _cache[28] || (_cache[28] = createBaseVNode("label", { class: "fw-semibold mb-2 d-block" }, "Running Services", -1 /* CACHED */)),
                                      createBaseVNode("div", _hoisted_69$1, [
                                        (openBlock(true), createElementBlock(Fragment, null, renderList(deviceData.value.server_services, (service, index) => {
                                          return (openBlock(), createElementBlock("span", {
                                            key: index,
                                            class: "badge bg-success"
                                          }, toDisplayString(service.label || service.type), 1 /* TEXT */))
                                        }), 128 /* KEYED_FRAGMENT */))
                                      ])
                                    ]))
                                  : createCommentVNode("v-if", true),
                                createCommentVNode(" DHCP Fingerprint "),
                                (deviceData.value.has_dhcp_fingerprint)
                                  ? (openBlock(), createElementBlock("div", _hoisted_70$1, [...(_cache[29] || (_cache[29] = [
                                      createBaseVNode("div", { class: "d-flex align-items-center gap-2" }, [
                                        createBaseVNode("i", { class: "fas fa-bolt text-warning" }),
                                        createBaseVNode("span", { class: "badge bg-warning" }, " DHCP Fingerprint ")
                                      ], -1 /* CACHED */)
                                    ]))]))
                                  : createCommentVNode("v-if", true),
                                createCommentVNode(" SNMP "),
                                (deviceData.value.has_snmp)
                                  ? (openBlock(), createElementBlock("div", _hoisted_71$1, [...(_cache[30] || (_cache[30] = [
                                      createBaseVNode("div", { class: "d-flex align-items-center gap-2" }, [
                                        createBaseVNode("i", { class: "fas fa-network-wired text-info" }),
                                        createBaseVNode("span", { class: "badge bg-info bg-opacity-10 text-info border border-info rounded-pill px-3 py-1" }, " SNMP Enabled ")
                                      ], -1 /* CACHED */)
                                    ]))]))
                                  : createCommentVNode("v-if", true),
                                createCommentVNode(" Open Ports "),
                                ((deviceData.value.tcp_ports && deviceData.value.tcp_ports.length > 0) || (deviceData.value.udp_ports && deviceData.value.udp_ports.length > 0))
                                  ? (openBlock(), createElementBlock("div", _hoisted_72$1, [
                                      _cache[33] || (_cache[33] = createBaseVNode("label", { class: "fw-semibold mb-3 d-block" }, "Open Ports", -1 /* CACHED */)),
                                      createBaseVNode("div", _hoisted_73$1, [
                                        createCommentVNode(" TCP Ports "),
                                        (deviceData.value.tcp_ports && deviceData.value.tcp_ports.length > 0)
                                          ? (openBlock(), createElementBlock("div", {
                                              key: 0,
                                              class: normalizeClass({
                                                    'tcp-ports-highlighted': highlightMode.value === 'tcp',
                                                    'dimmed': highlightMode.value && highlightMode.value !== 'tcp'
                                                })
                                            }, [
                                              _cache[31] || (_cache[31] = createBaseVNode("p", { class: "fw-semibold mb-2" }, "TCP Ports", -1 /* CACHED */)),
                                              createBaseVNode("div", _hoisted_74$1, [
                                                (openBlock(true), createElementBlock(Fragment, null, renderList(deviceData.value.tcp_ports.slice(0, 6), (port, index) => {
                                                  return (openBlock(), createElementBlock("div", {
                                                    key: index,
                                                    class: "col-6"
                                                  }, [
                                                    createBaseVNode("div", {
                                                      class: normalizeClass(["port-item rounded-3 p-2 transition-all", highlightMode.value === 'tcp' ? 'bg-success text-white border-0' : 'bg-primary bg-opacity-10 border border-primary'])
                                                    }, [
                                                      createBaseVNode("div", _hoisted_75$1, [
                                                        (port.flows_url)
                                                          ? (openBlock(), createElementBlock("a", {
                                                              key: 0,
                                                              href: port.flows_url,
                                                              class: normalizeClass(["fw-bold text-decoration-none", highlightMode.value === 'tcp' ? 'text-white' : 'text-primary'])
                                                            }, toDisplayString(port.port), 11 /* TEXT, CLASS, PROPS */, _hoisted_76$1))
                                                          : (openBlock(), createElementBlock("span", {
                                                              key: 1,
                                                              class: normalizeClass(["fw-bold", highlightMode.value === 'tcp' ? 'text-white' : 'text-primary'])
                                                            }, toDisplayString(port.port), 3 /* TEXT, CLASS */)),
                                                        createBaseVNode("span", {
                                                          title: port.name,
                                                          class: normalizeClass(highlightMode.value === 'tcp' ? 'text-white' : 'text-primary')
                                                        }, toDisplayString(port.name), 11 /* TEXT, CLASS, PROPS */, _hoisted_77$1)
                                                      ])
                                                    ], 2 /* CLASS */)
                                                  ]))
                                                }), 128 /* KEYED_FRAGMENT */))
                                              ])
                                            ], 2 /* CLASS */))
                                          : createCommentVNode("v-if", true),
                                        createCommentVNode(" UDP Ports "),
                                        (deviceData.value.udp_ports && deviceData.value.udp_ports.length > 0)
                                          ? (openBlock(), createElementBlock("div", {
                                              key: 1,
                                              class: normalizeClass({
                                                    'udp-ports-highlighted': highlightMode.value === 'udp',
                                                    'dimmed': highlightMode.value && highlightMode.value !== 'udp'
                                                })
                                            }, [
                                              _cache[32] || (_cache[32] = createBaseVNode("p", { class: "fw-semibold mb-2" }, "UDP Ports", -1 /* CACHED */)),
                                              createBaseVNode("div", _hoisted_78$1, [
                                                (openBlock(true), createElementBlock(Fragment, null, renderList(deviceData.value.udp_ports.slice(0, 6), (port, index) => {
                                                  return (openBlock(), createElementBlock("div", {
                                                    key: index,
                                                    class: "col-6"
                                                  }, [
                                                    createBaseVNode("div", {
                                                      class: normalizeClass(["port-item rounded-3 p-2 transition-all", highlightMode.value === 'udp' ? 'bg-warning text-white' : 'bg-warning bg-opacity-10 border border-warning text-warning'])
                                                    }, [
                                                      createBaseVNode("div", _hoisted_79$1, [
                                                        (port.flows_url)
                                                          ? (openBlock(), createElementBlock("a", {
                                                              key: 0,
                                                              href: port.flows_url,
                                                              class: normalizeClass(["fw-bold text-decoration-none", highlightMode.value === 'udp' ? 'text-white' : 'text-warning'])
                                                            }, toDisplayString(port.port), 11 /* TEXT, CLASS, PROPS */, _hoisted_80$1))
                                                          : (openBlock(), createElementBlock("span", {
                                                              key: 1,
                                                              class: normalizeClass(["fw-bold", highlightMode.value === 'udp' ? 'text-white' : 'text-warning'])
                                                            }, toDisplayString(port.port), 3 /* TEXT, CLASS */)),
                                                        createBaseVNode("span", {
                                                          title: port.name,
                                                          class: normalizeClass(highlightMode.value === 'udp' ? 'text-white' : 'text-warning')
                                                        }, toDisplayString(port.name), 11 /* TEXT, CLASS, PROPS */, _hoisted_81$1)
                                                      ])
                                                    ], 2 /* CLASS */)
                                                  ]))
                                                }), 128 /* KEYED_FRAGMENT */))
                                              ])
                                            ], 2 /* CLASS */))
                                          : createCommentVNode("v-if", true)
                                      ])
                                    ]))
                                  : createCommentVNode("v-if", true)
                              ])
                            ])
                          ], 2 /* CLASS */)
                        ])
                      ]),
                      createCommentVNode(" Additional JSON Fields (if any) "),
                      (deviceData.value.json_fields && deviceData.value.json_fields.length > 0)
                        ? (openBlock(), createElementBlock("div", _hoisted_82$1, [
                            createBaseVNode("div", _hoisted_83$1, [
                              createBaseVNode("div", {
                                class: normalizeClass(["card shadow-sm border rounded-3", { 'dimmed': highlightMode.value }])
                              }, [
                                createBaseVNode("div", _hoisted_84$1, [
                                  _cache[35] || (_cache[35] = createBaseVNode("div", { class: "d-flex align-items-center gap-3 mb-4" }, [
                                    createBaseVNode("div", { class: "p-2 bg-secondary bg-opacity-10 rounded-3" }, [
                                      createBaseVNode("i", { class: "fas fa-cog text-secondary" })
                                    ]),
                                    createBaseVNode("h2", { class: "h5 fw-semibold mb-0" }, "Additional Information")
                                  ], -1 /* CACHED */)),
                                  createBaseVNode("div", _hoisted_85$1, [
                                    (openBlock(true), createElementBlock(Fragment, null, renderList(deviceData.value.json_fields, (field, index) => {
                                      return (openBlock(), createElementBlock("div", {
                                        key: index,
                                        class: "col-md-6 info-item"
                                      }, [
                                        createBaseVNode("label", _hoisted_86$1, toDisplayString(field.label), 1 /* TEXT */),
                                        createBaseVNode("p", _hoisted_87$1, toDisplayString(field.value), 1 /* TEXT */)
                                      ]))
                                    }), 128 /* KEYED_FRAGMENT */))
                                  ])
                                ])
                              ], 2 /* CLASS */)
                            ])
                          ]))
                        : createCommentVNode("v-if", true)
                    ])
                  ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
        ])
      ]))
    }
    }

    };

    var css_248z$d = "\n/* Transition effects */\n.transition-all[data-v-45adc427] {\n    transition: all 0.3s ease;\n}\n\n/* Dimming effect */\n.dimmed[data-v-45adc427] {\n    opacity: 0.3;\n    transition: opacity 0.3s ease;\n}\n\n/* TCP highlighting */\n.tcp-highlighted[data-v-45adc427] {\n    background-color: #198754 !important;\n    border-color: #198754 !important;\n    box-shadow: 0 0 20px rgba(25, 135, 84, 0.5);\n    transform: scale(1.05);\n}\n\n/* TCP ports section highlighting */\n.tcp-ports-highlighted[data-v-45adc427] {\n    background: linear-gradient(135deg, rgba(25, 135, 84, 0.9), rgba(25, 135, 84, 0.7));\n    padding: 1rem;\n    border-radius: 0.5rem;\n    box-shadow: 0 0 25px rgba(25, 135, 84, 0.4);\n    border: 2px solid rgba(25, 135, 84, 0.6);\n}\n.tcp-ports-highlighted .port-item[data-v-45adc427] {\n    background-color: #198754 !important;\n    border-color: #198754 !important;\n    color: white !important;\n    box-shadow: 0 0 10px rgba(25, 135, 84, 0.3);\n    transform: scale(1.02);\n}\n.port-highlighted[data-v-45adc427] {\n    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2) !important;\n    transform: translateY(-2px) !important;\n}\n\n/* UDP highlighting */\n.udp-highlighted[data-v-45adc427] {\n    background-color: #ffc107 !important;\n    border-color: #ffc107 !important;\n    box-shadow: 0 0 20px rgba(255, 193, 7, 0.5);\n    transform: scale(1.05);\n}\n\n/* UDP ports section highlighting */\n.udp-ports-highlighted[data-v-45adc427] {\n    background: linear-gradient(135deg, rgba(255, 193, 7, 0.9), rgba(255, 193, 7, 0.7));\n    padding: 1rem;\n    border-radius: 0.5rem;\n    box-shadow: 0 0 25px rgba(255, 193, 7, 0.4);\n    border: 2px solid rgba(255, 193, 7, 0.6);\n}\n.udp-ports-highlighted .port-item[data-v-45adc427] {\n    background-color: #ffc107 !important;\n    border-color: #ffc107 !important;\n    color: white !important;\n    box-shadow: 0 0 10px rgba(255, 193, 7, 0.3);\n    transform: scale(1.02);\n}\n\n/* Pulsing animation for online status */\n.pulse-animation[data-v-45adc427] {\n    animation: pulse-45adc427 2s infinite;\n}\n@keyframes pulse-45adc427 {\n0% {\n        box-shadow: 0 0 0 0 rgba(25, 135, 84, 0.7);\n}\n70% {\n        box-shadow: 0 0 0 10px rgba(25, 135, 84, 0);\n}\n100% {\n        box-shadow: 0 0 0 0 rgba(25, 135, 84, 0);\n}\n}\n\n/* Custom card styling */\n.card[data-v-45adc427] {\n    transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;\n}\n\n/* Info item styling for consistent label alignment */\n.info-item[data-v-45adc427] {\n    display: flex;\n    flex-direction: column;\n}\n.info-item label[data-v-45adc427] {\n    margin-bottom: 0.25rem;\n}\n\n/* Hover effects for port stat boxes */\n.hover-highlight[data-v-45adc427] {\n    cursor: pointer;\n    transition: all 0.3s ease;\n}\n.hover-highlight[data-v-45adc427]:hover {\n    transform: translateY(-2px);\n}\n\n/* Port item styling */\n.port-item[data-v-45adc427] {\n    transition: all 0.3s ease;\n}\n";
    styleInject(css_248z$d);

    script$C.__scopeId = "data-v-45adc427";
    script$C.__file = "http_src/vue/page-asset-details-new.vue";

    const _hoisted_1$w = { class: "row" };
    const _hoisted_2$p = {
      class: "col-form-label col-sm-3 pe-0",
      id: "advanced-view"
    };
    const _hoisted_3$m = { class: "col-sm-2 ps-0" };
    const _hoisted_4$l = {
      class: "form-check form-switch mt-2",
      id: "advanced-view"
    };
    const _hoisted_5$j = {
      key: 0,
      class: "form-group ms-2 me-2 mt-2 row"
    };
    const _hoisted_6$g = { class: "row mt-4" };
    const _hoisted_7$d = { class: "col-sm-2 pe-0 me-1" };
    const _hoisted_8$c = { class: "col-sm-2 p-0 me-1" };
    const _hoisted_9$c = ["onUpdate:modelValue", "placeholder"];
    const _hoisted_10$b = { class: "col-sm-2 p-0" };
    const _hoisted_11$9 = ["onUpdate:modelValue", "placeholder"];
    const _hoisted_12$8 = {
      key: 0,
      class: "col-sm-2 p-0 ms-1"
    };
    const _hoisted_13$7 = {
      key: 1,
      class: "col-sm-2 p-0 ms-1"
    };
    const _hoisted_14$7 = ["onUpdate:modelValue", "placeholder"];
    const _hoisted_15$7 = {
      class: "btn-group m-auto align-items-center col-2",
      role: "group"
    };
    const _hoisted_16$7 = ["id", "onClick"];
    const _hoisted_17$7 = ["id", "for"];
    const _hoisted_18$6 = ["id", "onClick"];
    const _hoisted_19$6 = ["id", "for"];
    const _hoisted_20$6 = {
      key: 1,
      class: "col-sm-4 p-0 me-1"
    };
    const _hoisted_21$6 = ["onUpdate:modelValue", "placeholder"];
    const _hoisted_22$6 = { class: "col-sm-8 mt-2" };
    const _hoisted_23$6 = ["onUpdate:modelValue", "placeholder"];
    const _hoisted_24$6 = {
      key: 2,
      class: "col-sm-1 mt-2 me-4 ms-auto"
    };
    const _hoisted_25$5 = ["onClick"];
    const _hoisted_26$4 = ["data-bs-original-title"];
    const _hoisted_27$4 = { class: "form-group" };
    const _hoisted_28$3 = {
      key: 0,
      class: "me-auto w-100 text-danger"
    };
    const _hoisted_29$3 = {
      key: 1,
      class: "mt-1 notes bg-danger me-auto w-100 bg-opacity-25"
    };
    const _hoisted_30$3 = ["disabled"];


    var script$B = {
      __name: 'modal-add-acl-rule',
      props: {
      context: Object,
      url_request: String,
      l4_proto_list: Array,
      l7_proto_list: Array,
    },
      emits: ['add'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    const disable_add = ref(false);
    const rules_to_add = ref('');
    const advanced_settings_enabled = ref(false);
    const advanced_settings = ref(i18n('acl_page.advanced_settings'));
    const show_feedback = ref(false);
    const activate_add_spinner = ref(false);
    const feedback = ref('');
    const error = ref(i18n('acl_page.error_detected'));
    const title = ref(i18n('acl_page.add_acl_rule'));
    const new_rules_array = ref([]);
    const client_ip_place_holder = ref(_i18n('db_search.tags.cli_ip'));
    const client_mac_place_holder = ref(_i18n('db_search.tags.cli_mac'));
    const server_ip_place_holder = ref(_i18n('db_search.tags.srv_ip'));
    const notes_placeholder = ref(_i18n('acl_page.notes'));
    const port_place_holder = ref(_i18n('port'));

    const note_list_advanced_settings = [
      _i18n("acl_page.advanced_settings_one_rule_per_line"),
      _i18n("acl_page.advanced_settings_rule_format"),
      _i18n("acl_page.advanced_settings_l7_proto_port"),
      _i18n("acl_page.advanced_settings_select_one_parameter"),
      _i18n("acl_page.advanced_settings_parameters_separator"),
    ];

    const note_list = [
      _i18n("acl_page.edit_acl_required"),
      _i18n("acl_page.edit_acl_notes"),
      _i18n("acl_page.edit_arp_only_client"),
      _i18n("acl_page.edit_arp_tcp_udp"),
      _i18n("acl_page.all_ports"),
      _i18n("acl_page.ports_range"),
      _i18n("acl_page.add_save"),
      _i18n("acl_page.add_rule_plus"),
      _i18n("acl_page.add_rule_arp"),
      _i18n("acl_page.add_rule_advanced_rules"),
    ];

    /* ************************************** */

    const props = __props;


    /* ************************************** */

    function change_to_port_type(index) {
      new_rules_array.value[index].rule_port_type = true;
      new_rules_array.value[index].rule_application_type = false;

      const port_id = "use_port_label_" + index;
      const app_id = "use_application_label_" + index;

      $('#' + port_id).addClass('btn-primary');
      $('#' + port_id).removeClass('btn-secondary');

      $('#' + app_id).removeClass('btn-primary');
      $('#' + app_id).addClass('btn-secondary');
    }

    /* ************************************** */

    function change_to_app_type(index) {
      new_rules_array.value[index].rule_port_type = false;
      new_rules_array.value[index].rule_application_type = true;
      new_rules_array.value[index].selected_l7_proto = props.l7_proto_list[0];

      const port_id = "use_port_label_" + index;
      const app_id = "use_application_label_" + index;

      $('#' + app_id).addClass('btn-primary');
      $('#' + app_id).removeClass('btn-secondary');

      $('#' + port_id).removeClass('btn-primary');
      $('#' + port_id).addClass('btn-secondary');
    }

    /* ************************************** */

    function reset_modal_form() {
      activate_add_spinner.value = false;
      show_feedback.value = false;
      rules_to_add.value = '';
      feedback.value = '';
      new_rules_array.value = [{
        selected_l4_proto: props.l4_proto_list[1],
        selected_l7_proto: props.l7_proto_list[0],
        port: null,
        client: null,
        server: null,
        is_arp_proto: false,
        notes: null,
        rule_port_type: true,
        rule_application_type: false,
      }];
    }

    /* ************************************** */

    function change_arp(index) {
      if (new_rules_array[index].selected_l4_proto.id == 'arp') {
        new_rules_array[index].is_arp_proto = true;
      } else {
        new_rules_array[index].is_arp_proto = false;
      }
    }

    /* ************************************** */

    function remove_row(index) {
      new_rules_array.value = new_rules_array.value.filter((_, i) => i !== index);
    }

    /* ************************************** */

    function add_row() {
      new_rules_array.value.push({
        selected_l4_proto: props.l4_proto_list[1],
        selected_l7_proto: props.l7_proto_list[0],
        port: null,
        client: null,
        server: null,
        is_arp_proto: false,
        notes: null,
        rule_port_type: true,
        rule_application_type: false,
      });
    }

    /* ************************************** */

    const show = (row) => {
      reset_modal_form();
      modal_id.value.show();
    };

    /* ************************************** */

    function calculate_rules_to_add() {
      if (advanced_settings_enabled.value) {
        return rules_to_add.value
      } else {
        let new_rules = '';
        new_rules_array.value.forEach((el, index) => {
          let new_single_rule = '';
          if (!dataUtils.isEmptyOrNull(el.selected_l4_proto)) {
            new_single_rule = new_single_rule + '' + el.selected_l4_proto.label;
          }
          if (!dataUtils.isEmptyOrNull(el.client)) {
            new_single_rule = new_single_rule + ';' + el.client;
          }
          if (!dataUtils.isEmptyOrNull(el.server) && !el.is_arp_proto) {
            new_single_rule = new_single_rule + ';' + el.server;
          }
          if (!dataUtils.isEmptyOrNull(el.port) && el.rule_port_type && !el.is_arp_proto) {
            new_single_rule = new_single_rule + ';' + el.port;
          }
          if (!dataUtils.isEmptyArrayOrNull(el.selected_l7_proto) && el.rule_application_type && !el.is_arp_proto) {
            new_single_rule = new_single_rule + ';' + el.selected_l7_proto.label;
          }
          if (!dataUtils.isEmptyOrNull(el.notes)) {
            new_single_rule = new_single_rule + '#' + el.notes;
          }
          new_rules = new_rules + new_single_rule + "\n";
        });
        new_rules = new_rules.slice(0, -1);
        return new_rules
      }
    }

    /* ************************************** */

    const add_ = async () => {
      activate_add_spinner.value = true;
      const new_rules_to_add = calculate_rules_to_add();
      const params = {
        csrf: props.context.csrf,
        rules: new_rules_to_add
      };
      const rsp = await ntopng_utility.http_post_request(props.url_request, params);
      activate_add_spinner.value = false;
      if (rsp.result == 'ok') {
        show_feedback.value = false;
        emit('add', params);
        close();
      } else {
        show_feedback.value = true;
        feedback.value = rsp.result;
      }
    };

    /* ************************************** */

    const close = () => {
      modal_id.value.close();
    };

    /* ************************************** */

    onBeforeMount(() => { });

    __expose({ show, close });



    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("form", null, [
            createBaseVNode("div", _hoisted_1$w, [
              createBaseVNode("label", _hoisted_2$p, [
                createBaseVNode("b", null, toDisplayString(advanced_settings.value), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_3$m, [
                createBaseVNode("div", _hoisted_4$l, [
                  withDirectives(createBaseVNode("input", {
                    "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((advanced_settings_enabled).value = $event)),
                    name: "show_advanced_settings",
                    class: "form-check-input",
                    type: "checkbox",
                    role: "switch"
                  }, null, 512 /* NEED_PATCH */), [
                    [vModelCheckbox, advanced_settings_enabled.value]
                  ])
                ])
              ])
            ]),
            (advanced_settings_enabled.value)
              ? (openBlock(), createElementBlock("div", _hoisted_5$j, [
                  withDirectives(createBaseVNode("textarea", {
                    rows: "10",
                    cols: "10",
                    style: {"width":"100%","height":"100%"},
                    "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((rules_to_add).value = $event))
                  }, null, 512 /* NEED_PATCH */), [
                    [vModelText, rules_to_add.value]
                  ])
                ]))
              : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                  (openBlock(true), createElementBlock(Fragment, null, renderList(new_rules_array.value, (single_rule, index) => {
                    return (openBlock(), createElementBlock("div", _hoisted_6$g, [
                      createBaseVNode("div", _hoisted_7$d, [
                        createVNode(script$2H, {
                          selected_option: single_rule.selected_l4_proto,
                          "onUpdate:selected_option": $event => ((single_rule.selected_l4_proto) = $event),
                          options: props.l4_proto_list,
                          onSelectOption: $event => (change_arp(index))
                        }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options", "onSelectOption"])
                      ]),
                      (single_rule.selected_l4_proto.id !== 'arp')
                        ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                            createBaseVNode("div", _hoisted_8$c, [
                              withDirectives(createBaseVNode("input", {
                                "onUpdate:modelValue": $event => ((single_rule.client) = $event),
                                class: "form-control",
                                type: "text",
                                placeholder: client_ip_place_holder.value,
                                required: ""
                              }, null, 8 /* PROPS */, _hoisted_9$c), [
                                [vModelText, single_rule.client]
                              ])
                            ]),
                            createBaseVNode("div", _hoisted_10$b, [
                              withDirectives(createBaseVNode("input", {
                                "onUpdate:modelValue": $event => ((single_rule.server) = $event),
                                class: "form-control",
                                type: "text",
                                placeholder: server_ip_place_holder.value,
                                required: ""
                              }, null, 8 /* PROPS */, _hoisted_11$9), [
                                [vModelText, single_rule.server]
                              ])
                            ]),
                            (single_rule.selected_l4_proto.id == 6 || single_rule.selected_l4_proto.id == 17)
                              ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                                  (single_rule.rule_application_type)
                                    ? (openBlock(), createElementBlock("div", _hoisted_12$8, [
                                        createVNode(script$2H, {
                                          selected_option: single_rule.selected_l7_proto,
                                          "onUpdate:selected_option": $event => ((single_rule.selected_l7_proto) = $event),
                                          options: props.l7_proto_list
                                        }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options"])
                                      ]))
                                    : (openBlock(), createElementBlock("div", _hoisted_13$7, [
                                        withDirectives(createBaseVNode("input", {
                                          "onUpdate:modelValue": $event => ((single_rule.port) = $event),
                                          class: "form-control",
                                          type: "text",
                                          placeholder: port_place_holder.value
                                        }, null, 8 /* PROPS */, _hoisted_14$7), [
                                          [vModelText, single_rule.port]
                                        ])
                                      ])),
                                  createBaseVNode("div", _hoisted_15$7, [
                                    createBaseVNode("input", {
                                      id: 'use_port_' + index,
                                      class: "btn-check btn-primary",
                                      type: "radio",
                                      onClick: $event => (change_to_port_type(index))
                                    }, null, 8 /* PROPS */, _hoisted_16$7),
                                    createBaseVNode("label", {
                                      id: 'use_port_label_' + index,
                                      class: "btn btn-sm btn-primary",
                                      for: 'use_port_' + index
                                    }, toDisplayString(_i18n('acl_page.use_port')), 9 /* TEXT, PROPS */, _hoisted_17$7),
                                    createBaseVNode("input", {
                                      id: 'use_application_' + index,
                                      class: "btn-check btn-primary",
                                      type: "radio",
                                      onClick: $event => (change_to_app_type(index))
                                    }, null, 8 /* PROPS */, _hoisted_18$6),
                                    createBaseVNode("label", {
                                      id: 'use_application_label_' + index,
                                      class: "btn btn-sm btn-secondary",
                                      for: 'use_application_' + index
                                    }, toDisplayString(_i18n('acl_page.use_application')), 9 /* TEXT, PROPS */, _hoisted_19$6)
                                  ])
                                ], 64 /* STABLE_FRAGMENT */))
                              : createCommentVNode("v-if", true)
                          ], 64 /* STABLE_FRAGMENT */))
                        : (openBlock(), createElementBlock("div", _hoisted_20$6, [
                            withDirectives(createBaseVNode("input", {
                              "onUpdate:modelValue": $event => ((single_rule.client) = $event),
                              class: "form-control",
                              type: "text",
                              placeholder: client_mac_place_holder.value,
                              required: ""
                            }, null, 8 /* PROPS */, _hoisted_21$6), [
                              [vModelText, single_rule.client]
                            ])
                          ])),
                      createBaseVNode("div", _hoisted_22$6, [
                        withDirectives(createBaseVNode("input", {
                          "onUpdate:modelValue": $event => ((single_rule.notes) = $event),
                          class: "form-control",
                          type: "text",
                          placeholder: notes_placeholder.value
                        }, null, 8 /* PROPS */, _hoisted_23$6), [
                          [vModelText, single_rule.notes]
                        ])
                      ]),
                      (index !== 0)
                        ? (openBlock(), createElementBlock("div", _hoisted_24$6, [
                            createBaseVNode("button", {
                              class: "btn btn-danger btn-sm",
                              type: "button",
                              onClick: $event => (remove_row(index))
                            }, [
                              createBaseVNode("i", {
                                class: "fas fa fa-trash",
                                "data-bs-toggle": "tooltip",
                                "data-bs-placement": "top",
                                "data-bs-original-title": _i18n('acl_page.remove_rule')
                              }, null, 8 /* PROPS */, _hoisted_26$4)
                            ], 8 /* PROPS */, _hoisted_25$5)
                          ]))
                        : createCommentVNode("v-if", true)
                    ]))
                  }), 256 /* UNKEYED_FRAGMENT */)),
                  createBaseVNode("div", { class: "row" }, [
                    createBaseVNode("div", { class: "mt-3 ps-0" }, [
                      createBaseVNode("button", {
                        class: "btn btn-link",
                        type: "button",
                        onClick: add_row
                      }, [...(_cache[2] || (_cache[2] = [
                        createBaseVNode("i", { class: "fas fa-plus" }, null, -1 /* CACHED */)
                      ]))])
                    ])
                  ])
                ], 64 /* STABLE_FRAGMENT */))
          ]),
          createBaseVNode("div", _hoisted_27$4, [
            (!advanced_settings_enabled.value)
              ? (openBlock(), createBlock(script$2u, {
                  key: 0,
                  note_list: note_list
                }))
              : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                  (advanced_settings_enabled.value)
                    ? (openBlock(), createBlock(script$2u, {
                        key: 0,
                        note_list: note_list_advanced_settings
                      }))
                    : createCommentVNode("v-if", true)
                ], 64 /* STABLE_FRAGMENT */))
          ])
        ]),
        footer: withCtx(() => [
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_28$3, toDisplayString(error.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_29$3, toDisplayString(feedback.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createVNode(script$2v, {
              show: activate_add_spinner.value,
              size: "1rem",
              class: "me-2"
            }, null, 8 /* PROPS */, ["show"]),
            createBaseVNode("button", {
              type: "button",
              onClick: add_,
              class: "btn btn-primary",
              disabled: disable_add.value
            }, toDisplayString(_i18n('add')), 9 /* TEXT, PROPS */, _hoisted_30$3)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$B.__file = "http_src/vue/modal-add-acl-rule.vue";

    const _hoisted_1$v = { class: "row" };
    const _hoisted_2$o = { class: "col-sm-2 pe-0 me-1" };
    const _hoisted_3$l = ["placeholder"];
    const _hoisted_4$k = { class: "col-sm-2 p-0" };
    const _hoisted_5$i = ["placeholder"];
    const _hoisted_6$f = {
      key: 0,
      class: "col-sm-2 p-0 ms-1"
    };
    const _hoisted_7$c = {
      key: 1,
      class: "col-sm-2 p-0 ms-1"
    };
    const _hoisted_8$b = ["placeholder"];
    const _hoisted_9$b = {
      class: "btn-group m-auto align-items-center col-2",
      role: "group"
    };
    const _hoisted_10$a = {
      key: 1,
      class: "col-sm-4 p-0 me-1"
    };
    const _hoisted_11$8 = ["placeholder"];
    const _hoisted_12$7 = { class: "col-sm-8 mt-2" };
    const _hoisted_13$6 = ["placeholder"];
    const _hoisted_14$6 = { class: "form-group" };
    const _hoisted_15$6 = {
      key: 0,
      class: "me-auto w-100 text-danger"
    };
    const _hoisted_16$6 = {
      key: 1,
      class: "mt-1 notes bg-danger me-auto w-100 bg-opacity-25"
    };
    const _hoisted_17$6 = ["disabled"];


    var script$A = {
      __name: 'modal-edit-acl-rule',
      props: {
      context: Object,
      url_request: String,
      l4_proto_list: Array,
      l7_proto_list: Array,
    },
      emits: ['edit'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    const disable_add = ref(false);
    ref('');
    const client = ref(null);
    const server = ref(null);
    const port = ref(null);
    const client_ip_place_holder = ref(_i18n('db_search.tags.cli_ip'));
    const client_mac_place_holder = ref(_i18n('db_search.tags.cli_mac'));
    const server_ip_place_holder = ref(_i18n('db_search.tags.srv_ip'));
    const notes_placeholder = ref(_i18n('acl_page.notes'));
    const port_place_holder = ref(_i18n('port'));
    const show_feedback = ref(false);
    const activate_edit_spinner = ref(false);
    const feedback = ref('');
    const error = ref(i18n('acl_page.error_detected'));
    const title = ref(i18n('acl_page.edit_acl_rule'));
    const selected_l4_proto = ref([]);
    const selected_l7_proto = ref([]);
    const row_key = ref(null);
    const rule_port_type = ref(false);
    const rule_application_type = ref(false);
    const use_port_ref = ref(null);
    const use_application_ref = ref(null);
    const is_arp_proto = ref(false);
    const client_ref = ref(null);
    const notes = ref(null);

    const note_list = [
      _i18n("acl_page.edit_acl_required"),
      _i18n("acl_page.edit_acl_notes"),
      _i18n("acl_page.edit_arp_only_client"),
      _i18n("acl_page.edit_arp_tcp_udp"),
      _i18n("acl_page.all_ports"),
      _i18n("acl_page.ports_range"),
      _i18n("acl_page.edit_save"),
    ];

    /* ************************************** */

    const props = __props;

    /* ************************************** */

    watch(() => selected_l4_proto.value, (cur_value) => {
      if (cur_value.id == 'arp') {
        is_arp_proto.value = true;
      } else {
        is_arp_proto.value = false;
        rule_port_type.value = true;
      }
    }, { flush: 'pre' });


    /* ************************************** */

    function change_to_port_type() {
      rule_port_type.value = true;
      rule_application_type.value = false;

      use_port_ref.value.classList.add('btn-primary');
      use_port_ref.value.classList.remove('btn-secondary');

      use_application_ref.value.classList.remove('btn-primary');
      use_application_ref.value.classList.add('btn-secondary');
    }

    /* ************************************** */

    function change_to_app_type() {
      rule_port_type.value = false;
      rule_application_type.value = true;

      use_port_ref.value.classList.remove('btn-primary');
      use_port_ref.value.classList.add('btn-secondary');

      use_application_ref.value.classList.add('btn-primary');
      use_application_ref.value.classList.remove('btn-secondary');
      selected_l7_proto.value = props.l7_proto_list[0];
    }

    /* ************************************** */

    function reset_modal_form(row) {
      row_key.value = row.key;
      selected_l4_proto.value = props.l4_proto_list.find((t) => t.id == row.proto.id);
      if (row.l7_proto) {
        change_to_app_type();
        selected_l7_proto.value = props.l7_proto_list.find((t) => t.id == row.l7_proto.id);
        port.value = null;
      } else if (row.port) {
        change_to_port_type();
        port.value = row.port;
        selected_l7_proto.value = null;
      }
      client.value = row.client;
      server.value = row.server;
      activate_edit_spinner.value = false;
      show_feedback.value = false;
      feedback.value = '';
      notes.value = row.note;
    }

    /* ************************************** */

    const show = (row) => {
      reset_modal_form(row);
      modal_id.value.show();
    };

    /* ************************************** */

    const edit_ = async () => {
      let new_rule = '';
      if (!dataUtils.isEmptyOrNull(selected_l4_proto.value)) {
        new_rule = new_rule + '' + selected_l4_proto.value.label;
      }
      if (!dataUtils.isEmptyOrNull(client.value)) {
        new_rule = new_rule + ';' + client.value;
      }
      if (!dataUtils.isEmptyOrNull(server.value) && !is_arp_proto.value) {
        new_rule = new_rule + ';' + server.value;
      }
      if (!dataUtils.isEmptyOrNull(port.value) && rule_port_type.value && !is_arp_proto.value) {
        new_rule = new_rule + ';' + port.value;
      }
      if (!dataUtils.isEmptyArrayOrNull(selected_l7_proto.value) && rule_application_type.value && !is_arp_proto.value) {
        new_rule = new_rule + ';' + selected_l7_proto.value.label;
      }
      if (!dataUtils.isEmptyOrNull(notes.value)) {
        new_rule = new_rule + '#' + notes.value;
      }

      const params = {
        csrf: props.context.csrf,
        rules: new_rule,
        serial_key: row_key.value
      };

      const rsp = await ntopng_utility.http_post_request(props.url_request, params);
      activate_edit_spinner.value = false;
      if (rsp.result == 'ok') {
        show_feedback.value = false;
        emit('edit', params);
        close();
      } else {
        show_feedback.value = true;
        feedback.value = rsp.result;
      }
    };

    /* ************************************** */

    const close = () => {
      modal_id.value.close();
    };

    /* ************************************** */

    onBeforeMount(() => { });

    __expose({ show, close });



    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("form", null, [
            createBaseVNode("div", _hoisted_1$v, [
              createBaseVNode("div", _hoisted_2$o, [
                createVNode(script$2H, {
                  selected_option: selected_l4_proto.value,
                  "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_l4_proto).value = $event)),
                  options: props.l4_proto_list
                }, null, 8 /* PROPS */, ["selected_option", "options"])
              ]),
              (!is_arp_proto.value)
                ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                    createBaseVNode("div", {
                      ref_key: "client_ref",
                      ref: client_ref,
                      class: "col-sm-2 p-0 me-1"
                    }, [
                      withDirectives(createBaseVNode("input", {
                        "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((client).value = $event)),
                        class: "form-control",
                        type: "text",
                        placeholder: client_ip_place_holder.value,
                        required: ""
                      }, null, 8 /* PROPS */, _hoisted_3$l), [
                        [vModelText, client.value]
                      ])
                    ], 512 /* NEED_PATCH */),
                    createBaseVNode("div", _hoisted_4$k, [
                      withDirectives(createBaseVNode("input", {
                        "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((server).value = $event)),
                        class: "form-control",
                        type: "text",
                        placeholder: server_ip_place_holder.value,
                        required: ""
                      }, null, 8 /* PROPS */, _hoisted_5$i), [
                        [vModelText, server.value]
                      ])
                    ]),
                    (selected_l4_proto.value.id == 6 || selected_l4_proto.value.id == 17)
                      ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                          (rule_application_type.value)
                            ? (openBlock(), createElementBlock("div", _hoisted_6$f, [
                                createVNode(script$2H, {
                                  selected_option: selected_l7_proto.value,
                                  "onUpdate:selected_option": _cache[3] || (_cache[3] = $event => ((selected_l7_proto).value = $event)),
                                  options: props.l7_proto_list
                                }, null, 8 /* PROPS */, ["selected_option", "options"])
                              ]))
                            : (openBlock(), createElementBlock("div", _hoisted_7$c, [
                                withDirectives(createBaseVNode("input", {
                                  "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((port).value = $event)),
                                  class: "form-control",
                                  type: "text",
                                  placeholder: port_place_holder.value
                                }, null, 8 /* PROPS */, _hoisted_8$b), [
                                  [vModelText, port.value]
                                ])
                              ])),
                          createBaseVNode("div", _hoisted_9$b, [
                            createBaseVNode("input", {
                              id: "use_port",
                              class: "btn-check btn-primary",
                              type: "radio",
                              onClick: change_to_port_type
                            }),
                            createBaseVNode("label", {
                              ref_key: "use_port_ref",
                              ref: use_port_ref,
                              class: "btn btn-sm btn-primary",
                              for: "use_port"
                            }, toDisplayString(_i18n('acl_page.use_port')), 513 /* TEXT, NEED_PATCH */),
                            createBaseVNode("input", {
                              id: "use_application",
                              class: "btn-check btn-primary",
                              type: "radio",
                              onClick: change_to_app_type
                            }),
                            createBaseVNode("label", {
                              ref_key: "use_application_ref",
                              ref: use_application_ref,
                              class: "btn btn-sm btn-secondary",
                              for: "use_application"
                            }, toDisplayString(_i18n('acl_page.use_application')), 513 /* TEXT, NEED_PATCH */)
                          ])
                        ], 64 /* STABLE_FRAGMENT */))
                      : createCommentVNode("v-if", true)
                  ], 64 /* STABLE_FRAGMENT */))
                : (openBlock(), createElementBlock("div", _hoisted_10$a, [
                    withDirectives(createBaseVNode("input", {
                      "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((client).value = $event)),
                      class: "form-control",
                      type: "text",
                      placeholder: client_mac_place_holder.value,
                      required: ""
                    }, null, 8 /* PROPS */, _hoisted_11$8), [
                      [vModelText, client.value]
                    ])
                  ])),
              createBaseVNode("div", _hoisted_12$7, [
                withDirectives(createBaseVNode("input", {
                  "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((notes).value = $event)),
                  class: "form-control",
                  type: "text",
                  placeholder: notes_placeholder.value
                }, null, 8 /* PROPS */, _hoisted_13$6), [
                  [vModelText, notes.value]
                ])
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_14$6, [
            createVNode(script$2u, { note_list: note_list })
          ])
        ]),
        footer: withCtx(() => [
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_15$6, toDisplayString(error.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_16$6, toDisplayString(feedback.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createVNode(script$2v, {
              show: activate_edit_spinner.value,
              size: "1rem",
              class: "me-2"
            }, null, 8 /* PROPS */, ["show"]),
            createBaseVNode("button", {
              type: "button",
              onClick: edit_,
              class: "btn btn-primary",
              disabled: disable_add.value
            }, toDisplayString(_i18n('edit')), 9 /* TEXT, PROPS */, _hoisted_17$6)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$A.__file = "http_src/vue/modal-edit-acl-rule.vue";

    const _hoisted_1$u = {
      key: 0,
      class: "me-auto w-100 text-danger"
    };
    const _hoisted_2$n = {
      key: 1,
      class: "mt-1 notes bg-danger me-auto w-100 bg-opacity-25"
    };


    var script$z = {
      __name: 'modal-delete-acl-rule',
      props: {
      context: Object,
    },
      emits: ["delete_rule"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);
    const message = ref(i18n('acl_page.delete_confirmation'));
    const emit = __emit;
    const row_key = ref({});
    const show_feedback = ref(null);
    const feedback = ref(null);
    const props = __props;

    onMounted(() => { });

    async function delete_rule() {
      const url = `${http_prefix}/lua/pro/rest/v2/delete/system/access_control_list.lua`;
      const params = {
        csrf: props.context.csrf,
        serial_key: row_key.value,
      };

      let headers = {
        'Content-Type': 'application/json'
      };
      const rsp = await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
      if (rsp.result == 'ok') {
        show_feedback.value = false;
        emit('delete_rule', params);
        close();
      } else {
        show_feedback.value = true;
        feedback.value = rsp.result;
      }
    }

    const show = (row) => {
      row_key.value = row.key;
      message.value = i18n('acl_page.delete_confirmation') + ": " + (row.rule || "") + "?";
      modal_id.value.show();
    };

    const close = () => {
      modal_id.value.close();
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("acl_page.delete_acl_rule")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(message.value), 1 /* TEXT */)
        ]),
        footer: withCtx(() => [
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_1$u, toDisplayString(_ctx.error), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_2$n, toDisplayString(feedback.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("button", {
            type: "button",
            onClick: delete_rule,
            class: "btn btn-danger"
          }, toDisplayString(_i18n("delete")), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$z.__file = "http_src/vue/modal-delete-acl-rule.vue";

    const _hoisted_1$t = {
      key: 0,
      class: "me-auto w-100 text-danger"
    };
    const _hoisted_2$m = {
      key: 1,
      class: "mt-1 notes bg-danger me-auto w-100 bg-opacity-25"
    };


    var script$y = {
      __name: 'modal-delete-all-acl-rules',
      props: {
      context: Object,
    },
      emits: ["delete_rules"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);
    const message = ref(i18n('acl_page.delete_all_confirmation'));
    const emit = __emit;
    const show_feedback = ref(null);
    const feedback = ref(null);
    const props = __props;

    onMounted(() => { });

    async function delete_host() {
      const url = `${http_prefix}/lua/pro/rest/v2/delete/system/access_control_list_all.lua`;
      const params = {
        csrf: props.context.csrf,
      };

      let headers = {
        'Content-Type': 'application/json'
      };
      const rsp = await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
      if (rsp.result == 'ok') {
        show_feedback.value = false;
        emit('delete_rules', params);
        close();
      } else {
        show_feedback.value = true;
        feedback.value = rsp.result;
      }
    }

    const show = () => {
      modal_id.value.show();
    };

    const close = () => {
      modal_id.value.close();
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("acl_page.delete_all_rules")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(message.value), 1 /* TEXT */)
        ]),
        footer: withCtx(() => [
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_1$t, toDisplayString(_ctx.error), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_2$m, toDisplayString(feedback.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("button", {
            type: "button",
            onClick: delete_host,
            class: "btn btn-danger"
          }, toDisplayString(_i18n("delete")), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$y.__file = "http_src/vue/modal-delete-all-acl-rules.vue";

    const _hoisted_1$s = { class: "m-2 mb-3" };
    const _hoisted_2$l = ["title"];
    const _hoisted_3$k = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_4$j = { class: "no-wrap d-flex align-items-center my-auto me-2 filters-label" };
    const _hoisted_5$h = { class: "d-flex justify-content-center align-items-center" };
    const _hoisted_6$e = { class: "card-footer mt-3" };

    const url_add_request = '/lua/pro/rest/v2/add/system/access_control_list_rules.lua';
    const url_edit_request = '/lua/pro/rest/v2/add/system/edit_access_control_list_rule.lua';

    var script$x = {
      __name: 'page-access-control-list',
      props: {
      context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const table_access_control_list = ref(null);
    const modal_add = ref(null);
    const modal_edit = ref(null);
    const modal_delete = ref(null);
    const modal_delete_all = ref(null);
    const filter_table_array = ref([]);
    const error_message = i18n('acl_page.check_disabled') + " <a href='" + http_prefix + "/lua/admin/edit_configset.lua?subdir=all#disabled'><i class='fas fa-cog fa-sm'></i></a>";
    const table_id = ref('access_control_list');
    const date_format = ref(null);
    const l4_proto = ref([]);
    const l7_proto = ref([]);
    const notes = [
      _i18n("acl_page.acl_use"),
      _i18n("acl_page.non_blocking_rules"),
      _i18n("acl_page.add_new_rule"),
      _i18n("acl_page.action_column"),
      _i18n("acl_page.action_delete_all_rules")
    ];

    /* ************************************** */

    const props = __props;

    /* ******************************************************************** */

    /* Function to add a new host to scan */
    function add_rule() {
      if (props.context.host != null && props.context.host != "")
        modal_add.value.show(null, props.context.host);
      else modal_add.value.show();
    }

    /* ************************************** */

    const map_table_def_columns = (columns) => {
      let map_columns = {
        "proto": (value, row) => {
          if (value)
            return value.name

          return ""
        },
        "l7_proto": (value, row) => {
          if (value)
            return value.name

          return ""
        },
        "client": (value, row) => {
          return value
        },
        "server": (value, row) => {
          return value
        },
        "port": (value, row) => {
          return value
        },
        "creation_timestamp": (value, row) => {
          if (value > 0) {
            return formatterUtils.formatDateTime(value)
          }
          return ''
        }
      };

      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];
        if (c.id == "actions") {
          const visible_dict = {
            edit_rule: true,
            delete_rule: true,
          };
          c.button_def_array.forEach((b) => {
            if (!visible_dict[b.id]) {
              b.class.push("disabled");
            }
          });
        }
      });

      return columns;
    };

    /* ************************************** */

    function column_data(col, row) {
      return row[col.data.data_field];
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
      if (col != null) {
        let r0_col = column_data(col, r0);
        let r1_col = column_data(col, r1);

        /* In case the values are the same, sort by IP */
        if (col.id == "proto") {
          let val0 = r0_col;
          let val1 = r1_col;
          if (r0_col) val0 = r0_col.name;
          if (r1_col) val1 = r1_col.name;
          return sortingFunctions$1.sortByName(val0, val1, col.sort);
        } else if (col.id == "l7_proto") {
          let val0 = r0_col;
          let val1 = r1_col;
          if (r0_col) val0 = r0_col.name;
          if (r1_col) val1 = r1_col.name;
          return sortingFunctions$1.sortByName(val0, val1, col.sort);
        } else if (col.id == "client") {
          return sortingFunctions$1.sortByIP(r0_col, r1_col, col.sort);
        } else if (col.id == "server") {
          return sortingFunctions$1.sortByIP(r0_col, r1_col, col.sort);
        } else if (col.id == "port") {
          const lower_value = 0;
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
        } else if (col.id == "creation_timestamp") {
          const lower_value = 0;
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
        } else if (col.id == "annotation") {
          return sortingFunctions$1.sortByName((r0_col || ""), (r1_col || ""), col.sort);
        }
      }
    }

    /* ************************************** */

    /* This function is used to load the filters for the table */
    async function load_table_filters_array() {
      let extra_params = get_extra_params_obj();
      let url_params = ntopng_url_manager.obj_to_url_params(extra_params);
      const url = `${http_prefix}/lua/pro/rest/v2/get/system/acl_filters.lua?${url_params}`;
      // Get the filters
      let res = await ntopng_utility.http_request(url);

      // Load them as an array, that is going to update automatically the filters
      return res.map((t) => {
        const key_in_url = ntopng_url_manager.get_url_entry(t.name);
        if (dataUtils.isEmptyOrNull(key_in_url)) {
          ntopng_url_manager.set_key_to_url(t.name, ``);
        }
        return {
          id: t.name,
          label: t.label,
          title: t.tooltip,
          options: t.value,
          hidden: (t.value.length == 1)
        };
      });
    }

    /* ************************************** */

    function reset_filters() {
      filter_table_array.value.forEach((el, index) => {
        /* Getting the currently selected filter */
        ntopng_url_manager.set_key_to_url(el.id, ``);
      });
      load_table_filters_array();
      refresh_table();
    }

    /* ************************************** */

    /* This function is automatically called whenever a filter is set, going to update the
     * filter label and refresh the table
     */
    async function add_table_filter(opt) {
      ntopng_url_manager.set_key_to_url(opt.key, `${opt.value}`);
      refresh_table();
      filter_table_array.value = await load_table_filters_array();
      set_filter_array_label();
    }

    /* ************************************** */

    /* This function is going to set the label shown on the dropdown */
    function set_filter_array_label() {
      filter_table_array.value.forEach((el, index) => {
        /* Setting the basic label */
        if (el.basic_label == null) {
          el.basic_label = el.label;
        }

        /* Getting the currently selected filter */
        const url_entry = ntopng_url_manager.get_url_entry(el.id);
        el.options.forEach((option) => {
          if (option.value.toString() === url_entry) {
            el.current_option = option;
          }
        });
      });
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager.get_url_object();
      return extra_params;
    };

    /* ************************************** */

    function click_button_edit_rule(event) {
      const row = event.row;
      modal_edit.value.show(row);
    }

    /* ************************************** */

    function click_button_delete_rule(event) {
      const row = event.row;
      modal_delete.value.show(row);
    }

    /* ************************************** */

    function delete_all_rules() {
      modal_delete_all.value.show();
    }

    /* ************************************** */

    function export_rules() {
      const url = `${http_prefix}/lua/pro/rest/v2/get/system/access_control_list.lua?`;
      const params = ntopng_url_manager.get_url_params();
      window.open(url + params + '&download=true');
    }

    /* ************************************** */

    function on_table_custom_event(event) {
      let events_managed = {
        "click_button_edit_rule": click_button_edit_rule,
        "click_button_delete_rule": click_button_delete_rule,
      };
      if (events_managed[event.event_id] == null) {
        return;
      }
      events_managed[event.event_id](event);
    }

    /* ************************************** */

    async function load_protocols() {
      let url = `${http_prefix}/lua/rest/v2/get/l4/protocol/consts.lua`;
      let rsp = await ntopng_utility.http_request(url);
      l4_proto.value = rsp.map((t) => {
        return {
          id: t.id,
          label: t.name,
          title: t.name,
        };
      });
      /* *** Special case *** */
      l4_proto.value.unshift({
        id: 'arp',
        label: 'ARP',
        title: 'ARP',
      });
      url = `${http_prefix}/lua/rest/v2/get/l7/application/consts.lua`;
      rsp = await ntopng_utility.http_request(url);
      l7_proto.value = rsp.map((t) => {
        return {
          id: t.appl_id,
          label: t.name,
          title: t.name,
        };
      });
    }

    /* ************************************** */

    function refresh_table() {
      table_access_control_list.value.refresh_table(true);
    }

    onBeforeMount(async () => {
      date_format.value = await ntopng_utility.get_date_format(false, props.context.csrf, http_prefix);
    });

    /* ************************************** */

    onMounted(async () => {
      filter_table_array.value = await load_table_filters_array();
      set_filter_array_label();
      await load_protocols();
    });

    /* ************************************** */


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$s, [
          ((!props.context.is_check_enabled))
            ? (openBlock(), createElementBlock("div", {
                key: 0,
                class: "alert alert-warning",
                role: "alert",
                id: "error-alert",
                innerHTML: error_message
              }))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", {
            class: normalizeClass([(!props.context.is_check_enabled) ? 'ntopng-gray-out' : ''])
          }, [
            createVNode(script$2w, {
              ref_key: "table_access_control_list",
              ref: table_access_control_list,
              table_id: table_id.value,
              csrf: __props.context.csrf,
              f_map_columns: map_table_def_columns,
              get_extra_params_obj: get_extra_params_obj,
              f_sort_rows: columns_sorting,
              onCustom_event: on_table_custom_event
            }, {
              custom_buttons: withCtx(() => [
                createBaseVNode("button", {
                  class: "btn btn-link",
                  type: "button",
                  onClick: add_rule
                }, [
                  createBaseVNode("i", {
                    class: "fas fa-plus",
                    "data-bs-toggle": "tooltip",
                    "data-bs-placement": "top",
                    title: _i18n('policy.add_rule')
                  }, null, 8 /* PROPS */, _hoisted_2$l)
                ])
              ]),
              custom_header: withCtx(() => [
                (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
                  return (openBlock(), createElementBlock("div", _hoisted_3$k, [
                    createBaseVNode("span", _hoisted_4$j, [
                      createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                    ]),
                    createVNode(script$2H, {
                      selected_option: item['current_option'],
                      "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                      theme: "bootstrap-5",
                      dropdown_size: "small",
                      options: item['options'],
                      onSelect_option: add_table_filter
                    }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options"])
                  ]))
                }), 256 /* UNKEYED_FRAGMENT */)),
                createBaseVNode("div", _hoisted_5$h, [
                  createBaseVNode("div", {
                    class: "btn btn-sm btn-primary mt-2 me-3",
                    type: "button",
                    onClick: reset_filters
                  }, toDisplayString(_i18n('reset')), 1 /* TEXT */)
                ])
              ]),
              _: 1 /* STABLE */
            }, 8 /* PROPS */, ["table_id", "csrf"]),
            createBaseVNode("div", _hoisted_6$e, [
              createBaseVNode("button", {
                type: "button",
                ref: "delete_all_rules",
                onClick: delete_all_rules,
                class: "btn btn-danger"
              }, [
                _cache[0] || (_cache[0] = createBaseVNode("i", { class: "fas fa-trash" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString(_i18n("acl_page.delete_all_rules")), 1 /* TEXT */)
              ], 512 /* NEED_PATCH */),
              createBaseVNode("button", {
                type: "button",
                ref: "export_rules",
                onClick: export_rules,
                class: "btn btn-primary ms-1"
              }, [
                _cache[1] || (_cache[1] = createBaseVNode("i", { class: "fas fa-file-export" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString(_i18n("acl_page.export_rules")), 1 /* TEXT */)
              ], 512 /* NEED_PATCH */)
            ]),
            createVNode(script$2u, { note_list: notes })
          ], 2 /* CLASS */)
        ]),
        createVNode(script$B, {
          ref_key: "modal_add",
          ref: modal_add,
          context: __props.context,
          url_request: url_add_request,
          l4_proto_list: l4_proto.value,
          l7_proto_list: l7_proto.value,
          onAdd: refresh_table
        }, null, 8 /* PROPS */, ["context", "l4_proto_list", "l7_proto_list"]),
        createVNode(script$A, {
          ref_key: "modal_edit",
          ref: modal_edit,
          context: __props.context,
          url_request: url_edit_request,
          l4_proto_list: l4_proto.value,
          l7_proto_list: l7_proto.value,
          onEdit: refresh_table
        }, null, 8 /* PROPS */, ["context", "l4_proto_list", "l7_proto_list"]),
        createVNode(script$z, {
          ref_key: "modal_delete",
          ref: modal_delete,
          context: __props.context,
          onDelete_rule: refresh_table
        }, null, 8 /* PROPS */, ["context"]),
        createVNode(script$y, {
          ref_key: "modal_delete_all",
          ref: modal_delete_all,
          context: __props.context,
          onDelete_rules: refresh_table
        }, null, 8 /* PROPS */, ["context"]),
        createCommentVNode("\n  <ModalDeleteInactiveHost ref=\"modal_delete\" :context=\"context\" @delete_host=\"refresh_table\"></ModalDeleteInactiveHost>\n  <ModalDeleteInactiveHostEpoch ref=\"modal_delete_older\" :context=\"context\" @delete_host=\"refresh_table\">\n  </ModalDeleteInactiveHostEpoch>\n  <ModalDownloadInactiveHost ref=\"modal_download\" :context=\"context\"></ModalDownloadInactiveHost>\n")
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$x.__file = "http_src/vue/page-access-control-list.vue";

    const _hoisted_1$r = { class: "m-2 mb-3" };
    const _hoisted_2$k = {
      class: "row ms-4",
      id: "interface_applications"
    };
    const _hoisted_3$j = { class: "col-6 widget-box row" };
    const _hoisted_4$i = { class: "widget-name" };
    const _hoisted_5$g = { class: "col-6" };
    const _hoisted_6$d = { class: "widget-name" };
    const _hoisted_7$b = { class: "col-6 widget-box row" };
    const _hoisted_8$a = { class: "widget-name" };
    const _hoisted_9$a = { class: "col-6" };
    const _hoisted_10$9 = { class: "widget-name" };


    var script$w = {
      __name: 'page-top-interface-applications',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    /* ************************************** */

    const props = __props;

    /* ************************************** */

    const table_id = ref('top_interface_applications');
    const table_top_app = ref([]);
    const recreate_table = ref(false);
    const chart_options_since_startup = ref([
        {
            title: i18n('top_l7_proto'),
            type: ntopChartApex.typeChart.DONUT,
            url: `${http_prefix}/lua/rest/v2/get/interface/l7/stats.lua`,
            extra_params: { ndpistats_mode: "sinceStartup", ifid: props.context.ifid },
            id: `top_applications_since_startup`,
        },
        {
            title: i18n('breed_title'),
            type: ntopChartApex.typeChart.DONUT,
            url: `${http_prefix}/lua/rest/v2/get/interface/l7/stats.lua`,
            extra_params: { breed: "true", ndpistats_mode: "sinceStartup", ifid: props.context.ifid },
            id: `top_breed_since_startup`,
        }]);
    const chart_options_live = ref([
        {
            title: i18n('top_l7_proto'),
            type: ntopChartApex.typeChart.DONUT,
            url: `${http_prefix}/lua/rest/v2/get/interface/l7/stats.lua`,
            extra_params: { breed: "true", ndpistats_mode: "count", ifid: props.context.ifid },
            id: `top_breed_live`,
        },
        {
            title: i18n('graphs.tcp_flags'),
            type: ntopChartApex.typeChart.DONUT,
            url: `${http_prefix}/lua/rest/v2/get/interface/tcp_stats.lua`,
            extra_params: {},
            id: `top_breed_live`,
        }]);

    /* ************************************** */

    function column_data(col, row) {
        return row[col.data.data_field];
    }

    /* ************************************** */

    function get_params_url_request(status) {
        if (status.id) {
            let array_element = chart_options_since_startup.value.find((el) => el.id === status.id);
            if (!array_element) array_element = chart_options_live.value.find((el) => el.id === status.id);
            const params = array_element.extra_params;
            return ntopng_url_manager$1.obj_to_url_params(params);
        }
        return "";
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            let r0_col = column_data(col, r0);
            let r1_col = column_data(col, r1);

            /* In case the values are the same, sort by IP */
            if (col.id == "application") {
                let val0 = r0_col;
                let val1 = r1_col;
                if (r0_col) val0 = r0_col.name;
                if (r1_col) val1 = r1_col.name;
                return sortingFunctions$1.sortByName(val0, val1, col.sort);
            } else if (col.id == "breed") {
                return sortingFunctions$1.sortByName(r0_col, r1_col, col.sort);
            } else if (col.id == "total_bytes") {
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0.bytes.total, r1.bytes.total, col.sort);
            } else if (col.id == "total_packets") {
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0.packets.total, r1.packets.total, col.sort);
            } else if (col.id == "bytes_sent") {
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0.bytes.sent, r1.bytes.sent, col.sort);
            } else if (col.id == "bytes_rcvd") {
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0.bytes.rcvd, r1.bytes.rcvd, col.sort);
            }
        }
    }

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        const bytes_formatter = formatterUtils.getFormatter("bytes");
        const packets_formatter = NtopUtils$1.formatPackets;
        let map_columns = {
            "application": (value, row) => {
                return value.name
            },
            "breed": (value, row) => {
                return value
            },
            "total_bytes": (value, row) => {
                return bytes_formatter(row.bytes.total)
            },
            "total_packets": (value, row) => {
                return packets_formatter(row.packets.total)
            },
            "bytes_sent": (value, row) => {
                return bytes_formatter(row.bytes.sent)
            },
            "bytes_rcvd": (value, row) => {
                return bytes_formatter(row.bytes.rcvd)
            },
            "percentage": (value, row) => {
                return NtopUtils$1.createProgressBar(row.bytes.percentage)
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    historical_flows: props.context.historical_available,
                    live_flows: true,
                    timeseries: props.context.l7_timeseries_enabled,
                };
                c.button_def_array.forEach((b) => {
                    if (!visible_dict[b.id]) {
                        b.class.push("disabled");
                    }
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function create_historical_flows_url_link(row) {
        const epoch_begin = Math.floor(new Date().getTime() / 1000) - props.context.uptime;
        return `${http_prefix}/lua/pro/db_search.lua?ifid=${props.context.ifid}&l7_proto=${row.application.id};eq&epoch_begin=${epoch_begin}&epoch_end=${Math.floor(new Date().getTime() / 1000)}`
    }

    /* ************************************** */

    function create_live_flows_url_link(row) {
        return `${http_prefix}/lua/flows_stats.lua?ifid=${props.context.ifid}&application=${row.application.id}`
    }

    /* ************************************** */

    function create_timeseries_url_link(row) {
        const epoch_begin = Math.floor(new Date().getTime() / 1000) - props.context.uptime;
        const url = `${http_prefix}/lua/if_stats.lua?ifid=${props.context.ifid}&page=historical&epoch_begin=${epoch_begin}&epoch_end=${Math.floor(new Date().getTime() / 1000)}&ts_schema=top:${props.context.iface_ts_id}&ts_query=ifid:${props.context.ifid},protocol:${row.application.name}`;
        return url;
    }

    /* ************************************** */

    function click_button_historical_flows(event) {
        const row = event.row;
        window.open(create_historical_flows_url_link(row), "_self");
    }

    /* ************************************** */

    function click_button_live_flows(event) {
        const row = event.row;
        window.open(create_live_flows_url_link(row), "_self");
    }

    /* ************************************** */

    function click_button_timeseries(event) {
        const row = event.row;
        window.open(create_timeseries_url_link(row), "_self");
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_historical_flows": click_button_historical_flows,
            "click_button_live_flows": click_button_live_flows,
            "click_button_timeseries": click_button_timeseries,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    onMounted(async () => { });

    /* ************************************** */


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$r, [
        createBaseVNode("div", _hoisted_2$k, [
          createBaseVNode("div", _hoisted_3$j, [
            createBaseVNode("h3", _hoisted_4$i, toDisplayString(_i18n('since_startup')), 1 /* TEXT */),
            (openBlock(true), createElementBlock(Fragment, null, renderList(chart_options_since_startup.value, (chart_option) => {
              return (openBlock(), createElementBlock("div", _hoisted_5$g, [
                createBaseVNode("h5", _hoisted_6$d, toDisplayString(chart_option.title), 1 /* TEXT */),
                createVNode(script$2I, {
                  id: chart_option.id,
                  chart_type: chart_option.type,
                  get_params_url_request: get_params_url_request,
                  base_url_request: chart_option.url,
                  register_on_status_change: false
                }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"])
              ]))
            }), 256 /* UNKEYED_FRAGMENT */))
          ]),
          createBaseVNode("div", _hoisted_7$b, [
            createBaseVNode("h3", _hoisted_8$a, toDisplayString(_i18n('active_flows')), 1 /* TEXT */),
            (openBlock(true), createElementBlock(Fragment, null, renderList(chart_options_live.value, (chart_option) => {
              return (openBlock(), createElementBlock("div", _hoisted_9$a, [
                createBaseVNode("h5", _hoisted_10$9, toDisplayString(chart_option.title), 1 /* TEXT */),
                createVNode(script$2I, {
                  id: chart_option.id,
                  chart_type: chart_option.type,
                  get_params_url_request: get_params_url_request,
                  base_url_request: chart_option.url,
                  register_on_status_change: false
                }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"])
              ]))
            }), 256 /* UNKEYED_FRAGMENT */))
          ])
        ]),
        (openBlock(), createBlock(script$2w, {
          key: recreate_table.value,
          ref_key: "table_top_app",
          ref: table_top_app,
          table_id: table_id.value,
          csrf: __props.context.csrf,
          f_map_columns: map_table_def_columns,
          get_extra_params_obj: get_extra_params_obj,
          f_sort_rows: columns_sorting,
          onCustom_event: on_table_custom_event
        }, null, 8 /* PROPS */, ["table_id", "csrf"]))
      ]))
    }
    }

    };

    script$w.__file = "http_src/vue/page-top-interface-applications.vue";

    const _hoisted_1$q = { class: "m-2 mb-3" };
    const _hoisted_2$j = {
      class: "row ms-3",
      id: "interface_categories"
    };
    const _hoisted_3$i = { class: "col-12 widget-box row" };
    const _hoisted_4$h = { class: "widget-name" };
    const _hoisted_5$f = { class: "col-12" };
    const _hoisted_6$c = { class: "widget-name" };


    var script$v = {
      __name: 'page-top-interface-categories',
      props: {
      context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    /* ************************************** */

    const props = __props;

    /* ************************************** */

    const table_id = ref('top_interface_categories');
    const table_top_cat = ref([]);
    const recreate_table = ref(false);
    const chart_options_since_startup = ref([
      {
        title: i18n('top_l7_cat'),
        type: ntopChartApex.typeChart.DONUT,
        url: `${http_prefix}/lua/rest/v2/get/interface/l7/stats.lua`,
        extra_params: { ndpi_category: true, ndpistats_mode: "sinceStartup", ifid: props.context.ifid },
        tab: `applications`,
        id: `top_l7_categories`,
      }]);

    /* ************************************** */

    function column_data(col, row) {
      return row[col.data.data_field];
    }

    /* ************************************** */

    function get_params_url_request(status) {
      if (status.id) {
        let array_element = chart_options_since_startup.value.find((el) => el.id === status.id);
        if (!array_element) array_element = chart_options_live.value.find((el) => el.id === status.id);
        const params = array_element.extra_params;
        return ntopng_url_manager$1.obj_to_url_params(params);
      }
      return "";
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
      if (col != null) {
        let r0_col = column_data(col, r0);
        let r1_col = column_data(col, r1);

        /* In case the values are the same, sort by IP */
        if (col.id == "category") {
          let val0 = r0_col;
          let val1 = r1_col;
          if (r0_col) val0 = r0_col.name;
          if (r1_col) val1 = r1_col.name;
          return sortingFunctions$1.sortByName(val0, val1, col.sort);
        } else if (col.id == "total_bytes") {
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0.bytes.total, r1.bytes.total, col.sort);
        } else if (col.id == "bytes_sent") {
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0.bytes.sent, r1.bytes.sent, col.sort);
        } else if (col.id == "bytes_rcvd") {
          return sortingFunctions$1.sortByNumberWithNormalizationValue(r0.bytes.rcvd, r1.bytes.rcvd, col.sort);
        }
      }
    }

    /* ************************************** */

    const map_table_def_columns = (columns) => {
      const bytes_formatter = formatterUtils.getFormatter("bytes");
      let map_columns = {
        "category": (value, row) => {
          return value.name
        },
        "applications_list": (value, row) => {
          return value
        },
        "total_bytes": (value, row) => {
          return bytes_formatter(row.bytes.total)
        },
        "bytes_sent": (value, row) => {
          return bytes_formatter(row.bytes.sent)
        },
        "bytes_rcvd": (value, row) => {
          return bytes_formatter(row.bytes.rcvd)
        },
        "percentage": (value, row) => {
          return NtopUtils$1.createProgressBar(row.bytes.percentage)
        }
      };

      columns.forEach((c) => {
        c.render_func = map_columns[c.data_field];
        if (c.id == "actions") {
          const visible_dict = {
            historical_flows: props.context.historical_available,
            live_flows: true,
            timeseries: props.context.l7_timeseries_enabled,
          };
          c.button_def_array.forEach((b) => {
            if (!visible_dict[b.id]) {
              b.class.push("disabled");
            }
          });
        }
      });

      return columns;
    };

    /* ************************************** */

    const get_extra_params_obj = () => {
      let extra_params = ntopng_url_manager$1.get_url_object();
      return extra_params;
    };

    /* ************************************** */

    function create_historical_flows_url_link(row) {
      const epoch_begin = Math.floor(new Date().getTime() / 1000) - props.context.uptime;
      return `${http_prefix}/lua/pro/db_search.lua?ifid=${props.context.ifid}&l7cat=${row.category.id};eq&epoch_begin=${epoch_begin}&epoch_end=${Math.floor(new Date().getTime() / 1000)}`
    }

    /* ************************************** */

    function create_live_flows_url_link(row) {
      return `${http_prefix}/lua/flows_stats.lua?ifid=${props.context.ifid}&application=cat_${row.category.id}`
    }

    /* ************************************** */

    function create_timeseries_url_link(row) {
      const epoch_begin = Math.floor(new Date().getTime() / 1000) - props.context.uptime;
      return `${http_prefix}/lua/if_stats.lua?ifid=${props.context.ifid}&page=historical&epoch_begin=${epoch_begin}&epoch_end=${Math.floor(new Date().getTime() / 1000)}&timeseries_groups_mode=1_chart_x_metric&timeseries_groups=interface;${props.context.ifid};top:iface:ndpi_categories%2bcategory:${row.category.name};bytes%3Dtrue:true:false:false`
    }

    /* ************************************** */

    function click_button_historical_flows(event) {
      const row = event.row;
      window.open(create_historical_flows_url_link(row), "_self");
    }

    /* ************************************** */

    function click_button_live_flows(event) {
      const row = event.row;
      window.open(create_live_flows_url_link(row), "_self");
    }

    /* ************************************** */

    function click_button_timeseries(event) {
      const row = event.row;
      window.open(create_timeseries_url_link(row), "_self");
    }

    /* ************************************** */

    function on_table_custom_event(event) {
      let events_managed = {
        "click_button_historical_flows": click_button_historical_flows,
        "click_button_live_flows": click_button_live_flows,
        "click_button_timeseries": click_button_timeseries,
      };
      if (events_managed[event.event_id] == null) {
        return;
      }
      events_managed[event.event_id](event);
    }

    /* ************************************** */

    onMounted(async () => { });

    /* ************************************** */


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$q, [
        createBaseVNode("div", _hoisted_2$j, [
          createBaseVNode("div", _hoisted_3$i, [
            createBaseVNode("h3", _hoisted_4$h, toDisplayString(_i18n('since_startup')), 1 /* TEXT */),
            (openBlock(true), createElementBlock(Fragment, null, renderList(chart_options_since_startup.value, (chart_option) => {
              return (openBlock(), createElementBlock("div", _hoisted_5$f, [
                createBaseVNode("h5", _hoisted_6$c, toDisplayString(chart_option.title), 1 /* TEXT */),
                createVNode(script$2I, {
                  id: chart_option.id,
                  chart_type: chart_option.type,
                  get_params_url_request: get_params_url_request,
                  base_url_request: chart_option.url,
                  register_on_status_change: false
                }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"])
              ]))
            }), 256 /* UNKEYED_FRAGMENT */))
          ])
        ]),
        (openBlock(), createBlock(script$2w, {
          key: recreate_table.value,
          ref_key: "table_top_cat",
          ref: table_top_cat,
          table_id: table_id.value,
          csrf: __props.context.csrf,
          f_map_columns: map_table_def_columns,
          get_extra_params_obj: get_extra_params_obj,
          f_sort_rows: columns_sorting,
          onCustom_event: on_table_custom_event
        }, null, 8 /* PROPS */, ["table_id", "csrf"]))
      ]))
    }
    }

    };

    script$v.__file = "http_src/vue/page-top-interface-categories.vue";

    const _hoisted_1$p = { class: "m-2 mb-3" };

    var script$u = {
      __name: 'page-observation-points',
      props: {
        context: Object
    },
      setup(__props) {

    const props = __props;

    const note_list = ref([
        i18n("flow_devices.note_observation_points_fields1").replace("%{url}", `${http_prefix}/lua/admin/prefs.lua?tab=on_disk_ts`),
        i18n("flow_devices.note_observation_points_fields2").replace("%{url}", `https://www.ntop.org/guides/ntopng/using_with_other_tools/nprobe.html#observation-points`),
        i18n("flow_devices.note_observation_points_fields3")
    ]);
    ref(null);
    const table_id = ref('observation_points');
    const table_observation_points = ref(null);
    const csrf = props.context.csrf;

    const observation_point_config_url = `${http_prefix}/lua/pro/enterprise/observation_points.lua?page=config&observation_point=`;
    const exporters_url = `${http_prefix}/lua/pro/enterprise/flowdevices_list.lua?obs_point=`;
    const timeseries_url = `${http_prefix}/lua/pro/enterprise/observation_points.lua?page=historical&observation_point=`;

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "observation_point": (value, row) => {
                // get table footer notes
                var returnValue = value;

                // Add timeseries icon if timeseries are enabled
                if (row['observation_point_name']) {
                    returnValue = `&nbsp;${row['observation_point_name']}`;
                }

                let config_url = observation_point_config_url + value;
                returnValue += `&nbsp;<a href=${config_url}><i class="fas fa-cog fa-lg"></i></a>`;

                if (row["timeseries"]) {
                    let timeseries_full_url = timeseries_url + value;
                    returnValue += `&nbsp;<a href=${timeseries_full_url}><i class="fas fa-chart-area fa-lg"></i></a>`;
                }

                return returnValue
            },
            "exporters": (value, row) => {
                if (!value)
                    return '';

                let exporters_formatted_url = exporters_url + value + `&ifid=${props.context.ifid}`;
                return `<a href=${exporters_formatted_url}>${value}</a>`
            },
            "total_flows": (value, row) => {
                if (!value)
                    return '';
                return formatterUtils.getFormatter("number")(value);
            },
            "hosts": (value, row) => {
                if (!value)
                    return '';
                return formatterUtils.getFormatter("number")(value);
            },
            "throughput": (value, row) => {
                return formatterUtils.getFormatter("bps")(value);
            },
            "traffic": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });

        return columns;
    };


    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "observation_point") {
                return sortingFunctions$1.sortByName(r0.observation_point, r1.observation_point, col.sort);
            } else if (col.id == "exporters") {
                return sortingFunctions$1.sortByNumber(r0.exporters, r1.exporters, col.sort);
            } else if (col.id == "total_flows") {
                return sortingFunctions$1.sortByNumber(r0.total_flows, r1.total_flows, col.sort);
            } else if (col.id == "hosts") {
                return sortingFunctions$1.sortByNumber(r0.hosts, r1.hosts, col.sort);
            } else if (col.id == "throughput") {
                return sortingFunctions$1.sortByNumber(r0.throughput, r1.throughput, col.sort);
            } else if (col.id == "traffic") {
                return sortingFunctions$1.sortByNumber(r0.traffic, r1.traffic, col.sort);
            }
        }
    }

    function create_config_url_link(row) {
        return `${http_prefix}/lua/pro/db_search.lua?observation_point_id=${row.observation_point};eq`
    }

    function click_button_live_flows(event) {
        const row = event.row;
        window.open(create_config_url_link(row));
    }

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_live_flows": click_button_live_flows,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$p, [
        createVNode(script$2w, {
          ref_key: "table_observation_points",
          ref: table_observation_points,
          table_id: table_id.value,
          csrf: unref(csrf),
          f_map_columns: map_table_def_columns,
          f_sort_rows: columns_sorting,
          get_extra_params_obj: get_extra_params_obj,
          onCustom_event: on_table_custom_event
        }, null, 8 /* PROPS */, ["table_id", "csrf"]),
        createVNode(script$2u, { note_list: note_list.value }, null, 8 /* PROPS */, ["note_list"])
      ]))
    }
    }

    };

    script$u.__file = "http_src/vue/page-observation-points.vue";

    const _hoisted_1$o = { class: "m-3" };
    const _hoisted_2$i = { class: "card card-shadow" };
    const _hoisted_3$h = { class: "card-body" };
    const _hoisted_4$g = { class: "table table-striped table-bordered" };
    const _hoisted_5$e = ["placeholder"];
    const _hoisted_6$b = { class: "d-flex justify-content-end me-1" };
    const _hoisted_7$a = ["disabled"];


    var script$t = {
      __name: 'page-observation-points-config',
      props: {
        context: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const aliasValue = ref('');
    const defaultAlias = ref('');
    const disableSave = ref(true);
    const isSaving = ref(false);
    const saveSuccess = ref(false);

    // Computed properties for button states
    const saveButtonText = computed$1(() => {
        if (isSaving.value) return 'Saving...';
        if (saveSuccess.value) return 'Saved!';
        return _i18n("save_settings");
    });

    const saveButtonClass = computed$1(() => {
        if (saveSuccess.value) return 'btn-success';
        return 'btn-primary';
    });

    // API endpoints
    const get_alias_url = `${http_prefix}/lua/pro/rest/v2/get/observation_points/alias.lua?observation_point=${props.context.obs_point_id}`;
    const set_alias_url = `${http_prefix}/lua/pro/rest/v2/set/observation_points/alias.lua`;

    // get alias
    onMounted(async () => {
        try {
            const response = await ntopng_utility$1.http_request(get_alias_url);
            if (response) {
                aliasValue.value = response;
            }
        } catch (error) {
            console.error('Error fetching alias:', error);
        }
    });

    // Handle input changes
    const handleInput = () => {
        disableSave.value = aliasValue.value === defaultAlias.value;
    };


    const saveConfig = async () => {
        if (disableSave.value) return;
        
        isSaving.value = true;
        try {
            await ntopng_utility$1.http_post_request(set_alias_url, {
                csrf: props.context.csrf,
                alias: aliasValue.value,
                observation_point: props.context.obs_point_id
            });

            // Show success state
            saveSuccess.value = true;
            disableSave.value = true;
            defaultAlias.value = aliasValue.value;
            
            setTimeout(() => {
                saveSuccess.value = false;
            }, 100);
            window.location.reload();

        } catch (error) {
            console.error('Error saving alias:', error);
        } finally {
            isSaving.value = false;
        }
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$o, [
        createBaseVNode("div", _hoisted_2$i, [
          createBaseVNode("div", _hoisted_3$h, [
            createBaseVNode("form", {
              onSubmit: withModifiers(saveConfig, ["prevent"])
            }, [
              createBaseVNode("table", _hoisted_4$g, [
                createBaseVNode("tbody", null, [
                  createBaseVNode("tr", null, [
                    createBaseVNode("th", null, toDisplayString(_i18n("observation_point_alias")), 1 /* TEXT */),
                    createBaseVNode("td", null, [
                      withDirectives(createBaseVNode("input", {
                        type: "text",
                        "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((aliasValue).value = $event)),
                        class: "form-control",
                        placeholder: defaultAlias.value,
                        onInput: handleInput
                      }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_5$e), [
                        [vModelText, aliasValue.value]
                      ])
                    ])
                  ])
                ])
              ]),
              createBaseVNode("div", _hoisted_6$b, [
                createBaseVNode("button", {
                  class: normalizeClass(["btn", saveButtonClass.value]),
                  disabled: disableSave.value,
                  onClick: saveConfig
                }, toDisplayString(saveButtonText.value), 11 /* TEXT, CLASS, PROPS */, _hoisted_7$a)
              ])
            ], 32 /* NEED_HYDRATION */)
          ])
        ])
      ]))
    }
    }

    };

    script$t.__file = "http_src/vue/page-observation-points-config.vue";

    const _hoisted_1$n = { class: "m-2 mb-3" };



    var script$s = {
      __name: 'page-flow-exporters-list',
      props: {
        context: Object
    },
      setup(__props) {


    const props = __props;

    const table_id = ref('flow_exporters_list');
    const table_exporters_list = ref(null);
    const csrf = props.context.csrf;

    const exporter_info_url = `${http_prefix}/lua/pro/db_search.lua?probe_ip=`;
    const exporter_url = `${http_prefix}/lua/pro/enterprise/flowdevice_details.lua?ip=`;

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "exporter_device": (value, row) => {
                if (!value)
                    return '';

                let exporters_formatted_url = exporter_url + value;
                return `<a href=${exporters_formatted_url}>${value}</a>`
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });

        return columns;
    };


    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "exporter_device") {
                return sortingFunctions$1.sortByName(r0.exporter_device, r1.exporter_device, col.sort);
            } else if (col.id == "exporters") {
                return sortingFunctions$1.sortByNumber(r0.exporters, r1.exporters, col.sort);
            }
        }
    }

    function create_config_url_link(row) {
        return exporter_info_url + row.exporter_device + ";eq&observation_point_id=" + props.context.observation_point + ";eq"
    }

    function click_button_live_flows(event) {
        const row = event.row;
        window.open(create_config_url_link(row));
    }

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_live_flows": click_button_live_flows,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$n, [
        createVNode(script$2w, {
          ref_key: "table_exporters_list",
          ref: table_exporters_list,
          table_id: table_id.value,
          csrf: unref(csrf),
          showLoading: true,
          f_map_columns: map_table_def_columns,
          f_sort_rows: columns_sorting,
          get_extra_params_obj: get_extra_params_obj,
          onCustom_event: on_table_custom_event
        }, null, 8 /* PROPS */, ["table_id", "csrf"])
      ]))
    }
    }

    };

    script$s.__file = "http_src/vue/page-flow-exporters-list.vue";

    /*
      (C) 2013-23 - ntop.org
     */

    /*
      Here a list of functions used to check, format data;
      e.g. functions that check if a string is null or empty
     */

    /* This function create a small heatmap, depending on the hours */
    const createHeatmap = (data) => {
      const squareLength = 7, squareHeight = 20;
      const colors = ['#d3d3d3', '#28a745', '#f00', '#ffc107'];
      let svg = `<svg width='${squareLength * data.length + data.length * 2}' height='20' viewBox='0 0 ${squareLength * data.length + data.length * 2} 20'>`;
      const this_hour = new Date().getHours();

      for (let x = 0; x < 24; x++) {
        const $rect = $(document.createElementNS("http://www.w3.org/2000/svg", "rect"));
        $rect.attr('x', x * (squareLength + 2)).attr('y', 0).attr('width', squareLength).attr('height', squareHeight);
        const colorIndex = (data.length > 0) ? data[x] : 0;
        $rect.attr('fill', colors[colorIndex]);
        if (this_hour == x) {
          $rect.attr('stroke', '#000'); /* Add stroke for the current hour */
          $rect.attr('stroke-width', '2');
          $rect.attr('stroke-dasharray', '2'); /* Make line dashed */
          $rect.attr('opacity', '0.6');
        }        
        svg = `${svg}${$rect[0].outerHTML}`;
      }
      svg = `${svg}</svg>`;
      return svg;
    };

    /* ******************************************************************** */

    const visualizationUtils = function () {
      return {
        createHeatmap
      };
    }();

    /*
      (C) 2013-24 - ntop.org
     */

    const active_monitoring_type = [
       { name: i18n('active_monitoring_stats.icmp_continuous'), id: 'cicmp' },
       { name: i18n('active_monitoring_stats.speedtest'), id: 'speedtest' },
       { name: i18n('active_monitoring_stats.throughput'), id: 'throughput' },
       { name: i18n('active_monitoring_stats.icmp_continuous_v6'), id: 'cicmp6' },
       { name: i18n('active_monitoring_stats.icmp'), id: 'icmp' },
       { name: i18n('active_monitoring_page.http'), id: 'http' },
    ];

    const getActiveMonitoringName = (id) => {
       const obj = active_monitoring_type.find(el => el.id === id);
       if (obj) {
          return obj.name;
       }
       return id;
    };

    const activeMonitoringUtils = function () {
       return {
          getActiveMonitoringName
       };
    }();

    const _hoisted_1$m = { class: "form-group mb-3 row" };
    const _hoisted_2$h = { class: "col-4 col-form-label" };
    const _hoisted_3$g = { class: "col-5" };
    const _hoisted_4$f = { class: "form-group mb-3 row" };
    const _hoisted_5$d = { class: "col-4 col-form-label" };
    const _hoisted_6$a = { class: "col-5" };
    const _hoisted_7$9 = ["disabled", "placeholder"];
    const _hoisted_8$9 = { class: "form-group mb-3 row" };
    const _hoisted_9$9 = { class: "col-4 col-form-label" };
    const _hoisted_10$8 = { class: "col-5" };
    const _hoisted_11$7 = { class: "form-group mb-3 row" };
    const _hoisted_12$6 = { class: "col-4 col-form-label" };
    const _hoisted_13$5 = { class: "col-5" };
    const _hoisted_14$5 = { class: "input-group" };
    const _hoisted_15$5 = { class: "input-group-text measurement-operator" };
    const _hoisted_16$5 = ["max"];
    const _hoisted_17$5 = { class: "col-sm-2 pl-0" };
    const _hoisted_18$5 = { class: "my-auto ml-1 measurement-unit d-inline-block p-2" };
    const _hoisted_19$5 = {
      key: 0,
      class: "form-group mb-3 row"
    };
    const _hoisted_20$5 = { class: "col-4 col-form-label" };
    const _hoisted_21$5 = { class: "col-5" };
    const _hoisted_22$5 = { class: "form-group" };
    const _hoisted_23$5 = {
      key: 0,
      class: "me-auto w-100 text-danger"
    };
    const _hoisted_24$5 = {
      key: 1,
      class: "mt-1 notes bg-danger me-auto w-100 bg-opacity-25"
    };
    const _hoisted_25$4 = ["disabled"];


    var script$r = {
      __name: 'modal-add-active-monitoring',
      props: {
        context: Object,
        measurements: Array,
        interfaces: Array,
        url_request: String,
    },
      emits: ['add'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const emit = __emit;
    const _i18n = (t) => i18n(t);
    const disable_add = ref(true);
    const show_feedback = ref(false);
    const activate_add_spinner = ref(false);
    const feedback = ref('');
    const error = ref(i18n('active_monitoring_page.error_detected'));
    const title = ref(i18n('active_monitoring_page.add_record'));
    const measurements_list = ref([]);
    const interfaces_list = ref([]);
    const granularities_list = ref([]);
    const selected_measurement = ref([]);
    const selected_interface = ref([]);
    const selected_granularity = ref([]);
    const disable_granularity = ref(false);
    const disable_interface = ref(false);
    const disable_host = ref(false);
    const threshold_operator = ref('');
    const threshold_unit = ref('');
    const threshold_max = ref(0);
    const threshold = ref(null);
    const host = ref(null);
    const row = ref(null);
    const add_button_title = ref(i18n('add'));
    const host_placeholder = ref(i18n('active_monitoring_page.host_placeholder'));

    const note_list = [
        _i18n("active_monitoring_page.note_icmp"),
        _i18n("active_monitoring_page.note_http"),
        _i18n("active_monitoring_page.note_alert"),
    ];

    const operators_list = [
        { id: "lt", label: '<' },
        { id: "gt", label: '>' },
    ];

    /* ************************************** */

    const props = __props;

    /* ************************************** */

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.measurements], (cur_value, old_value) => {
        update_lists();
    }, { flush: 'pre', deep: true });

    /* ************************************** */

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.interfaces], (cur_value, old_value) => {
        const tmp = props.interfaces.filter((t) => (t.is_packet_interface && !t.is_pcap_interface));

        interfaces_list.value = tmp.map((t) => {
            return {
                id: t.ifname,
                label: t.name,
                title: t.name,
            };
        });
        selected_interface.value = interfaces_list.value[0] || [];
        disable_interface.value = (interfaces_list.value.length <= 1);
    }, { flush: 'pre', deep: true });

    /* ************************************** */

    /* Brief check regarding the host, it is checked from the back end too */
    function check_host(value) {
        if (regexValidation.validateHostName(value) || regexValidation.validateIP(value) || regexValidation.validateURL(value)) {
            disable_add.value = true;
        }
        disable_add.value = false;
    }

    /* ************************************** */

    /* Reset the values to defualt */
    function reset_modal_form() {
        disable_add.value = true;
        update_lists(measurements_list.value[0]);
        host.value.value = '';
        selected_interface.value = interfaces_list.value[0] || [];
        threshold.value.value = '99';
        row.value = null;
        add_button_title.value = i18n('add');
        title.value = i18n('active_monitoring_page.add_record');
    }

    /* ************************************** */

    /* This function is called from the show, to correctly display the data from the selected row */
    function format_edit() {
        selected_measurement.value = measurements_list.value.find((el) => el.id === row.value.last_measurement.measurement_type);
        change_dropdowns(selected_measurement.value);
        host.value.value = row.value.target.host;
        threshold.value.value = row.value.threshold;
        selected_interface.value = interfaces_list.value.find((el) => el.id === row.value.metadata.interface_name);
        selected_granularity.value = granularities_list.value.find((el) => el.id === row.value.metadata.granularity);
        title.value = i18n('active_monitoring_page.edit_record');
        add_button_title.value = i18n('edit');
        disable_add.value = false;
    }

    /* ************************************** */

    /* Function automatically called when opening the modal */
    const show = (_row) => {
        reset_modal_form();
        if (_row) {
            row.value = _row;
            format_edit();
        }
        modal_id.value.show();
    };

    /* ************************************** */

    /* Function called when clicking the add button */
    const add_ = async () => {
        activate_add_spinner.value = true;
        let params = {
            csrf: props.context.csrf,
            host: host.value.value,
            action: (row.value) ? "edit" : "add",
            granularity: selected_granularity.value.id,
            measurement: selected_measurement.value.id,
            threshold: threshold.value.value,
            ifname: selected_interface.value?.id
        };
        /* Handle the edit */
        if (row.value) {
            params.old_host = row.value.target.host;
            params.old_granularity = row.value.metadata.granularity;
            params.old_measurement = row.value.last_measurement.measurement_type;
        }
        const rsp = await ntopng_utility.http_post_request(props.url_request, params);
        activate_add_spinner.value = false;

        if (rsp.result == 'ok') {
            show_feedback.value = false;
            emit('add', params);
            close();
        } else {
            show_feedback.value = true;
            feedback.value = rsp.result;
        }
    };

    /* ************************************** */

    const close = () => {
        modal_id.value.close();
    };

    /* ************************************** */

    const change_dropdowns = (item) => {
        disable_host.value = false;
        const tmp = props.measurements.find((el) => el.key === item.id);
        if (tmp.key === "throughput") {
            host_placeholder.value = i18n('active_monitoring_page.thpt_host_placeholder');
        }
        if (tmp.key === "speedtest") {
            host.value.value = "speedtest.net";
            disable_host.value = true;
            check_host(host.value.value);
        }
        granularities_list.value = tmp.granularities.map((t) => {
            return {
                id: t.value,
                label: t.title,
                title: t.title
            };
        });
        selected_granularity.value = granularities_list.value[0];
        disable_granularity.value = (granularities_list.value.length === 1);
        threshold_operator.value = operators_list.find((el) => el.id === tmp.operator).label;
        threshold_unit.value = tmp.unit;
        threshold_max.value = tmp.max_threshold;
    };

    /* ************************************** */

    onBeforeMount(() => { });

    /* ************************************** */

    onMounted(() => { });

    /* ************************************** */

    const update_lists = () => {
        measurements_list.value = props.measurements.map((t) => {
            return {
                id: t.key,
                label: t.label,
                title: t.label,
            };
        });
        selected_measurement.value = measurements_list.value[0];
        change_dropdowns(selected_measurement.value);
    };

    /* ************************************** */

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("form", null, [
            createBaseVNode("div", _hoisted_1$m, [
              createBaseVNode("label", _hoisted_2$h, [
                createBaseVNode("b", null, toDisplayString(_i18n('active_monitoring_page.measurement')), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_3$g, [
                createVNode(script$2H, {
                  selected_option: selected_measurement.value,
                  "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_measurement).value = $event)),
                  options: measurements_list.value,
                  onSelect_option: change_dropdowns
                }, null, 8 /* PROPS */, ["selected_option", "options"])
              ])
            ]),
            createBaseVNode("div", _hoisted_4$f, [
              createBaseVNode("label", _hoisted_5$d, [
                createBaseVNode("b", null, toDisplayString(_i18n('db_search.host')), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_6$a, [
                createBaseVNode("input", {
                  ref_key: "host",
                  ref: host,
                  class: "form-control",
                  disabled: disable_host.value,
                  type: "text",
                  placeholder: host_placeholder.value,
                  required: "",
                  onInput: check_host
                }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_7$9)
              ])
            ]),
            createBaseVNode("div", _hoisted_8$9, [
              createBaseVNode("label", _hoisted_9$9, [
                createBaseVNode("b", null, toDisplayString(_i18n('active_monitoring_page.run_monitoring')), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_10$8, [
                createVNode(script$2H, {
                  selected_option: selected_granularity.value,
                  "onUpdate:selected_option": _cache[1] || (_cache[1] = $event => ((selected_granularity).value = $event)),
                  options: granularities_list.value,
                  disabled: disable_granularity.value
                }, null, 8 /* PROPS */, ["selected_option", "options", "disabled"])
              ])
            ]),
            createBaseVNode("div", _hoisted_11$7, [
              createBaseVNode("label", _hoisted_12$6, [
                createBaseVNode("b", null, toDisplayString(_i18n('threshold')), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_13$5, [
                createBaseVNode("div", _hoisted_14$5, [
                  createBaseVNode("span", _hoisted_15$5, toDisplayString(threshold_operator.value), 1 /* TEXT */),
                  createBaseVNode("input", {
                    ref_key: "threshold",
                    ref: threshold,
                    value: "99",
                    name: "threshold",
                    type: "number",
                    class: "form-select rounded-right measurement-threshold",
                    min: "1",
                    max: threshold_max.value
                  }, null, 8 /* PROPS */, _hoisted_16$5)
                ])
              ]),
              createBaseVNode("div", _hoisted_17$5, [
                createBaseVNode("span", _hoisted_18$5, toDisplayString(threshold_unit.value), 1 /* TEXT */)
              ])
            ]),
            (!disable_interface.value)
              ? (openBlock(), createElementBlock("div", _hoisted_19$5, [
                  createBaseVNode("label", _hoisted_20$5, [
                    createBaseVNode("b", null, toDisplayString(_i18n('active_monitoring_page.interface_used')), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_21$5, [
                    createVNode(script$2H, {
                      selected_option: selected_interface.value,
                      "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((selected_interface).value = $event)),
                      options: interfaces_list.value
                    }, null, 8 /* PROPS */, ["selected_option", "options"])
                  ])
                ]))
              : createCommentVNode("v-if", true)
          ]),
          createBaseVNode("div", _hoisted_22$5, [
            createVNode(script$2u, { note_list: note_list })
          ])
        ]),
        footer: withCtx(() => [
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_23$5, toDisplayString(error.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          (show_feedback.value)
            ? (openBlock(), createElementBlock("div", _hoisted_24$5, toDisplayString(feedback.value), 1 /* TEXT */))
            : createCommentVNode("v-if", true),
          createBaseVNode("div", null, [
            createVNode(script$2v, {
              show: activate_add_spinner.value,
              size: "1rem",
              class: "me-2"
            }, null, 8 /* PROPS */, ["show"]),
            createBaseVNode("button", {
              disabled: disable_add.value,
              type: "button",
              onClick: add_,
              class: "btn btn-primary"
            }, toDisplayString(add_button_title.value), 9 /* TEXT, PROPS */, _hoisted_25$4)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$r.__file = "http_src/vue/modal-add-active-monitoring.vue";

    const _hoisted_1$l = {
      key: 0,
      class: "text-left"
    };


    var script$q = {
      __name: 'modal-delete-active-monitoring',
      props: {
      context: Object,
    },
      emits: ["delete"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);
    const modal_id = ref(null);
    const message = ref('');
    const return_message = ref('');
    const show_return_msg = ref(false);
    const title = ref('');
    const err = ref(false);
    const row = ref(null);

    const emit = __emit;

    const props = __props;

    onMounted(() => { });

    /* ****************************************** */

    /* This function simply reset the modal to factory values */
    async function resetModal() {
      row.value = null;
      message.value = '';
      return_message.value = '';
      show_return_msg.value = false;
      err.value = false;
      title.value = '';
    }

    /* ****************************************** */

    /* This function formats the delete message */
    async function formatMessage(_row) {
      title.value = `${i18n("delete")}: ${i18n("active_monitoring_page." + _row.last_measurement.measurement_type)} ${_row.target.name}`;
      message.value = i18n('active_monitoring_page.delete');
    }

    /* ****************************************** */

    /* This function runs the delete code, called when apply is clicked */
    async function delete_measurement() {
      const url = `${http_prefix}/lua/rest/v2/delete/active_monitoring/measurement.lua`;
      let params = {
        csrf: props.context.csrf,
        ifid: props.context.ifid,
        host: row.value.target.host,
        measurement: row.value.last_measurement.measurement_type,
      };

      const res = await ntopng_utility$1.http_post_request(url, params);
      if (res) {
        close();
      }
    }

    const show = (_row) => {
      resetModal();
      row.value = _row;
      formatMessage(_row);
      modal_id.value.show();
    };

    const close = () => {
      setTimeout(() => {
        modal_id.value.close();
        emit("delete");
      }, 500 /* 0.5 seconds */);
    };

    __expose({ show, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createTextVNode(toDisplayString(message.value) + " ", 1 /* TEXT */),
          (show_return_msg.value)
            ? (openBlock(), createElementBlock("div", _hoisted_1$l))
            : createCommentVNode("v-if", true)
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: delete_measurement,
            class: "btn btn-danger"
          }, toDisplayString(_i18n("delete")), 1 /* TEXT */)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$q.__file = "http_src/vue/modal-delete-active-monitoring.vue";

    /*
      (C) 2013-24 - ntop.org
     */

    const interfaces = [
       { is_pcap_interface: true, icon: "<i class='fas fa-file'></i>" },
       { is_packet_interface: true, icon: "<i class='fas fa-ethernet'></i>" },
       { is_zmq_interface: true, icon: "<i class='fas fa-bezier-curve'></i>" },
       { is_view_interface: true, icon: "<i class='fas fa-eye'></i>" },
       { is_dynamic_interface: true, icon: "<i class='fas fa-code-branch'></i>" },
       { is_dropping_interface: true, icon: "<i class='fas fa-tint'></i>" },
       { is_recording_interface: true, icon: "<i class='fas fa-hdd'></i>" },
    ];

    const getInterfaceIcon = (interface_types) => {
       for (const [key, value] of Object.entries(interface_types)) {
          const interface_info = interfaces.find((el) => el[key] ? (el[key] === value) : false);
          if (interface_info) {
             return interface_info.icon;
          }
       }
       return ''
    };

    const interfaceUtils = function () {
       return {
          getInterfaceIcon
       };
    }();

    const _hoisted_1$k = ["innerHTML"];
    const _hoisted_2$g = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_3$f = { class: "no-wrap d-flex align-items-center my-auto me-2 filters-label" };
    const _hoisted_4$e = { class: "card-footer mt-3" };


    var script$p = {
      __name: 'page-active-monitoring',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const host_filters_key = ref(0);
    const table_id = ref('active-monitoring');
    const filter_table_array = ref([]);
    const date_format = ref(null);
    const table_active_monitoring = ref();
    const measurements_list = ref([]);
    const interfaces_list = ref([]);
    const modal_add_active_monitoring = ref(null);
    const modal_delete = ref(null);
    const error_message = ref(i18n('host_config.active_monitor_enable'));
    const manage_configurations_url = `${http_prefix}/lua/admin/manage_configurations.lua?item=active_monitoring`;
    const measurements_url = ref(http_prefix + '/lua/rest/v2/get/active_monitoring/measurements.lua');
    const interfaces_url = ref(http_prefix + '/lua/rest/v2/get/ntopng/interfaces.lua');
    const new_measurement_url = ref(http_prefix + '/lua/rest/v2/set/active_monitoring/measurement.lua');

    /* ************************************** */

    const props = __props;
    /* ************************************** */

    const click_button_manage_configuration = () => {
        window.location.href = manage_configurations_url;
    };

    /* ************************************** */

    const map_table_def_columns = (columns) => {
        let map_columns = {
            "target": (value, row) => {
                let target_interface = {};
                let interface_name = '';
                let is_infrastructure_instance = '';
                let is_alerted = '';
                if (interfaces_list.value.length > 0) {
                    const tmp_iface = interfaces_list.value.find((el) => el.ifid == row.metadata.interface_id);
                    if (tmp_iface) {
                        target_interface["is_pcap_interface"] = tmp_iface["is_pcap_interface"];
                        target_interface["is_packet_interface"] = tmp_iface["is_packet_interface"];
                        target_interface["is_zmq_interface"] = tmp_iface["is_zmq_interface"];
                        target_interface["is_view_interface"] = tmp_iface["is_view_interface"];
                        target_interface["is_dynamic_interface"] = tmp_iface["is_dynamic_interface"];
                        target_interface["is_dropping_interface"] = tmp_iface["is_dropping_interface"];
                        target_interface["is_recording_interface"] = tmp_iface["is_recording_interface"];
                    }
                }
                if (!dataUtils.isEmptyOrNull(row.metadata.interface_name)) {
                    interface_name = ` [ ${interfaceUtils.getInterfaceIcon(target_interface)} ${row.metadata.interface_name} ] `;
                }
                if (row.metadata.is_infrastructure_instance) {
                    is_infrastructure_instance = " <i class='fas fa-building'></i> ";
                }
                if (row.metadata.is_alerted) {
                    is_alerted = ' <i class="fas fa-exclamation-triangle" style="color: #f0ad4e;"></i> ';
                }
                return `<span data-bs-toggle='tooltip' data-bs-placement='bottom' title='${(row.ip_address !== value.name) ? row.ip_address : ''}'>${value.name}${is_infrastructure_instance}${is_alerted}${interface_name}</span>`;
            },
            "ip_address": (value, row) => {
                return value;
            },
            "measurement_type": (value, row) => {
                return activeMonitoringUtils.getActiveMonitoringName(row.last_measurement.measurement_type);
            },
            "hourly_stats": (value, row) => {
                return visualizationUtils.createHeatmap(value);
            },
            "last_measurement_time": (value, row) => {
                value = row.last_measurement.last_measurement_time;
                if (value > 0) {
                    return formatterUtils.formatDateTime(value)
                }
                return;
            },
            "threshold": (value, row) => {
                return value + " " + i18n(row.metadata.unit);
            },
            "measurement_value": (value, row) => {
                let measurement = row.last_measurement.measurement_value;
                let measurement_type = row.last_measurement.measurement_type;

                if (measurement_type == "throughput") {
                    // measurement is in bytes per second, so * 8 to format bps
                    return formatterUtils.getFormatter("bps")(measurement * 8);
                }
                if (!dataUtils.isEmptyString(measurement)) {
                    measurement = measurement + " " + (row.last_measurement.measurement_type == "speedtest" ? "" : i18n(row.metadata.unit));
                }
                return measurement;
            },
            "extra_measurements": (value, row) => {
                let measurement_type = row.last_measurement.measurement_type;

                // measurements different than continuous icmp do not have jutter and mean RTT
                if (measurement_type != "cicmp") {
                    return ""
                }

                if (dataUtils.isEmptyString(value.mean)) {
                    value.mean = "-";
                }

                if (dataUtils.isEmptyString(value.jitter)) {
                    value.jitter = "-";
                }
                return `${value.mean} / ${value.jitter} ms`;
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id == "actions") {
                const visible_dict = {
                    edit: props.context.is_admin,
                    delete: props.context.is_admin,
                    timeseries: true,
                };
                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        // if is not defined is enabled
                        if (!visible_dict[b.id]) {
                            current_class.push("d-none");
                        } else if (!row.metadata.timeseries && (b.id) == "timeseries") {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };

    /* ************************************** */

    function column_data(col, row) {
        return row[col.data.data_field];
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            let r0_col = column_data(col, r0);
            let r1_col = column_data(col, r1);

            /* In case the values are the same, sort by IP */
            if (col.id == "target") {
                let val0 = r0_col;
                let val1 = r1_col;
                if (r0_col) val0 = r0_col.name;
                if (r1_col) val1 = r1_col.name;
                return sortingFunctions$1.sortByName(val0, val1, col.sort);
            } else if (col.id == "ip_address") {
                return sortingFunctions$1.sortByIP(r0_col, r1_col, col.sort);
            } else if (col.id == "measurement_type") {
                let val0 = r0.last_measurement.measurement_type;
                let val1 = r1.last_measurement.measurement_type;
                return sortingFunctions$1.sortByName(val0, val1, col.sort);
            } else if (col.id == "last_measurement_time") {
                let val0 = r0.last_measurement.last_measurement_time;
                let val1 = r1.last_measurement.last_measurement_time;
                const lower_value = 0;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(val0, val1, col.sort, lower_value);
            } else if (col.id == "threshold") {
                const lower_value = 0;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(r0_col, r1_col, col.sort, lower_value);
            } else if (col.id == "measurement_value") {
                let val0 = r0.last_measurement.measurement_value;
                let val1 = r1.last_measurement.measurement_value;
                const lower_value = 0;
                return sortingFunctions$1.sortByNumberWithNormalizationValue(val0, val1, col.sort, lower_value);
            }
        }
    }

    /* ************************************** */

    function set_filter_array_label() {
        filter_table_array.value.forEach((el, index) => {
            /* Setting the basic label */
            if (el.basic_label == null) {
                el.basic_label = el.label;
            }

            /* Getting the currently selected filter */
            const url_entry = ntopng_url_manager$1.get_url_entry(el.id);
            el.options.forEach((option) => {
                if (option.value.toString() === url_entry) {
                    el.current_option = option;
                }
            });
        });
    }

    /* ************************************** */

    async function add_table_filter(opt) {
        ntopng_url_manager$1.set_key_to_url(opt.key, `${opt.value}`);
        set_filter_array_label();
        table_active_monitoring.value.refresh_table();
        //  filter_table_array.value = await load_table_filters_array()
    }

    /* ************************************** */

    async function load_table_filters_array() {
        let extra_params = get_extra_params_obj();
        let url_params = ntopng_url_manager$1.obj_to_url_params(extra_params);
        const url = `${http_prefix}/lua/rest/v2/get/active_monitoring/filters.lua?${url_params}`;
        let res = await ntopng_utility.http_request(url);
        host_filters_key.value = host_filters_key.value + 1;

        return res.map((t) => {
            const key_in_url = ntopng_url_manager$1.get_url_entry(t.name);
            if (dataUtils.isEmptyOrNull(key_in_url)) {
                ntopng_url_manager$1.set_key_to_url(t.name, ``);
            }
            return {
                id: t.name,
                label: t.label,
                title: t.tooltip,
                options: t.value,
                hidden: (t.value.length == 1)
            };
        });
    }

    /* ************************************** */

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function add_monitoring() {
        modal_add_active_monitoring.value.show();
    }

    /* ************************************** */

    function click_button_delete(event) {
        const row = event.row;
        modal_delete.value.show(row);
    }

    /* ************************************** */

    function click_button_edit(event) {
        const row = event.row;
        modal_add_active_monitoring.value.show(row);
    }

    /* ************************************** */

    function click_button_timeseries(event) {
        const row = event.row;
        window.open(`${http_prefix}/lua/active_monitoring.lua?host=${row.target.host}&measurement=${row.last_measurement.measurement_type}&page=historical`);
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_edit": click_button_edit,
            "click_button_delete": click_button_delete,
            "click_button_timeseries": click_button_timeseries,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    /* ************************************** */

    function refresh_table() {
        table_active_monitoring.value.refresh_table();
    }

    /* ************************************** */

    onMounted(async () => {
        filter_table_array.value = await load_table_filters_array();
        set_filter_array_label();
    });

    /* ************************************** */

    onBeforeMount(async () => {
        measurements_list.value = await ntopng_utility.http_request(measurements_url.value);
        interfaces_list.value = await ntopng_utility.http_request(interfaces_url.value);
        date_format.value = await ntopng_utility.get_date_format(false, props.context.csrf, http_prefix);
    });

    /* ************************************** */


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        ((!props.context.is_am_active))
          ? (openBlock(), createElementBlock("div", {
              key: 0,
              class: "alert alert-warning",
              role: "alert",
              id: "error-alert",
              innerHTML: error_message.value
            }, null, 8 /* PROPS */, _hoisted_1$k))
          : createCommentVNode("v-if", true),
        createBaseVNode("div", {
          class: normalizeClass(["m-2 mb-3", [(!props.context.is_am_active) ? 'ntopng-gray-out' : '']])
        }, [
          createVNode(script$2w, {
            ref_key: "table_active_monitoring",
            ref: table_active_monitoring,
            table_id: table_id.value,
            csrf: __props.context.csrf,
            showLoading: true,
            f_map_columns: map_table_def_columns,
            get_extra_params_obj: get_extra_params_obj,
            f_sort_rows: columns_sorting,
            onCustom_event: on_table_custom_event
          }, {
            custom_buttons: withCtx(() => [
              createBaseVNode("button", {
                class: "btn btn-link",
                type: "button",
                onClick: add_monitoring
              }, [...(_cache[0] || (_cache[0] = [
                createBaseVNode("i", { class: "fas fa-plus" }, null, -1 /* CACHED */)
              ]))])
            ]),
            custom_header: withCtx(() => [
              (openBlock(true), createElementBlock(Fragment, null, renderList(filter_table_array.value, (item) => {
                return (openBlock(), createElementBlock("div", _hoisted_2$g, [
                  createBaseVNode("span", _hoisted_3$f, [
                    createBaseVNode("b", null, toDisplayString(item["basic_label"]), 1 /* TEXT */)
                  ]),
                  createVNode(script$2H, {
                    selected_option: item['current_option'],
                    "onUpdate:selected_option": $event => ((item['current_option']) = $event),
                    theme: "bootstrap-5",
                    dropdown_size: "small",
                    options: item['options'],
                    onSelect_option: add_table_filter
                  }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options"])
                ]))
              }), 256 /* UNKEYED_FRAGMENT */))
            ]),
            _: 1 /* STABLE */
          }, 8 /* PROPS */, ["table_id", "csrf"]),
          createBaseVNode("div", _hoisted_4$e, [
            (props.context.is_admin)
              ? (openBlock(), createElementBlock("button", {
                  key: 0,
                  type: "button",
                  class: "btn btn-secondary ms-1",
                  onClick: click_button_manage_configuration
                }, [
                  _cache[1] || (_cache[1] = createBaseVNode("i", { class: "fas fa-tasks" }, null, -1 /* CACHED */)),
                  createTextVNode(" " + toDisplayString(_i18n("manage_configurations.manage_configuration")), 1 /* TEXT */)
                ]))
              : createCommentVNode("v-if", true)
          ])
        ], 2 /* CLASS */),
        createVNode(script$r, {
          ref_key: "modal_add_active_monitoring",
          ref: modal_add_active_monitoring,
          interfaces: interfaces_list.value,
          measurements: measurements_list.value,
          context: __props.context,
          url_request: new_measurement_url.value,
          onAdd: refresh_table
        }, null, 8 /* PROPS */, ["interfaces", "measurements", "context", "url_request"]),
        createVNode(script$q, {
          ref_key: "modal_delete",
          ref: modal_delete,
          interfaces: interfaces_list.value,
          measurements: measurements_list.value,
          context: __props.context,
          url_request: new_measurement_url.value,
          onDelete: refresh_table
        }, null, 8 /* PROPS */, ["interfaces", "measurements", "context", "url_request"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$p.__file = "http_src/vue/page-active-monitoring.vue";

    const _hoisted_1$j = { class: "dashboard-container bg-light" };
    const _hoisted_2$f = { class: "row g-4" };
    const _hoisted_3$e = { class: "range-container d-flex flex-wrap" };
    const _hoisted_4$d = { class: "card-body w-100 range-picker d-flex m-auto flex-wrap" };
    const _hoisted_5$c = { class: "ms-4 d-flex align-items-center ms-2" };
    const _hoisted_6$9 = { class: "text-nowrap fw-semibold me-1" };
    const _hoisted_7$8 = ["title"];
    const _hoisted_8$8 = { class: "ms-4 d-flex align-items-center ms-2" };
    const _hoisted_9$8 = { class: "text-nowrap fw-semibold me-1" };
    const _hoisted_10$7 = ["title"];
    const _hoisted_11$6 = { class: "ms-4 d-flex align-items-center" };
    const _hoisted_12$5 = { class: "w-100" };
    const _hoisted_13$4 = { class: "d-flex" };
    const _hoisted_14$4 = {
      key: 0,
      class: "invalid-feedback d-block"
    };
    const _hoisted_15$4 = { class: "col-lg-8" };
    const _hoisted_16$4 = { class: "card shadow-sm h-100" };
    const _hoisted_17$4 = { class: "card-header bg-white py-3 d-flex justify-content-between align-items-center" };
    const _hoisted_18$4 = { class: "card-title mb-0 fw-bold" };
    const _hoisted_19$4 = { class: "card-body p-0" };
    const _hoisted_20$4 = {
      key: 0,
      class: "d-flex justify-content-center align-items-center h-100"
    };
    const _hoisted_21$4 = { class: "text-center text-muted" };
    const _hoisted_22$4 = { class: "col-lg-4" };
    const _hoisted_23$4 = { class: "card shadow-sm h-100" };
    const _hoisted_24$4 = { class: "card-header bg-white py-3" };
    const _hoisted_25$3 = { class: "card-title mb-0 fw-bold" };
    const _hoisted_26$3 = {
      key: 0,
      class: "card-body"
    };
    const _hoisted_27$3 = {
      key: 0,
      class: "node-details"
    };
    const _hoisted_28$2 = { class: "mb-4" };
    const _hoisted_29$2 = { class: "fw-bold fs-5 text-black" };
    const _hoisted_30$2 = { class: "row g-3" };
    const _hoisted_31$2 = { class: "col-12" };
    const _hoisted_32$2 = { class: "detail-label" };
    const _hoisted_33$2 = { class: "col-12" };
    const _hoisted_34$2 = { class: "detail-label" };
    const _hoisted_35$2 = ["href"];
    const _hoisted_36$2 = {
      key: 1,
      class: "fw-bold"
    };
    const _hoisted_37$2 = { class: "col-12" };
    const _hoisted_38$2 = { class: "detail-label" };
    const _hoisted_39$1 = ["href"];
    const _hoisted_40$1 = {
      key: 1,
      class: "disabled"
    };
    const _hoisted_41$1 = { class: "col-12" };
    const _hoisted_42$1 = ["href"];
    const _hoisted_43$1 = { class: "detail-label text-primary" };
    const _hoisted_44$1 = { class: "col-12" };
    const _hoisted_45$1 = ["href"];
    const _hoisted_46$1 = { class: "detail-label text-primary" };
    const _hoisted_47 = {
      class: "nav nav-tabs",
      id: "nodeRoleTabs",
      role: "tablist"
    };
    const _hoisted_48 = {
      class: "nav-item",
      role: "presentation"
    };
    const _hoisted_49 = {
      class: "nav-link active",
      "data-bs-toggle": "tab",
      "data-bs-target": "#client",
      type: "button"
    };
    const _hoisted_50 = {
      class: "nav-item",
      role: "presentation"
    };
    const _hoisted_51 = {
      class: "nav-link",
      "data-bs-toggle": "tab",
      "data-bs-target": "#server",
      type: "button"
    };
    const _hoisted_52 = { class: "tab-content pt-3" };
    const _hoisted_53 = ["id"];
    const _hoisted_54 = { key: 0 };
    const _hoisted_55 = { class: "detail-row" };
    const _hoisted_56 = { class: "detail-label" };
    const _hoisted_57 = { class: "detail-value" };
    const _hoisted_58 = { class: "detail-row" };
    const _hoisted_59 = { class: "detail-label" };
    const _hoisted_60 = { class: "detail-value" };
    const _hoisted_61 = { class: "detail-row" };
    const _hoisted_62 = { class: "detail-label" };
    const _hoisted_63 = { class: "detail-value" };
    const _hoisted_64 = { class: "detail-row" };
    const _hoisted_65 = { class: "detail-label" };
    const _hoisted_66 = { class: "detail-value" };
    const _hoisted_67 = { class: "detail-row" };
    const _hoisted_68 = { class: "detail-label" };
    const _hoisted_69 = { class: "detail-value" };
    const _hoisted_70 = { key: 1 };
    const _hoisted_71 = { class: "detail-label" };
    const _hoisted_72 = {
      key: 2,
      class: "alert-summary card bg-light mt-3"
    };
    const _hoisted_73 = { class: "card-body p-3" };
    const _hoisted_74 = { class: "card-subtitle mb-2 text-muted" };
    const _hoisted_75 = {
      class: "progress mb-3",
      style: {"height":"8px"}
    };
    const _hoisted_76 = ["title"];
    const _hoisted_77 = { class: "alert-details" };
    const _hoisted_78 = { class: "mb-4" };
    const _hoisted_79 = { class: "row g-3" };
    const _hoisted_80 = { class: "col-12" };
    const _hoisted_81 = { class: "detail-row" };
    const _hoisted_82 = { class: "detail-label fw-bold" };
    const _hoisted_83 = { class: "detail-value" };
    const _hoisted_84 = { class: "detail-row" };
    const _hoisted_85 = { class: "detail-label fw-bold" };
    const _hoisted_86 = { class: "detail-value" };
    const _hoisted_87 = { class: "detail-row" };
    const _hoisted_88 = { class: "detail-label fw-bold" };
    const _hoisted_89 = { class: "detail-value" };
    const _hoisted_90 = { class: "detail-row" };
    const _hoisted_91 = { class: "detail-label fw-bold" };
    const _hoisted_92 = { class: "detail-value" };
    const _hoisted_93 = { class: "col-12" };
    const _hoisted_94 = { class: "fw-bold" };
    const _hoisted_95 = { class: "ms-2 mb-2" };
    const _hoisted_96 = { class: "detail-row" };
    const _hoisted_97 = { class: "detail-label fw-bold" };
    const _hoisted_98 = { class: "detail-value" };
    const _hoisted_99 = { class: "detail-row" };
    const _hoisted_100 = { class: "detail-label fw-bold" };
    const _hoisted_101 = { class: "detail-value" };
    const _hoisted_102 = { class: "detail-row" };
    const _hoisted_103 = { class: "detail-label fw-bold" };
    const _hoisted_104 = {
      key: 0,
      class: "detail-value fw-bold"
    };
    const _hoisted_105 = {
      key: 1,
      class: "detail-value fw-bold"
    };
    const _hoisted_106 = { class: "col-12" };
    const _hoisted_107 = { class: "fw-bold" };
    const _hoisted_108 = { class: "ms-2" };
    const _hoisted_109 = { class: "detail-row" };
    const _hoisted_110 = { class: "detail-label" };
    const _hoisted_111 = { class: "detail-value" };
    const _hoisted_112 = { class: "detail-row" };
    const _hoisted_113 = { class: "detail-label" };
    const _hoisted_114 = { class: "detail-value" };
    const _hoisted_115 = { class: "detail-row" };
    const _hoisted_116 = { class: "detail-label" };
    const _hoisted_117 = {
      key: 0,
      class: "detail-value fw-bold"
    };
    const _hoisted_118 = {
      key: 1,
      class: "detail-value fw-bold"
    };


    var script$o = {
      __name: 'page-alerts-graph',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const d3 = d3v7;

    const props = __props;

    // State data
    const ifid = String(props.context.ifid);
    const hostDataLoading = ref(true);
    const alerts_graph = ref(null);
    const range_picker = ref(null);
    const loading = ref(true);
    const no_data = ref(false);
    const slider_min_incoming_edges = ref(null);
    const slider_min_outgoing_edges = ref(null);
    const last_url = ref();
    const minIncomingEdges = ref(0); // minimum number of incoming edges (alerts) of a node
    const minOutgoingEdges = ref(0); // minimum number of outgoing edges (alerts) of a node

    // Selected node information (right div next to graph)
    const selectedNodeData = ref({});
    const selectedAlertData = ref({});
    const selectedNode = ref(false);
    const lastClickedElementIsNode = ref(true); // if true last clicked was a node, if false is an edge between nodes
    const activeFlows = ref({ recordsTotal: 0, url: "#" });
    const searchNodeId = ref('');
    const nodeNotFoundMessage = ref(false); // if focus on a node does not find a node, show error

    /* Computed URLs to get host information*/
    const asnPageUrl = computed$1(() => {
        return `${http_prefix}/lua/hosts_stats.lua?asn=${selectedNodeData.value?.host_info?.info?.asn || ''}&version=&network=&traffic_type=&mode=&pool=`;
    });

    const live_flows_url = computed$1(() => {
        return `${http_prefix}/lua/flows_stats.lua?flowhosts_type=${selectedNode.value}%400&l4proto=&application=&alert_type=&qoe=&tcp_flow_state=&dscp=&traffic_type=&host_pool_id=&network=#`;
    });

    const hist_flows_url = computed$1(() => {
        let epoch_begin = get_url_param("epoch_begin");
        let epoch_end = get_url_param("epoch_end");
        return `${http_prefix}/lua/pro/db_search.lua?ifid=${ifid}&epoch_begin=${epoch_begin}&epoch_end=${epoch_end}&aggregated=false&query_preset=&count=THROUGHPUT&ip=${selectedNode.value}%3Beq`;
    });

    const hist_alerts_url = computed$1(() => {
        let epoch_begin = get_url_param("epoch_begin");
        let epoch_end = get_url_param("epoch_end");

        return `${http_prefix}/lua/alert_stats.lua?page=flow&epoch_begin=${epoch_begin}&epoch_end=${epoch_end}&status=any&ifid=${ifid}&query_preset=&count=&ip=${selectedNode.value}%3Beq`;
    });

    /**************************************/

    // D3 Graph data
    let links = [];
    let nodes = [];

    let resizeTimeout;

    let clickTimer = null;
    let lastClickedNode = null;

    /**********************************************/
    const applyFilters = async () => {
        // Security check in order to not reload the component if the filters are the same
        if (last_url.value === window.location.href) return;
        let center_graph_on_ip = null;
        last_url.value = window.location.href;

        // Empty links and nodes to make request to backend
        links = [];
        nodes = [];

        // Draw graph with new filters
        await draw_graph(true, center_graph_on_ip);
    };

    /******************************************************************************/
    /**************************** GRAPH FUNCTIONS ******************************* */

    async function draw_graph(redraw = false, centerIP = null) {
        loading.value = true;
        try {

            if (redraw) {
                // remove svg if there was a new filter
                d3.select(alerts_graph.value).select("svg").remove();
            }

            // remove old tooltips
            $('.tooltip').remove();
            $('[data-toggle="tooltip"]').tooltip('dispose');

            // fetch data on first rendering
            if (links.length === 0 && nodes.length === 0) {
                const data = await get_links_and_nodes();
                links = data.links;
                nodes = data.nodes;
            }

            // redraw graph and links and nodes are not defined
            if (redraw && links.length === 0 && nodes.length === 0) {

                const data = await get_links_and_nodes();

                links = data.links;
                nodes = data.nodes;

                if (nodes.length === 0) {
                    no_data.value = true;
                    loading.value = false;
                    hostDataLoading.value = false;
                    return;

                }
            }

            const width = alerts_graph.value.clientWidth || alerts_graph.value.offsetWidth || alerts_graph.value.getBoundingClientRect().width;
            const height = alerts_graph.value.clientHeight || 500;

            // Create SVG with zoom behavior
            const svg = d3.select(alerts_graph.value)
                .append("svg")
                .attr("width", width)
                .attr("height", height)
                .style("user-select", "none")
                .style("-webkit-user-select", "none")
                .style("-moz-user-select", "none")
                .style("-ms-user-select", "none");

            // Create main group for zoom transformations
            const mainGroup = svg.append("g");

            // Calculate alerts counts for each node
            nodes.forEach(node => {
                node.alert_count = links.filter(link => link.target.id === node.id || link.target === node.id ||
                    link.source.id === node.id || link.source === node.id).length;
            });

            // select as node the one with most alerts
            const maxNode = nodes.reduce((prev, current) => {
                return (prev && prev.alert_count > current.alert_count) ? prev : current;
            }, nodes[0]);

            selectedNode.value = maxNode.id;

            // add filter to url
            add_filter('ip', selectedNode.value);
            await get_host_info();
            activeFlows.value = await get_active_flows();

            // Node color scale based on alert count
            const nodeColorScale = d3.scaleSequential()
                .domain([0, d3.max(nodes, d => d.alert_count) || 1])
                .interpolator(d3.interpolateYlOrRd);

            // Link color scale
            const linkColorScale = d3.scaleThreshold()
                .domain([1, 50, 100])
                .range(["#E0E0E0", "#FFB74D", "#FF9800", "#FF8F00"]);

            // Link color scale for highlighted paths - using more saturated colors
            const highlightColorScale = d3.scaleThreshold()
                .domain([1, 50, 100])
                .range(["#1E88E5", "#1565C0", "#0D47A1", "#0A2472"]);

            // compute nodes position
            const simulation = d3.forceSimulation(nodes)
                .force("link", d3.forceLink(links).id(d => d.id).distance(150))
                .force("charge", d3.forceManyBody().strength(-500))
                .force("center", d3.forceCenter(width / 2, height / 2))
                .force("collision", d3.forceCollide().radius(30))
                .force("x", d3.forceX(width / 2).strength(0.1))
                .force("y", d3.forceY(height / 2).strength(0.1));
            simulation.stop();
            for (let i = 0; i < 300; ++i) simulation.tick();

            // DFS init adjacency list
            const adjacencyList = {};

            nodes.forEach(node => {
                adjacencyList[node.id] = [];
            });

            links.forEach(link => {
                const sourceId = link.source.id || link.source;
                const targetId = link.target.id || link.target;
                adjacencyList[sourceId].push({ targetId, link });
            });

            // Find all outgoing paths from a node, when clicked
            function findOutgoingPathsFromNode(sourceId) {
                const pathLinks = new Set();
                const visited = new Set();

                function dfs(currentId) {
                    if (visited.has(currentId)) return;
                    visited.add(currentId);

                    // get neighbors of current node
                    const neighbors = adjacencyList[currentId] || [];

                    neighbors.forEach(neighbor => {
                        pathLinks.add(neighbor.link);
                        // iterate
                        dfs(neighbor.targetId);
                    });
                }

                // start dfs from node
                dfs(sourceId);
                return pathLinks;
            }

            // Replace the line-based links with path-based curved links
            const link = mainGroup.append("g")
                .selectAll("path")
                .data(links)
                .enter().append("path")
                .attr("class", "link")
                .attr("style", d => {
                    return `stroke: ${linkColorScale(d.weight)} !important`
                })
                .attr("stroke-opacity", 1)
                .attr("fill", "none")
                .attr("stroke-width", 4)
                .attr("stroke-dasharray", null)
                .attr("marker-end", "url(#arrow)")
                .attr("d", d => {
                    const source = d.source;
                    const target = d.target;

                    // Calculate midpoint
                    const midX = (source.x + target.x) / 2;
                    const midY = (source.y + target.y) / 2;

                    // Calculate perpendicular offset
                    const dx = target.x - source.x;
                    const dy = target.y - source.y;
                    const dist = Math.sqrt(dx * dx + dy * dy);

                    // Only apply offset if points aren't too close
                    if (dist > 10) {
                        // Fixed offset - adjust based on your preference
                        const offset = 30;

                        // Calculate the offset coordinates
                        const offsetX = -dy * offset / dist;
                        const offsetY = dx * offset / dist;

                        // Return a simple curved path
                        return `M${source.x},${source.y} Q${midX + offsetX},${midY + offsetY} ${target.x},${target.y}`;
                    } else {
                        // For very close nodes, use a straight line
                        return `M${source.x},${source.y} L${target.x},${target.y}`;
                    }
                })
                .on("click", (event, d) => {
                    event.preventDefault();

                    // last clicked item is an edge
                    lastClickedElementIsNode.value = false;

                    // a link is an alert
                    selectedAlertData.value.alerts_count = d.weight;
                    selectedAlertData.value.alert_type = d.label.alert;
                    selectedAlertData.value.proto = d.label.protocol;
                    selectedAlertData.value.l7 = d.label.l7;

                    selectedAlertData.value.src_ip = d.source.id;
                    selectedAlertData.value.src_asn = d.source.src_asn;
                    selectedAlertData.value.src_country = d.source.src_country;

                    selectedAlertData.value.dst_ip = d.target.id;
                    selectedAlertData.value.dst_asn = d.target.dst_asn;
                    selectedAlertData.value.dst_country = d.target.dst_country;
                });

            // Create a map to track parallel links between the same nodes
            const linkLookup = {};

            // Update path positions with curved links
            link.each(function (d) {
                const sourceId = d.source.id || d.source;
                const targetId = d.target.id || d.target;
                const linkKey = `${sourceId}-${targetId}`;
                const reverseLinkKey = `${targetId}-${sourceId}`;

                // Track number of parallel links
                linkLookup[linkKey] = linkLookup[linkKey] || [];
                d.linkIndex = linkLookup[linkKey].length;
                linkLookup[linkKey].push(d);

                // Count total parallel links
                const totalLinks = linkLookup[linkKey].length;

                // Check if there are also reverse links
                const reverseLinks = linkLookup[reverseLinkKey] || [];
                const totalBidirectionalLinks = totalLinks + reverseLinks.length;

                // Determine curve strength based on the number of links
                // between the same source-target pair
                const curveStrength = Math.min(50, Math.max(20, 15 * totalBidirectionalLinks));

                // Calculate the curvature offset based on this link's index
                const offset = (d.linkIndex - (totalLinks - 1) / 2) * (curveStrength / totalLinks);

                // Determine curved path
                const dx = d.target.x - d.source.x;
                const dy = d.target.y - d.source.y;
                const dr = Math.sqrt(dx * dx + dy * dy);

                // Calculate midpoint with an offset perpendicular to the straight line
                const offsetX = -dy * offset / dr;
                const offsetY = dx * offset / dr;

                // Control point for the curve
                const cpx = d.source.x + dx / 2 + offsetX;
                const cpy = d.source.y + dy / 2 + offsetY;

                // Create the path
                const path = `M${d.source.x},${d.source.y} Q${cpx},${cpy} ${d.target.x},${d.target.y}`;
                d3.select(this).attr("d", path);
            });

            // Update the tooltips
            link.each(function (d) {
                let tooltipContent = `<strong>${d.label.alert}</strong><br>`;

                if (d.alert_info) tooltipContent += `Alert Info: ${d.alert_info}<br>`;

                if (d.label.alerts_count) tooltipContent += `Alerts Count: ${d.label.alerts_count}<br>`;
                if (d.label.avg_score) tooltipContent += `Avg Score: ${d.label.avg_score}<br>`;
                if ((d.label.src_asn) && (d.source.src_asn !== d.source.id)) tooltipContent += `Src ASN: ${d.label.src_asn}<br>`;
                if ((d.label.dst_asn) && (d.target.dst_asn !== d.target.id)) tooltipContent += `Dst ASN: ${d.label.dst_asn}<br>`;

                if (d.label.src_country) {
                    tooltipContent += `Src Country: ${d.label.src_country} <img src='/dist/images/blank.gif' class='flag flag-${d.label.src_country.toLowerCase()}'><br>`;
                }
                if (d.label.dst_country) {
                    tooltipContent += `Dst Country: ${d.label.dst_country} <img src='/dist/images/blank.gif' class='flag flag-${d.label.dst_country.toLowerCase()}'><br>`;
                }

                tooltipContent += `L4 Proto: ${d.label.protocol}<br>L7 App: ${d.label.l7}`;

                // Apply Bootstrap tooltip
                $(this).tooltip({
                    title: tooltipContent,
                    html: true,
                    container: 'body',
                    placement: 'top'
                });
            });

            mainGroup.append("defs").selectAll("marker")
                .data(["arrow", "arrowDotted"])
                .enter().append("marker")
                .attr("id", d => d)
                .attr("viewBox", "0 -5 10 10")
                .attr("refX", 25)
                .attr("refY", 0)
                .attr("markerWidth", 6)
                .attr("markerHeight", 6)
                .attr("orient", "auto")
                .append("path")
                .attr("d", d => d === "arrowDotted" ? "M0,-4L10,0L0,4" : "M0,-5L10,0L0,5")
                .attr("fill", d => d === "arrowDotted" ? "#FF5722" : "#999");

            const nodeGroup = mainGroup.append("g")
                .selectAll("g")
                .data(nodes)
                .enter().append("g")
                .attr("class", "node-group")
                .attr("transform", d => `translate(${d.x}, ${d.y})`)
                .call(drag())
                .style("pointer-events", "all")
                .on("pointerup", async (event, clicked_node) => {
                    event.stopPropagation();
                    event.preventDefault();

                    lastClickedElementIsNode.value = true;
                    selectedNode.value = clicked_node.id;

                    try {
                        // Reset all node styles
                        d3.selectAll(".node-group circle, .node-group path")
                            .attr("stroke", "#212121")
                            .attr("stroke-width", 1);

                        // Highlight selected node

                        d3.select(event.currentTarget).selectAll("circle, path")
                            .attr("stroke", "#FFC107")
                            .attr("stroke-width", 2);

                        // Reset all links to default style
                        d3.selectAll(".link")
                            .attr("style", d => `stroke: ${linkColorScale(d.weight)} !important`)
                            .attr("stroke-width", 8)
                            .attr("stroke-dasharray", null);

                        // Find all paths with the node as source
                        const outgoingPathLinks = findOutgoingPathsFromNode(clicked_node.id);

                        // Highlight outgoing paths
                        d3.selectAll(".link")
                            .filter(d => outgoingPathLinks.has(d))
                            .attr("style", d => `stroke: ${highlightColorScale(d.weight)} !important`)
                            .attr("stroke-width", 10)
                            .attr("stroke-opacity", 4.0);

                        // Dashed lines, outgoing links
                        d3.selectAll(".link")
                            .attr("stroke-dasharray", link =>
                                (link.source.id === clicked_node.id || link.source === clicked_node.id) ? "5,5" : null);

                    } catch (err) {
                        console.error("Error in updating visual:", err);
                    }

                    // Update URL and get host info
                    try {
                        // add filter to url
                        add_filter('ip', clicked_node.id);

                        await new Promise(resolve => setTimeout(resolve, 0));

                        await get_host_info();
                        activeFlows.value = await get_active_flows();
                    } catch (err) {
                        console.error("Error in URL/host update:", err);
                    }
                }).on("dblclick", async function (event, clicked_node) {
                    event.preventDefault();

                    if (clickTimer) {
                        clearTimeout(clickTimer);
                        clickTimer = null;
                    }
                    lastClickedNode = null;

                    selectedNode.value = clicked_node.id;

                    // Filter links where the clicked node ID appears as source or destination
                    const filteredLinks = links.filter(link => {
                        const sourceId = link.source.id || link.source;
                        const targetId = link.target.id || link.target;
                        return sourceId === clicked_node.id || targetId === clicked_node.id;
                    });

                    // Extract the node IDs from filtered links
                    const nodeIds = new Set();
                    filteredLinks.forEach(link => {
                        nodeIds.add(link.source.id || link.source);
                        nodeIds.add(link.target.id || link.target);
                    });

                    // Filter nodes that are part of the filtered links
                    const filteredNodes = nodes.filter(node => nodeIds.has(node.id));

                    // Update global variables
                    nodes = filteredNodes;
                    links = filteredLinks;

                    // add filter to url
                    add_filter('ip', clicked_node.id);
                    await get_host_info();
                    activeFlows.value = await get_active_flows();

                    // Redraw graph with the new filtered data
                    await draw_graph(true, clicked_node.id);
                });

            // Add the node circles with color based on alert count
            const nodeRadius = 10;

            nodeGroup.each(function (d) {
                const group = d3.select(this);

                if (d.is_localhost) {
                    // Circle for local hosts
                    group.append("circle")
                        .attr("r", nodeRadius)
                        .attr("fill", nodeColorScale(d.alert_count))
                        .attr("stroke", "#212121")
                        .attr("stroke-width", 1);
                } else {
                    // Triangle for remote hosts
                    group.append("path")
                        .attr("d", d3.symbol().type(d3.symbolTriangle).size(200)) // size controls area, tweak if needed
                        .attr("fill", nodeColorScale(d.alert_count))
                        .attr("stroke", "#212121")
                        .attr("stroke-width", 1);
                }
            });

            nodeGroup.append("text")
                .attr("x", -nodeRadius - 6)
                .attr("y", 4)
                .attr("text-anchor", "end")
                .attr("font-size", "12px")
                .text(d => d.name || d.id); // render resolved name or ip

            // Bootstrap tooltips to nodes
            nodeGroup.each(function (d) {
                let total_alerts = d.incoming_count + d.outgoing_count;
                $(this).tooltip({
                    title: `<strong>${d.name || d.id}</strong><br>
                Total Alerts: ${total_alerts}<br>
                Incoming: ${d.incoming_count}<br>
                Outgoing: ${d.outgoing_count}`,
                    html: true,
                    container: 'body',
                    placement: 'top'
                });

            });

            // Get node position and extent for minimap
            const xExtent = d3.extent(nodes, d => d.x);
            const yExtent = d3.extent(nodes, d => d.y);

            // Add padding to the extents
            const paddingFactor = 0.1;
            const xPadding = (xExtent[1] - xExtent[0]) * paddingFactor || width * paddingFactor;
            const yPadding = (yExtent[1] - yExtent[0]) * paddingFactor || height * paddingFactor;

            const paddedXExtent = [xExtent[0] - xPadding, xExtent[1] + xPadding];
            const paddedYExtent = [yExtent[0] - yPadding, yExtent[1] + yPadding];

            // Get graph size
            const graphWidth = paddedXExtent[1] - paddedXExtent[0];
            const graphHeight = paddedYExtent[1] - paddedYExtent[0];

            // Set max zoom level (1.0 for 1x)
            const maxZoom = 6.0;
            const minZoom = Math.max(0.1, Math.min(
                width / graphWidth,
                height / graphHeight
            ) * 0.9); // 90% of the scale

            // Create zoom behavior with constraints
            const zoomBehavior = d3.zoom()
                .scaleExtent([minZoom, maxZoom])
                .translateExtent([[paddedXExtent[0], paddedYExtent[0]], [paddedXExtent[1], paddedYExtent[1]]])
                .on("zoom", (event) => {
                    mainGroup.attr("transform", event.transform);
                });

            svg.call(zoomBehavior);

            // Store this in a global variable or access it later
            window.graphZoomBehavior = zoomBehavior;
            // If centerIP is provided, center the graph on that node
            if (centerIP) {
                centerOnNode(centerIP, svg, zoomBehavior, width, height);
            } else {
                // Center and scale the view to fit all nodes
                const padding = 50;

                const xSize = xExtent[1] - xExtent[0] + padding * 2;
                const ySize = yExtent[1] - yExtent[0] + padding * 2;

                const scale = Math.min(
                    maxZoom,
                    Math.max(minZoom, Math.min(width / xSize, height / ySize))
                );

                // Calculate center position
                const tx = width / 2 - (xExtent[0] + xExtent[1]) / 2 * scale;
                const ty = height / 2 - (yExtent[0] + yExtent[1]) / 2 * scale;

                svg.transition().duration(750)
                    .call(zoomBehavior.transform, d3.zoomIdentity.translate(tx, ty).scale(scale));
            }

            loading.value = false;

        } catch (error) {
            console.error("Error drawing graph:", error);
            loading.value = false;
            hostDataLoading.value = false;
        } finally {
            loading.value = false;
        }
    }


    function centerOnNode(nodeId, svg, zoom, width, height) {
        const node = nodes.find(n => n.id === nodeId);
        if (!node) return;

        const x = node.x;
        const y = node.y;

        // Calculate the translation to center the node
        const tx = width / 2 - x * zoom.scale();
        const ty = height / 2 - y * zoom.scale();

        svg.transition().duration(750)
            .call(zoom.transform, d3.zoomIdentity.translate(tx, ty).scale(zoom.scale()));
    }

    function findNode() {

        if (!searchNodeId.value) return;

        const foundNode = nodes.find(node => ((node.id === searchNodeId.value) || (node.name === searchNodeId.value)));

        if (foundNode) {

            selectedNode.value = foundNode.id;

            const svg = d3.select(alerts_graph.value).select("svg");
            const zoom = window.graphZoomBehavior; // reuse the zoom behavior
            svg.select("g"); // assuming your nodes/links are inside a <g> tag

            const newZoom = 3;

            const svgNode = svg.node();
            svgNode.clientWidth || svgNode.getBoundingClientRect().width;
            svgNode.clientHeight || svgNode.getBoundingClientRect().height;

            // First, apply scale
            svg.transition().duration(300)
                .call(zoom.scaleTo, newZoom)
                .transition().duration(300)
                .call(zoom.translateTo, foundNode.x, foundNode.y);

            // Highlight links and node
            d3.selectAll(".link")
                .attr("stroke-dasharray", link =>
                    (link.source.id === foundNode.id || link.source === foundNode.id) ? "5,5" : null);
            /*
            d3.selectAll(".node-group circle")
                .attr("stroke", "#212121")
                .attr("stroke-width", 1);
            */

            d3.selectAll(".node-group")
                .filter(d => d.id === foundNode.id)
                .select("circle")
                .attr("stroke", "#FFC107")
                .attr("stroke-width", 2);

            nodeNotFoundMessage.value = false;
        } else {
            nodeNotFoundMessage.value = true;
        }
    }

    function resetZoom() {
        const svg = d3.select(alerts_graph.value).select("svg");
        const zoom = window.graphZoomBehavior;

        svg.transition()
            .duration(500)
            .call(zoom.transform, d3.zoomIdentity);
    }


    /******************************************************************************/
    /****************************** API GETTERS ********************************* */

    const get_alerts_data = async function () {

        // Create url filters
        let url = `${http_prefix}/lua/pro/rest/v2/get/alert/graph/alerts.lua?`;
        url = create_url(url);

        try {
            let headers = {
                "Content-Type": "application/json",
            };
            const rsp = await ntopng_utility$1.http_request(url, { method: "get", headers });
            no_data.value = false;

            return rsp;

        } catch (err) {
            console.error(err);
        }
    };


    const get_host_info = async function () {
        // Create url filters
        hostDataLoading.value = true;
        let url = `${http_prefix}/lua/pro/rest/v2/get/alert/graph/host_info.lua?`;
        url = create_url(url);

        try {
            let headers = {
                "Content-Type": "application/json",
            };
            const rsp = await ntopng_utility$1.http_request(url, { method: "get", headers });

            if (rsp) {
                no_data.value = false;
                selectedNodeData.value = rsp;
            }
            hostDataLoading.value = false;

            return [];
        } catch (err) {
            console.error(err);
            hostDataLoading.value = false;
        }
    };
    const get_active_flows = async function () {

        try {
            let headers = {
                "Content-Type": "application/json",
            };
            let url = `${http_prefix}/lua/rest/v2/get/flow/active_list.lua?start=0&length=10&map_search=&visible_columns=actions%2Clast_seen%2Cfirst_seen%2Cprotocol%2Cscore%2Cqoe%2Cflow%2Cthroughput%2Cbytes%2Cinfo&flowhosts_type=${selectedNode.value}%400&l4proto=&application=&alert_type=&qoe=&tcp_flow_state=&dscp=&traffic_type=&host_pool_id=&network=`;
            const rsp = await ntopng_utility$1.http_request(url, { method: "get", headers }, false, true);

            if (rsp && rsp.recordsTotal !== undefined) {
                return { recordsTotal: rsp.recordsTotal, live_flows_url };
            }

            return { recordsTotal: 0, live_flows_url };
        } catch (err) {
            console.error(err);
            return { recordsTotal: 0, url: "#" };
        }
    };

    const get_links_and_nodes = async function () {
        const data = await get_alerts_data();
        const links = [];
        const nodesDict = new Map();

        const incomingAlertsCounts = new Map();
        const outgoingAlertsCounts = new Map();

        // compute incoming and outgoing count for each node
        for (let alert of data) {

            const src_ip = alert.src_ip;
            const dst_ip = alert.dst_ip;
            const alertCount = parseInt(alert.alerts_count);

            // outgoing count for source IP (cli_ip)
            const currentOutgoing = outgoingAlertsCounts.get(src_ip) || 0;
            outgoingAlertsCounts.set(src_ip, currentOutgoing + alertCount);

            // incoming count for target IP (srv_ip)
            const currentIncoming = incomingAlertsCounts.get(dst_ip) || 0;
            incomingAlertsCounts.set(dst_ip, currentIncoming + alertCount);
        }

        for (let alert of data) {

            let link = {
                source: alert.src_ip,
                target: alert.dst_ip,
                weight: parseInt(alert.avg_alert_score),
                label: { alert_count: alert.alert_count, alert: alert.alert, avg_score: alert.avg_alert_score, src_asn: alert.src_asn, dst_asn: alert.dst_asn, src_country: alert.src_country, dst_country: alert.dst_country, protocol: alert.l4_proto, l7: alert.l7_app },
                alert_info: alert.info
            };

            links.push(link);

            // prepare node data
            if (!nodesDict.has(alert.src_ip)) {

                let node_data = {
                    id: alert.src_ip,
                    name: alert.src_ip,
                    src_asn: alert.src_asn,
                    src_country: alert.src_country,
                    is_localhost: alert.src_localhost === 1,
                    incoming_count: incomingAlertsCounts.get(alert.src_ip) || 0,
                    outgoing_count: outgoingAlertsCounts.get(alert.src_ip) || 0,
                    // total alerts count for node
                    alert_count: (incomingAlertsCounts.get(alert.src_ip) || 0) +
                        (outgoingAlertsCounts.get(alert.src_ip) || 0)
                };

                if (alert?.src_name) {
                    node_data["name"] = alert.src_name;
                }
                nodesDict.set(alert.src_ip, node_data);
            }

            if (!nodesDict.has(alert.dst_ip)) {
                let node_data = {
                    id: alert.dst_ip,
                    name: alert.dst_ip,
                    dst_asn: alert.dst_asn,
                    dst_country: alert.dst_country,
                    is_localhost: alert.dst_localhost === 1,
                    incoming_count: incomingAlertsCounts.get(alert.dst_ip) || 0,
                    outgoing_count: outgoingAlertsCounts.get(alert.dst_ip) || 0,
                    // total alerts count for node
                    alert_count: (incomingAlertsCounts.get(alert.dst_ip) || 0) +
                        (outgoingAlertsCounts.get(alert.dst_ip) || 0)
                };

                if (alert?.dst_name) {
                    node_data["name"] = alert.dst_name;
                }
                nodesDict.set(alert.dst_ip, node_data);
            }
        }
        const nodes = Array.from(nodesDict.values());

        return { links, nodes };
    };

    /******************************************************************************/
    /****************************** GUI HELPERS ********************************* */
    // Handle resize event
    function resize() {
        clearTimeout(resizeTimeout);
        resizeTimeout = setTimeout(() => {
            draw_graph(true);
        }, 250);
    }

    function drag() {
        return d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended);

        function dragstarted(event, d) {
            // Sync d.x and d.y with the actual position from the transform
            const [x, y] = d3.select(this).attr("transform").match(/translate\(([^,]+),([^)]+)\)/).slice(1).map(Number);
            d.x = x;
            d.y = y;

            $(this).tooltip('hide');
            $(this).tooltip('disable');

            d3.select(this).raise();
        }

        function dragged(event, d) {
            d.x = event.x;
            d.y = event.y;

            // Move group
            d3.select(this).attr("transform", `translate(${d.x}, ${d.y})`);

            // Create a direct reference to the node being dragged
            const draggedNode = d;

            // Use a Map to group links by source-target pair
            const linkGroups = new Map();

            // First pass: group links by their source-target combinations
            d3.selectAll(".link").each(function (linkData, i) {
                if (!linkData) return;

                // Extract source and target IDs
                const sourceId = typeof linkData.source === 'object' ? linkData.source.id : linkData.source;
                const targetId = typeof linkData.target === 'object' ? linkData.target.id : linkData.target;

                // Only process links connected to the dragged node
                if (sourceId === draggedNode.id || targetId === draggedNode.id) {
                    // Create a unique key for each source-target pair
                    const key = sourceId < targetId ?
                        `${sourceId}-${targetId}` :
                        `${targetId}-${sourceId}`;

                    if (!linkGroups.has(key)) {
                        linkGroups.set(key, []);
                    }

                    linkGroups.get(key).push({
                        element: this,
                        linkData: linkData,
                        sourceId: sourceId,
                        targetId: targetId
                    });
                }
            });

            // Second pass: update each group of links with different offsets
            let totalLinks = 0;

            linkGroups.forEach((links, key) => {
                totalLinks += links.length;

                // For each group of links between the same nodes
                links.forEach((pathInfo, groupIndex) => {
                    const element = pathInfo.element;
                    const linkData = pathInfo.linkData;

                    // Update source/target positions
                    if (typeof linkData.source === 'object' && linkData.source) {
                        if (linkData.source.id === draggedNode.id) {
                            linkData.source.x = draggedNode.x;
                            linkData.source.y = draggedNode.y;
                        }
                    }

                    if (typeof linkData.target === 'object' && linkData.target) {
                        if (linkData.target.id === draggedNode.id) {
                            linkData.target.x = draggedNode.x;
                            linkData.target.y = draggedNode.y;
                        }
                    }

                    // Get source and target positions
                    const sourceX = linkData.source.x !== undefined ? linkData.source.x : 0;
                    const sourceY = linkData.source.y !== undefined ? linkData.source.y : 0;
                    const targetX = linkData.target.x !== undefined ? linkData.target.x : 0;
                    const targetY = linkData.target.y !== undefined ? linkData.target.y : 0;

                    // Calculate path with varying offsets for multiple links between same nodes
                    const midX = (sourceX + targetX) / 2;
                    const midY = (sourceY + targetY) / 2;
                    const dx = targetX - sourceX;
                    const dy = targetY - sourceY;
                    const dist = Math.sqrt(dx * dx + dy * dy);

                    // Base offset
                    const baseOffset = 10;

                    // Vary offset by group index for multiple links between same nodes
                    // Links in the same group get progressively larger offsets
                    const offsetMultiplier = links.length > 1 ?
                        (1 + groupIndex * 0.5) : 1;
                    const offset = baseOffset * offsetMultiplier;

                    // Create path
                    let pathD;
                    if (dist > 10) {
                        const offsetX = -dy * offset / dist;
                        const offsetY = dx * offset / dist;
                        pathD = `M${sourceX},${sourceY} Q${midX + offsetX},${midY + offsetY} ${targetX},${targetY}`;
                    } else {
                        pathD = `M${sourceX},${sourceY} L${targetX},${targetY}`;
                    }

                    // Update path
                    d3.select(element).attr("d", pathD);
                });
            });

        }

        function dragended(event, d) {
            // re-enable tooltip
            $(this).tooltip('enable');
        }
    }

    onMounted(async () => {
        // Set default url parameters
        init_url_params();

        // Initially draw the graph
        await draw_graph();

        window.addEventListener("resize", resize);

        // get active flows value
        activeFlows.value = await get_active_flows();

        // Init bootstrap tooltip
        nextTick(() => {
            NtopUtils.reloadBSTooltips();
        });

        const tooltipTriggerminIncomingEdges = new bootstrap.Tooltip(slider_min_incoming_edges.value, { trigger: 'manual' });
        slider_min_incoming_edges.value.addEventListener('input', () => {
            $(".tooltip-inner").text(minIncomingEdges.value);
            slider_min_incoming_edges.value.setAttribute('data-bs-original-title', minIncomingEdges.value);
            tooltipTriggerminIncomingEdges.show();
        });
        slider_min_incoming_edges.value.addEventListener('mouseup', () => {
            applyFilters();
        });

        const tooltipTriggerminOutgoingEdges = new bootstrap.Tooltip(slider_min_outgoing_edges.value, { trigger: 'manual' });
        slider_min_outgoing_edges.value.addEventListener('input', () => {
            $(".tooltip-inner").text(minOutgoingEdges.value);
            slider_min_outgoing_edges.value.setAttribute('data-bs-original-title', minOutgoingEdges.value);
            tooltipTriggerminOutgoingEdges.show();
        });
        slider_min_outgoing_edges.value.addEventListener('mouseup', () => {
            applyFilters();
        });
        last_url.value = window.location.href;
        ntopng_events_manager.on_event_change('range_picker', ntopng_events.FILTERS_CHANGE, (new_status) => { applyFilters(); }, true);
        ntopng_events_manager.on_event_change('range_picker', ntopng_events.EPOCH_CHANGE, (new_status) => { applyFilters(); }, true);
    });

    onBeforeUnmount(() => {
        document.removeEventListener('click', () => { });

    });

    watch(minOutgoingEdges, (newValue) => {
        let min_outgoing_edges = newValue;
        add_filter('min_outgoing', min_outgoing_edges);
    });

    watch(minIncomingEdges, (newValue) => {
        let min_incoming_edges = newValue;
        add_filter('min_incoming', min_incoming_edges);
    });

    watch(searchNodeId, (newValue) => {
        // reset zoom if no node is selected
        if (newValue.length === 0) {
            resetZoom();
        }
    });

    function init_url_params() {
        ntopng_url_manager$1.set_key_to_url("ifid", ifid);
        // This is to retrieve all alerts and not filter on engaged or require attention
        ntopng_url_manager$1.set_key_to_url("status", "any");

        if (ntopng_url_manager$1.get_url_entry("epoch_begin") == null
            || ntopng_url_manager$1.get_url_entry("epoch_end") == null) {
            let now = Date.now();
            let default_epoch_begin = Number.parseInt((now - 1000 * 30 * 60) / 1000);
            let default_epoch_end = Number.parseInt(now / 1000);
            ntopng_url_manager$1.set_key_to_url("epoch_begin", default_epoch_begin);
            ntopng_url_manager$1.set_key_to_url("epoch_end", default_epoch_end);
        }

        // initial filters
        let score_greater_equal = minOutgoingEdges.value + ";gte";
        ntopng_url_manager$1.set_key_to_url("score", score_greater_equal);
        ntopng_url_manager$1.set_key_to_url("severity", "");
        ntopng_url_manager$1.set_key_to_url("ip", "");
        ntopng_url_manager$1.set_key_to_url("min_outgoing", 0);
        ntopng_url_manager$1.set_key_to_url("min_incoming", 0);
    }

    function add_filter(filter, value) {
        ntopng_url_manager$1.set_key_to_url(filter, value);
    }

    function reset_filters() {
        minOutgoingEdges.value = 0;
        minIncomingEdges.value = 0;

        // get all url parameters
        const currentParams = Object.keys(ntopng_url_manager$1.get_url_object());

        // remove all parameters
        ntopng_url_manager$1.delete_params(currentParams);

        init_url_params();

        applyFilters();
    }

    function get_url_param(param) {
        let params = ntopng_url_manager$1.get_url_object();
        for (const param_key in params) {
            if (param === param_key) {
                return params[param];
            }
        }

        return null;
    }
    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    const create_url = (url) => {
        let req_params = get_extra_params_obj();

        let params_inserted = 0;

        for (let param in req_params) {

            if (params_inserted > 0) {
                url += '&';
            }

            url += `${param}=${encodeURIComponent(req_params[param])}`;
            params_inserted += 1;
        }

        return url;
    };

    /******************************************************************************/


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$j, [
        createCommentVNode(" Main Content "),
        createBaseVNode("div", _hoisted_2$f, [
          createCommentVNode(" Filters Card - Vertically stacked "),
          createBaseVNode("div", _hoisted_3$e, [
            createBaseVNode("div", _hoisted_4$d, [
              createVNode(script$2D, {
                ref_key: "range_picker",
                ref: range_picker,
                id: "range-picker",
                enable_refresh: true,
                disabled_date_picker: false,
                min_time_interval_id: "5_min",
                round_time: true
              }, {
                extra_range_buttons: withCtx(() => [
                  createBaseVNode("div", _hoisted_5$c, [
                    createBaseVNode("label", _hoisted_6$9, toDisplayString(_i18n("map_page.asset_in_edges")), 1 /* TEXT */),
                    withDirectives(createBaseVNode("input", {
                      ref_key: "slider_min_incoming_edges",
                      ref: slider_min_incoming_edges,
                      type: "range",
                      class: "form-range",
                      min: "0",
                      max: "1000",
                      "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((minIncomingEdges).value = $event)),
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      title: minIncomingEdges.value
                    }, null, 8 /* PROPS */, _hoisted_7$8), [
                      [vModelText, minIncomingEdges.value]
                    ])
                  ]),
                  createBaseVNode("div", _hoisted_8$8, [
                    createBaseVNode("label", _hoisted_9$8, toDisplayString(_i18n("map_page.asset_out_edges")), 1 /* TEXT */),
                    withDirectives(createBaseVNode("input", {
                      ref_key: "slider_min_outgoing_edges",
                      ref: slider_min_outgoing_edges,
                      type: "range",
                      class: "form-range",
                      min: "0",
                      max: "1000",
                      "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((minOutgoingEdges).value = $event)),
                      "data-bs-toggle": "tooltip",
                      "data-bs-placement": "top",
                      title: minOutgoingEdges.value
                    }, null, 8 /* PROPS */, _hoisted_10$7), [
                      [vModelText, minOutgoingEdges.value]
                    ])
                  ]),
                  createBaseVNode("div", _hoisted_11$6, [
                    createBaseVNode("div", _hoisted_12$5, [
                      createBaseVNode("div", _hoisted_13$4, [
                        withDirectives(createBaseVNode("input", {
                          type: "text",
                          class: normalizeClass(["form-control form-control-sm", { 'is-invalid': nodeNotFoundMessage.value }]),
                          "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((searchNodeId).value = $event)),
                          placeholder: "Center on IP",
                          onKeyup: withKeys(findNode, ["enter"])
                        }, null, 34 /* CLASS, NEED_HYDRATION */), [
                          [vModelText, searchNodeId.value]
                        ]),
                        createBaseVNode("button", {
                          class: "btn btn-sm btn-primary",
                          onClick: findNode
                        }, [...(_cache[3] || (_cache[3] = [
                          createBaseVNode("i", { class: "fas fa-search" }, null, -1 /* CACHED */)
                        ]))])
                      ]),
                      (nodeNotFoundMessage.value)
                        ? (openBlock(), createElementBlock("div", _hoisted_14$4, " Host not present "))
                        : createCommentVNode("v-if", true)
                    ])
                  ])
                ]),
                _: 1 /* STABLE */
              }, 512 /* NEED_PATCH */)
            ])
          ]),
          createCommentVNode(" Graph Visualization Section - Full width when no node selected "),
          createBaseVNode("div", _hoisted_15$4, [
            createBaseVNode("div", _hoisted_16$4, [
              createBaseVNode("div", _hoisted_17$4, [
                createBaseVNode("h5", _hoisted_18$4, toDisplayString(_i18n("alert.graph.alerts_topology")), 1 /* TEXT */),
                createBaseVNode("button", {
                  class: "btn btn-sm btn-outline-secondary",
                  onClick: reset_filters
                }, [...(_cache[4] || (_cache[4] = [
                  createBaseVNode("i", { class: "fa-solid fa-rotate-right" }, null, -1 /* CACHED */)
                ]))])
              ]),
              createBaseVNode("div", _hoisted_19$4, [
                createBaseVNode("div", {
                  ref_key: "alerts_graph",
                  ref: alerts_graph,
                  class: "graph-content d-flex justify-content-center align-items-center"
                }, [
                  createVNode(script$2y, {
                    isLoading: loading.value,
                    class: normalizeClass('mt-1')
                  }, null, 8 /* PROPS */, ["isLoading"]),
                  (no_data.value)
                    ? (openBlock(), createElementBlock("div", _hoisted_20$4, [
                        createBaseVNode("p", _hoisted_21$4, toDisplayString(_i18n("alert.graph.no_data")), 1 /* TEXT */)
                      ]))
                    : createCommentVNode("v-if", true)
                ], 512 /* NEED_PATCH */)
              ])
            ])
          ]),
          createCommentVNode(" Node Details Section - Only shown when a node is selected "),
          createBaseVNode("div", _hoisted_22$4, [
            createCommentVNode(" Node or Alert Details Card "),
            createBaseVNode("div", _hoisted_23$4, [
              createCommentVNode(" Conditional header based on what was clicked "),
              createBaseVNode("div", _hoisted_24$4, [
                createBaseVNode("h5", _hoisted_25$3, toDisplayString(lastClickedElementIsNode.value ? _i18n("alert.graph.node_details") : "Alert Details"), 1 /* TEXT */)
              ]),
              createVNode(script$2y, { isLoading: hostDataLoading.value }, null, 8 /* PROPS */, ["isLoading"]),
              (!hostDataLoading.value)
                ? (openBlock(), createElementBlock("div", _hoisted_26$3, [
                    createCommentVNode(" Node Details Section "),
                    (lastClickedElementIsNode.value)
                      ? (openBlock(), createElementBlock("div", _hoisted_27$3, [
                          createBaseVNode("div", _hoisted_28$2, [
                            createBaseVNode("h6", _hoisted_29$2, [
                              _cache[5] || (_cache[5] = createBaseVNode("i", { class: "fas fa-laptop" }, null, -1 /* CACHED */)),
                              createTextVNode(" " + toDisplayString(selectedNodeData.value?.host_info?.info?.ip || 'N/A'), 1 /* TEXT */)
                            ]),
                            createBaseVNode("div", _hoisted_30$2, [
                              createBaseVNode("div", _hoisted_31$2, [
                                createBaseVNode("span", _hoisted_32$2, toDisplayString(_i18n("alert.graph.country")), 1 /* TEXT */),
                                createBaseVNode("img", {
                                  src: '/dist/images/blank.gif',
                                  class: normalizeClass(["flag", 'flag-' + (selectedNodeData.value?.host_info?.info?.country?.toLowerCase() || '')])
                                }, null, 2 /* CLASS */),
                                createTextVNode(" " + toDisplayString(selectedNodeData.value?.host_info?.info?.country || 'NA'), 1 /* TEXT */)
                              ]),
                              createBaseVNode("div", _hoisted_33$2, [
                                createBaseVNode("span", _hoisted_34$2, toDisplayString(_i18n("alert.graph.asn")), 1 /* TEXT */),
                                (selectedNodeData.value?.host_info?.info?.asn_name !== selectedNodeData.value?.host_info?.info?.ip)
                                  ? (openBlock(), createElementBlock("a", {
                                      key: 0,
                                      href: asnPageUrl.value,
                                      target: "_blank",
                                      class: "fw-bold"
                                    }, toDisplayString(selectedNodeData.value?.host_info?.info?.asn_name), 9 /* TEXT, PROPS */, _hoisted_35$2))
                                  : (openBlock(), createElementBlock("span", _hoisted_36$2, " None "))
                              ]),
                              createBaseVNode("div", _hoisted_37$2, [
                                createBaseVNode("span", _hoisted_38$2, toDisplayString(_i18n("alert.graph.live_flows")), 1 /* TEXT */),
                                (selectedNode.value)
                                  ? (openBlock(), createElementBlock("a", {
                                      key: 0,
                                      href: activeFlows.value.live_flows_url,
                                      target: "_blank",
                                      class: "fw-bold"
                                    }, toDisplayString(activeFlows.value.recordsTotal), 9 /* TEXT, PROPS */, _hoisted_39$1))
                                  : (openBlock(), createElementBlock("a", _hoisted_40$1, "0"))
                              ]),
                              createBaseVNode("div", _hoisted_41$1, [
                                createBaseVNode("a", {
                                  href: hist_flows_url.value,
                                  target: "_blank",
                                  class: "fw-bold"
                                }, [
                                  _cache[6] || (_cache[6] = createBaseVNode("i", { class: "fas fa-lg fa-chart-area me-1" }, null, -1 /* CACHED */)),
                                  createBaseVNode("span", _hoisted_43$1, toDisplayString(_i18n("alert.graph.hist_flows")), 1 /* TEXT */)
                                ], 8 /* PROPS */, _hoisted_42$1)
                              ]),
                              createBaseVNode("div", _hoisted_44$1, [
                                createBaseVNode("a", {
                                  href: hist_alerts_url.value,
                                  target: "_blank",
                                  class: "text-danger fw-bold"
                                }, [
                                  _cache[7] || (_cache[7] = createBaseVNode("i", { class: "fa-solid fa-triangle-exclamation me-1" }, null, -1 /* CACHED */)),
                                  createBaseVNode("span", _hoisted_46$1, toDisplayString(_i18n("alert.graph.hist_alerts")), 1 /* TEXT */)
                                ], 8 /* PROPS */, _hoisted_45$1)
                              ])
                            ])
                          ]),
                          createBaseVNode("ul", _hoisted_47, [
                            createBaseVNode("li", _hoisted_48, [
                              createBaseVNode("button", _hoisted_49, toDisplayString(_i18n("alert.graph.as_client")), 1 /* TEXT */)
                            ]),
                            createBaseVNode("li", _hoisted_50, [
                              createBaseVNode("button", _hoisted_51, toDisplayString(_i18n("alert.graph.as_server")), 1 /* TEXT */)
                            ])
                          ]),
                          createBaseVNode("div", _hoisted_52, [
                            (openBlock(), createElementBlock(Fragment, null, renderList(['client', 'server'], (role) => {
                              return createBaseVNode("div", {
                                key: role,
                                class: normalizeClass(["tab-pane fade", { 'show active': role === 'client' }]),
                                id: role
                              }, [
                                (selectedNodeData.value && selectedNodeData.value.host_info && selectedNodeData.value.host_info[role])
                                  ? (openBlock(), createElementBlock("div", _hoisted_54, [
                                      createBaseVNode("div", _hoisted_55, [
                                        createBaseVNode("span", _hoisted_56, toDisplayString(_i18n("alert.graph.first_seen")), 1 /* TEXT */),
                                        createBaseVNode("span", _hoisted_57, toDisplayString(unref(formatterUtils).formatDateTime(selectedNodeData.value.host_info[role]?.first_seen)
                                                        || '-'), 1 /* TEXT */)
                                      ]),
                                      createBaseVNode("div", _hoisted_58, [
                                        createBaseVNode("span", _hoisted_59, toDisplayString(_i18n("alert.graph.last_seen")), 1 /* TEXT */),
                                        createBaseVNode("span", _hoisted_60, toDisplayString(unref(formatterUtils).formatDateTime(selectedNodeData.value.host_info[role]?.last_seen) ||
                                                        '-'), 1 /* TEXT */)
                                      ]),
                                      createBaseVNode("div", _hoisted_61, [
                                        createBaseVNode("span", _hoisted_62, toDisplayString(_i18n("alert.graph.alerts_count")), 1 /* TEXT */),
                                        createBaseVNode("span", _hoisted_63, toDisplayString(unref(formatterUtils).getFormatter("number")(selectedNodeData.value.host_info[role]?.alerts_count)
                                                        || '-'), 1 /* TEXT */)
                                      ]),
                                      createBaseVNode("div", _hoisted_64, [
                                        createBaseVNode("span", _hoisted_65, toDisplayString(_i18n("alert.graph.total_score")), 1 /* TEXT */),
                                        createBaseVNode("span", _hoisted_66, toDisplayString(unref(formatterUtils).getFormatter("number")(selectedNodeData.value.host_info[role]?.total_score)
                                                        || '-'), 1 /* TEXT */)
                                      ]),
                                      createBaseVNode("div", _hoisted_67, [
                                        createBaseVNode("span", _hoisted_68, toDisplayString(_i18n("alert.graph.total_traffic")), 1 /* TEXT */),
                                        createBaseVNode("span", _hoisted_69, toDisplayString(unref(formatterUtils).getFormatter("bytes")(selectedNodeData.value.host_info[role]?.total_traffic_bytes)), 1 /* TEXT */)
                                      ])
                                    ]))
                                  : (openBlock(), createElementBlock("div", _hoisted_70, [
                                      createBaseVNode("span", _hoisted_71, "No alerts for " + toDisplayString(selectedNode.value) + " as " + toDisplayString(role), 1 /* TEXT */)
                                    ])),
                                (selectedNodeData.value && selectedNodeData.value.host_info && selectedNodeData.value.host_info[role])
                                  ? (openBlock(), createElementBlock("div", _hoisted_72, [
                                      createBaseVNode("div", _hoisted_73, [
                                        createBaseVNode("h6", _hoisted_74, toDisplayString(_i18n("alert.graph.alert_summary")), 1 /* TEXT */),
                                        createBaseVNode("div", _hoisted_75, [
                                          (openBlock(true), createElementBlock(Fragment, null, renderList(selectedNodeData.value.severity_info?.[role], (item, index) => {
                                            return (openBlock(), createElementBlock("div", {
                                              key: index,
                                              class: "progress-bar",
                                              style: normalizeStyle({ width: item.percentage + '%', backgroundColor: item.severity_color }),
                                              role: "progressbar",
                                              "data-bs-toggle": "tooltip",
                                              "data-bs-placement": "top",
                                              title: `${item.percentage.toFixed(2)}% ${item.severity}`
                                            }, null, 12 /* STYLE, PROPS */, _hoisted_76))
                                          }), 128 /* KEYED_FRAGMENT */))
                                        ])
                                      ])
                                    ]))
                                  : createCommentVNode("v-if", true)
                              ], 10 /* CLASS, PROPS */, _hoisted_53)
                            }), 64 /* STABLE_FRAGMENT */))
                          ])
                        ]))
                      : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                          createCommentVNode(" Alert Details Section "),
                          createBaseVNode("div", _hoisted_77, [
                            createBaseVNode("div", _hoisted_78, [
                              createBaseVNode("div", _hoisted_79, [
                                createBaseVNode("div", _hoisted_80, [
                                  createBaseVNode("div", _hoisted_81, [
                                    createBaseVNode("span", _hoisted_82, toDisplayString(_i18n("alert.graph.alert_type")), 1 /* TEXT */),
                                    createBaseVNode("span", _hoisted_83, toDisplayString(selectedAlertData.value?.alert_type), 1 /* TEXT */)
                                  ]),
                                  createBaseVNode("div", _hoisted_84, [
                                    createBaseVNode("span", _hoisted_85, toDisplayString(_i18n("alert.graph.alert_count")), 1 /* TEXT */),
                                    createBaseVNode("span", _hoisted_86, toDisplayString(selectedAlertData.value?.alerts_count), 1 /* TEXT */)
                                  ]),
                                  createBaseVNode("div", _hoisted_87, [
                                    createBaseVNode("span", _hoisted_88, toDisplayString(_i18n("alert.graph.country")), 1 /* TEXT */),
                                    createBaseVNode("span", _hoisted_89, toDisplayString(selectedAlertData.value?.proto), 1 /* TEXT */)
                                  ]),
                                  createBaseVNode("div", _hoisted_90, [
                                    createBaseVNode("span", _hoisted_91, toDisplayString(_i18n("alert_entities.l7")), 1 /* TEXT */),
                                    createBaseVNode("span", _hoisted_92, toDisplayString(selectedAlertData.value?.l7), 1 /* TEXT */)
                                  ])
                                ]),
                                createBaseVNode("div", _hoisted_93, [
                                  createBaseVNode("h6", _hoisted_94, toDisplayString(_i18n("alert.graph.src_info")), 1 /* TEXT */),
                                  createBaseVNode("div", _hoisted_95, [
                                    createBaseVNode("div", _hoisted_96, [
                                      createBaseVNode("span", _hoisted_97, toDisplayString(_i18n("alert.graph.ip")), 1 /* TEXT */),
                                      createBaseVNode("span", _hoisted_98, toDisplayString(selectedAlertData.value?.src_ip || 'N/A'), 1 /* TEXT */)
                                    ]),
                                    createBaseVNode("div", _hoisted_99, [
                                      createBaseVNode("span", _hoisted_100, toDisplayString(_i18n("alert.graph.country")), 1 /* TEXT */),
                                      createBaseVNode("span", _hoisted_101, [
                                        (selectedAlertData.value?.src_country)
                                          ? (openBlock(), createElementBlock("img", {
                                              key: 0,
                                              src: '/dist/images/blank.gif',
                                              class: normalizeClass(["flag", 'flag-' + (selectedAlertData.value?.src_country?.toLowerCase() || '')])
                                            }, null, 2 /* CLASS */))
                                          : createCommentVNode("v-if", true),
                                        createTextVNode(" " + toDisplayString(selectedAlertData.value?.src_country || 'N/A'), 1 /* TEXT */)
                                      ])
                                    ]),
                                    createBaseVNode("div", _hoisted_102, [
                                      createBaseVNode("span", _hoisted_103, toDisplayString(_i18n("alert.graph.asn")), 1 /* TEXT */),
                                      (selectedAlertData.value?.src_asn && selectedAlertData.value?.src_asn !== selectedAlertData.value?.src_ip)
                                        ? (openBlock(), createElementBlock("span", _hoisted_104, toDisplayString(selectedAlertData.value?.src_asn), 1 /* TEXT */))
                                        : (openBlock(), createElementBlock("span", _hoisted_105, " N/A "))
                                    ])
                                  ])
                                ]),
                                createBaseVNode("div", _hoisted_106, [
                                  createBaseVNode("h6", _hoisted_107, toDisplayString(_i18n("alert.graph.dst_info")), 1 /* TEXT */),
                                  createBaseVNode("div", _hoisted_108, [
                                    createBaseVNode("div", _hoisted_109, [
                                      createBaseVNode("span", _hoisted_110, toDisplayString(_i18n("alert.graph.ip")), 1 /* TEXT */),
                                      createBaseVNode("span", _hoisted_111, toDisplayString(selectedAlertData.value?.dst_ip || 'N/A'), 1 /* TEXT */)
                                    ]),
                                    createBaseVNode("div", _hoisted_112, [
                                      createBaseVNode("span", _hoisted_113, toDisplayString(_i18n("alert.graph.country")), 1 /* TEXT */),
                                      createBaseVNode("span", _hoisted_114, [
                                        (selectedAlertData.value?.dst_country)
                                          ? (openBlock(), createElementBlock("img", {
                                              key: 0,
                                              src: '/dist/images/blank.gif',
                                              class: normalizeClass(["flag", 'flag-' + (selectedAlertData.value?.dst_country?.toLowerCase() || '')])
                                            }, null, 2 /* CLASS */))
                                          : createCommentVNode("v-if", true),
                                        createTextVNode(" " + toDisplayString(selectedAlertData.value?.dst_country || 'N/A'), 1 /* TEXT */)
                                      ])
                                    ]),
                                    createBaseVNode("div", _hoisted_115, [
                                      createBaseVNode("span", _hoisted_116, toDisplayString(_i18n("alert.graph.asn")), 1 /* TEXT */),
                                      (selectedAlertData.value?.dst_asn && selectedAlertData.value?.dst_asn !== selectedAlertData.value?.dst_ip)
                                        ? (openBlock(), createElementBlock("span", _hoisted_117, toDisplayString(selectedAlertData.value?.dst_asn), 1 /* TEXT */))
                                        : (openBlock(), createElementBlock("span", _hoisted_118, " N/A "))
                                    ])
                                  ])
                                ])
                              ])
                            ])
                          ])
                        ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
                  ]))
                : createCommentVNode("v-if", true)
            ])
          ])
        ])
      ]))
    }
    }

    };

    var css_248z$c = "\n.dashboard-container[data-v-a55dc50e] {\n    min-height: 60vh;\n    padding: 1.5rem;\n}\n.filter-panel[data-v-a55dc50e] {\n    border-radius: 8px;\n    border: none;\n}\n.graph-content[data-v-a55dc50e] {\n    width: 100%;\n    height: 100%;\n    min-height: 60vh;\n}\n.card[data-v-a55dc50e] {\n    border: none;\n    border-radius: 8px;\n    overflow: hidden;\n}\n.card-header[data-v-a55dc50e] {\n    border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n    padding: 1rem 1.5rem;\n}\n.card-footer[data-v-a55dc50e] {\n    border-top: 1px solid rgba(0, 0, 0, 0.05);\n    padding: 0.75rem 1.5rem;\n}\n.detail-item[data-v-a55dc50e] {\n    display: flex;\n    justify-content: space-between;\n    margin-bottom: 0.75rem;\n}\n.detail-label[data-v-a55dc50e] {\n    color: #6c757d;\n    font-weight: bold;\n}\n.alert-summary[data-v-a55dc50e] {\n    border-radius: 6px;\n}\n.dropdown-menu[data-v-a55dc50e] {\n    max-height: 200px;\n    overflow-y: auto;\n    border: none;\n    border-radius: 6px;\n}\n.dropdown-item[data-v-a55dc50e] {\n    padding: 0.5rem 1rem;\n}\n.dropdown-item[data-v-a55dc50e]:hover {\n    background-color: #f8f9fa;\n}\n.badge[data-v-a55dc50e] {\n    width: 12px;\n    height: 12px;\n    display: inline-block;\n    border-radius: 50%;\n}\n.nav-tabs[data-v-a55dc50e] {\n    border-bottom: 1px solid rgba(0, 0, 0, 0.1);\n}\n.nav-tabs .nav-link[data-v-a55dc50e] {\n    border: none;\n    color: #6c757d;\n    padding: 0.5rem 1rem;\n    font-weight: 500;\n}\n.nav-tabs .nav-link.active[data-v-a55dc50e] {\n    color: #0d6efd;\n    border-bottom: 2px solid #0d6efd;\n    background: transparent;\n}\n.tab-content[data-v-a55dc50e] {\n    padding-top: 1rem;\n}\n.detail-row[data-v-a55dc50e] {\n    display: flex;\n    justify-content: space-between;\n    padding: 5px 0;\n}\n.detail-label[data-v-a55dc50e] {\n    font-weight: bold;\n    flex: 1;\n    text-align: left;\n}\n.detail-value[data-v-a55dc50e] {\n    flex: 1;\n    text-align: right;\n    font-weight: 600;\n}\n.dropdown[data-v-a55dc50e] {\n    position: relative;\n}\n.dropdown-menu.show[data-v-a55dc50e] {\n    display: block;\n    position: absolute;\n    top: 100%;\n    left: 0;\n    z-index: 1000;\n    width: 100%;\n    max-height: 300px;\n    overflow-y: auto;\n    margin-top: 2px;\n}\n";
    styleInject(css_248z$c);

    script$o.__scopeId = "data-v-a55dc50e";
    script$o.__file = "http_src/vue/page-alerts-graph.vue";

    const alerts_url = "/lua/pro/rest/v2/get/alert/geomap/alerts.lua";

    var script$n = {
      __name: 'page-alerts-geomap',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const ifid = String(props.context.ifid);
    const geomapDataArray = ref([]);

    onMounted(async () => {
        init_url_params();
    });

    const create_url = (url) => {
        let req_params = get_extra_params_obj();
        let params_inserted = 0;

        for (let param in req_params) {
            if (params_inserted > 0) {
                url += '&';
            }

            url += `${param}=${encodeURIComponent(req_params[param])}`;
            params_inserted += 1;
        }

        return url;
    };

    function init_url_params() {
        ntopng_url_manager$1.set_key_to_url("ifid", ifid);

        if (ntopng_url_manager$1.get_url_entry("epoch_begin") == null
            || ntopng_url_manager$1.get_url_entry("epoch_end") == null) {
            let now = Date.now();
            let default_epoch_begin = Number.parseInt((now - 1000 * 30 * 60) / 1000);
            let default_epoch_end = Number.parseInt(now / 1000);
            ntopng_url_manager$1.set_key_to_url("epoch_begin", default_epoch_begin);
            ntopng_url_manager$1.set_key_to_url("epoch_end", default_epoch_end);
        }
    }

    const get_extra_params_obj = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    // Fetch and process alert data
    async function getGeomapData() {
        try {

            if (geomapDataArray.value.length > 0) return;
            //build url
            let url = `${http_prefix}${alerts_url}?`;

            url = create_url(url);

            let headers = {
                "Content-Type": "application/json",
            };
            const rsp = await ntopng_utility$1.http_request(url, { method: "get", headers });
            console.log(`Got Data, len: ${geomapDataArray.value.length}`);

            if (rsp) {
                if (rsp.length > 0) {
                    geomapDataArray.value = rsp;
                }
            }

        } catch (error) {
            console.error('Error fetching or processing data:', error);
        }
    }
    const formatTooltipData = (geomapElement, countryName) => {
        const { country_code, alerts_count, severity, color } = geomapElement;

        // Map severity to a Bootstrap badge color
        const severityBadgeColor = {
            "Error": "danger",
            "Warning": "warning",
            "Info": "info"
        }[severity] || "secondary";

        // The country_code is usually what's used for flag sprites (e.g., 'us', 'fr', 'de')
        const flagClass = country_code ? `flag-${country_code.toLowerCase()}` : '';

        return `
        <div class="custom-tooltip-content p-2">
            <div class="tooltip-header mb-2">
                <h6 class="mb-1 fw-bold">
                    <img src='${http_prefix}/dist/images/blank.gif' class='flag ${flagClass}' alt="${countryName} flag">
                    ${countryName}
                </h6>
            </div>
            <hr class="my-2">
            <div class="tooltip-body">
                <div class="row mb-1">
                    <div class="col-6"><strong>Alerts:</strong></div>
                    <div class="col-6 text-end">
                        <span>${alerts_count}</span>
                    </div>
                </div>
                <div class="row">
                    <div class="col-6"><strong>Severity:</strong></div>
                    <div class="col-6 text-end">
                        <span class="badge bg-${severityBadgeColor}">${severity}</span>
                    </div>
                </div>
            </div>
        </div>
    `;
    };


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$16, {
        ifid: unref(ifid),
        tooltipFormatter: formatTooltipData,
        geomapDataArray: geomapDataArray.value,
        getGeomapData: getGeomapData
      }, null, 8 /* PROPS */, ["ifid", "geomapDataArray"]))
    }
    }

    };

    script$n.__file = "http_src/vue/page-alerts-geomap.vue";

    const _hoisted_1$i = { class: "row mb-3 align-items-center" };
    const _hoisted_2$e = { class: "col-md-3" };
    const _hoisted_3$d = {
      for: "profile_name",
      class: "form-label fw-bold mb-0"
    };
    const _hoisted_4$c = { class: "col-md-9" };
    const _hoisted_5$b = {
      key: 0,
      class: "invalid-feedback"
    };
    const _hoisted_6$8 = { class: "row mb-3 align-items-center" };
    const _hoisted_7$7 = { class: "col-md-3" };
    const _hoisted_8$7 = {
      for: "profile_filter",
      class: "form-label fw-bold mb-0"
    };
    const _hoisted_9$7 = { class: "col-md-9" };
    const _hoisted_10$6 = {
      key: 0,
      class: "invalid-feedback"
    };
    const _hoisted_11$5 = { class: "d-flex justify-content-end w-100" };
    const _hoisted_12$4 = ["disabled"];


    var script$m = {
      __name: 'modal-add-traffic-profile',
      props: {
        context: Object,
    },
      emits: ["add", "edit"],
      setup(__props, { expose: __expose, emit: __emit }) {

    const _i18n = (t) => i18n(t);

    const simple_notes = [
        _i18n("traffic_profiles.simple_filter_examples"),
        _i18n("traffic_profiles.http_traffic") + " tcp and port 80",
        _i18n("traffic_profiles.host_traffic") + " host 192.168.1.2",
        _i18n("traffic_profiles.facebook_traffic") + " l7proto Facebook"
    ];

    const advanced_notes = [
        _i18n("traffic_profiles.advanced_filter_examples"),
        _i18n("traffic_profiles.traffic_between") + " ip host 192.168.1.1 and 192.168.1.2",
        _i18n("traffic_profiles.traffic_from_to") + " ip src 192.168.1.1 and dst 192.168.1.2",
        _i18n("traffic_profiles.destination_network") + " ip dst net 192.168.1.0/24",
        _i18n("traffic_profiles.host_http_https") + " ip host 192.168.1.1 and tcp port (80 or 443)",
        _i18n("traffic_profiles.source_ethernet") + " ether src host 00:11:22:33:44:55"
    ];

    const additional_notes = [
        _i18n("traffic_profiles.note"),
        _i18n("traffic_profiles.note_0"),
        _i18n("traffic_profiles.note_1")
    ];

    const modal_id = ref(null);

    // Add and edit emits
    const emit = __emit;

    // Form inputs
    const profile_name = ref("");
    const profile_filter = ref("");
    const name_error = ref("");
    const filter_error = ref("");

    // edit mode and current item
    const isEditMode = ref(false);
    const currentItem = ref(null);

    // check if form is valid
    const is_form_valid = computed$1(() => {
        return profile_name.value &&
            profile_name.value.trim().length > 1 &&
            profile_filter.value &&
            profile_filter.value.trim().length > 0 &&
            !name_error.value &&
            !filter_error.value;
    });

    const validateName = () => {
        const trimmed_name = profile_name.value.trim();

        if (!trimmed_name) {
            name_error.value = _i18n("error_messages.name_cannot_be_empty");
        } else if (trimmed_name.length <= 1) {
            name_error.value = _i18n("error_messages.name_must_be_longer_than_1_character");
        } else if (trimmed_name.includes(' ')) {
            name_error.value = _i18n("error_messages.name_cannot_contain_spaces");
        } else {
            name_error.value = "";
        }
    };

    // validate nBPF filter using backend call, else show input cell error
    async function validate_nBP_filter(){
        const trimmed_filter = profile_filter.value.trim();

        // empty filter is valid
        if (trimmed_filter === "") {
            filter_error.value = "";
            return true;
        }

        let filter_check_url = `${http_prefix}/lua/pro/rest/v2/check/filter.lua?query=${trimmed_filter}`;

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            let res = await ntopng_utility.http_request(filter_check_url, {
                method: 'get',
                headers
            });

            if (res.response === false) {
                filter_error.value = _i18n("traffic_profiles.invalid_bpf");
                return false;
            } else {
                filter_error.value = "";
                return true;
            }

        } catch (e) {
            console.error('Network error:', e.message);
            filter_error.value = _i18n("traffic_profiles.invalid_bpf");
            return false;
        }
    }
    onMounted(() => { });

    // call edit or add emit
    async function handleSubmit() {
        // Validate name
        validateName();
        
        // validate BPF filter
        const filterIsValid = await validate_nBP_filter();

        // Check if form is valid
        if (!is_form_valid.value || !filterIsValid) {
            return;
        }

        const formData = {
            profile_name: profile_name.value.trim(),
            profile_filter: profile_filter.value.trim()
        };

        // Emit to parent component
        if (isEditMode.value) {
            emit('edit', {
                ...formData,
                item: currentItem.value
            });
        } else {
            emit('add', formData);
        }

        // Close the modal
        close();
    }

    // Show modal for adding new item
    const show = () => {
        // Reset form values
        profile_name.value = "";
        profile_filter.value = "";
        name_error.value = "";
        filter_error.value = "";
        isEditMode.value = false;
        currentItem.value = null;

        modal_id.value.show();
    };

    // show modal for editing traffic profile
    const showEdit = async (item) => {
        name_error.value = "";
        filter_error.value = "";

        isEditMode.value = true;
        currentItem.value = item;
        profile_name.value = item.profile_name;
        profile_filter.value = item.profile_filter || "";

        await nextTick();

        // Check if modal reference exists
        if (!modal_id.value) {
            console.error('Modal reference is null after nextTick');
            return;
        }

        modal_id.value.show();
    };

    const close = () => {
        modal_id.value.close();
    };

    // show close and show edit emits
    __expose({ show, showEdit, close });


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(isEditMode.value ? _i18n("traffic_profiles.edit_traffic_profile") :
                    _i18n("traffic_profiles.add_traffic_profile")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" First Row: Profile Name "),
          createBaseVNode("div", _hoisted_1$i, [
            createBaseVNode("div", _hoisted_2$e, [
              createBaseVNode("label", _hoisted_3$d, toDisplayString(_i18n("traffic_profiles.profile_name")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_4$c, [
              withDirectives(createBaseVNode("input", {
                id: "profile_name",
                type: "text",
                class: normalizeClass(["form-control", { 'is-invalid': name_error.value }]),
                "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((profile_name).value = $event)),
                placeholder: "Enter profile name",
                onInput: validateName,
                required: ""
              }, null, 34 /* CLASS, NEED_HYDRATION */), [
                [vModelText, profile_name.value]
              ]),
              (name_error.value)
                ? (openBlock(), createElementBlock("div", _hoisted_5$b, toDisplayString(name_error.value), 1 /* TEXT */))
                : createCommentVNode("v-if", true)
            ])
          ]),
          createCommentVNode(" Second Row: Profile Filter "),
          createBaseVNode("div", _hoisted_6$8, [
            createBaseVNode("div", _hoisted_7$7, [
              createBaseVNode("label", _hoisted_8$7, toDisplayString(_i18n("traffic_profiles.traffic_filter_bpf")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_9$7, [
              withDirectives(createBaseVNode("input", {
                id: "profile_filter",
                type: "text",
                class: normalizeClass(["form-control", { 'is-invalid': filter_error.value }]),
                "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((profile_filter).value = $event)),
                placeholder: "Enter filter in nbpf format",
                onInput: validate_nBP_filter,
                required: ""
              }, null, 34 /* CLASS, NEED_HYDRATION */), [
                [vModelText, profile_filter.value]
              ]),
              (filter_error.value)
                ? (openBlock(), createElementBlock("div", _hoisted_10$6, toDisplayString(filter_error.value), 1 /* TEXT */))
                : createCommentVNode("v-if", true)
            ])
          ]),
          createVNode(script$2u, { note_list: simple_notes }),
          createVNode(script$2u, { note_list: advanced_notes }),
          createVNode(script$2u, { note_list: additional_notes })
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", _hoisted_11$5, [
            createBaseVNode("button", {
              type: "button",
              onClick: handleSubmit,
              class: "btn btn-primary",
              disabled: !is_form_valid.value
            }, toDisplayString(isEditMode.value ? _i18n("save") : _i18n("add")), 9 /* TEXT, PROPS */, _hoisted_12$4)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$m.__file = "http_src/vue/modal-add-traffic-profile.vue";

    const _hoisted_1$h = { class: "m-2 mb-3" };
    const _hoisted_2$d = ["title"];


    var script$l = {
      __name: 'page-traffic-profiles',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const ifid = props.context.ifid;
    const areTsEnabled = props.context.areTsEnabled;

    const _i18n = (t) => i18n(t);
    const table_traffic_rules = ref(null);
    const trafficProfileModal = ref(null);

    const table_id = ref('traffic_profiles');

    const delete_profile_url = `${http_prefix}/lua/pro/rest/v2/delete/filters/traffic_profile.lua`;
    const add_profile_url = `${http_prefix}/lua/pro/rest/v2/add/filters/traffic_profile.lua`;

    /* ******************************************************************** */
    const map_table_def_columns = (columns) => {

        columns.forEach((c) => {

            if (c.id == "actions") {
                const visible_dict = {
                    edit_rule: true,
                    timeseries: areTsEnabled,
                    delete_rule: true
                };

                c.button_def_array.forEach((b) => {
                    b.f_map_class = (current_class, row) => {
                        // if is not defined is enabled
                        if (!visible_dict[b.id]) {
                            current_class.push("disabled");
                        }
                        return current_class;
                    };
                });
            }
        });

        return columns;
    };
    /* ******************************************************************** */

    /* Function to add a new host to scan */
    function add_profile() {
        trafficProfileModal.value.show(null, props.context.host);
    }

    /* ************************************** */

    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "profileName") {
                return sortingFunctions$1.sortByName(r0.profileName, r1.profileName, col.sort);
            }
        }
    }

    /* ************************************** */

    const showEditModal = (item) => {
        trafficProfileModal.value.showEdit(item);
    };

    /* ************************************** */
    // delete the traffic profile
    async function click_button_delete_profile(event) {
        const profile_name = event.row.profileName;

        const requestParams = {
            csrf: props.context.csrf,
            delete_profile: profile_name,
            ifid: ifid
        };

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            await ntopng_utility.http_request(delete_profile_url, {
                method: 'post',
                headers,
                body: JSON.stringify(requestParams)
            });

            // Refresh the table
            table_traffic_rules.value.refresh_table(true);
        } catch (e) {
            console.error('Network error:', e.message);
        }
    }

    /* ************************************** */

    function on_table_custom_event(event) {
        let events_managed = {
            "click_button_edit_profile": click_button_edit_profile,
            "click_button_delete_profile": click_button_delete_profile,
            "click_button_timeseries": click_button_timeseries,
        };
        if (events_managed[event.event_id] == null) {
            return;
        }
        events_managed[event.event_id](event);
    }

    // add new traffic profile
    const handleAddProfile = async (data) => {
        let profile_name = data.profile_name;
        let profile_filter = data.profile_filter;

        const requestParams = {
            csrf: props.context.csrf,
            profiles: [{ name: profile_name, filter: profile_filter }]
        };

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            await ntopng_utility.http_request(add_profile_url, {
                method: 'post',
                headers,
                body: JSON.stringify(requestParams)
            });

            // Refresh the table
            table_traffic_rules.value.refresh_table(true);
        } catch (e) {
            console.error('Network error:', e.message);
        }
    };

    // show edit modal when clicking edit on actions dropdown
    const click_button_edit_profile = (event) => {
        const profile_data = {
            profile_name: event.row.profileName,
            profile_filter: event.row.profileFilter || ""
        };

        showEditModal(profile_data);
    };

    function click_button_timeseries(event) {
        let profileName = event.row.profileName;
        window.location.href = `${http_prefix}/lua/pro/profile_details.lua?profile=${profileName}`;
    }

    const handleEditProfile = async (data) => {
        // data = { profile_name, profile_filter, item }
        const old_profile_name = data.item.profile_name;
        const new_profile_name = data.profile_name;
        const new_profile_filter = data.profile_filter;

        let headers = {
            'Content-Type': 'application/json'
        };

        try {
            // Delete the old profile
            const deleteParams = {
                csrf: props.context.csrf,
                delete_profile: old_profile_name,
                ifid: ifid
            };

            await ntopng_utility.http_request(delete_profile_url, {
                method: 'post',
                headers,
                body: JSON.stringify(deleteParams)
            });

            // create new profile
            const addParams = {
                csrf: props.context.csrf,
                profiles: [{
                    name: new_profile_name,
                    filter: new_profile_filter
                }]
            };

            await ntopng_utility.http_request(add_profile_url, {
                method: 'post',
                headers,
                body: JSON.stringify(addParams)
            });

            // Refresh the table
            table_traffic_rules.value.refresh_table(true);

        } catch (e) {
            console.error('Error during profile edit:', e);
            table_traffic_rules.value.refresh_table(true);
        }
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$h, [
        createVNode(script$2w, {
          ref_key: "table_traffic_rules",
          ref: table_traffic_rules,
          table_id: table_id.value,
          csrf: __props.context.csrf,
          f_sort_rows: columns_sorting,
          onCustom_event: on_table_custom_event,
          f_map_columns: map_table_def_columns
        }, {
          custom_buttons: withCtx(() => [
            createBaseVNode("button", {
              class: "btn btn-link",
              type: "button",
              onClick: add_profile
            }, [
              createBaseVNode("i", {
                class: "fas fa-plus",
                "data-bs-toggle": "tooltip",
                "data-bs-placement": "top",
                title: _i18n('policy.add_profile')
              }, null, 8 /* PROPS */, _hoisted_2$d)
            ])
          ]),
          _: 1 /* STABLE */
        }, 8 /* PROPS */, ["table_id", "csrf"]),
        createCommentVNode("\n        <div class=\"card-footer mt-3\">\n            <button v-if=\"false\" type=\"button\" ref=\"export_rules\" @click=\"export_rules\" class=\"btn btn-primary ms-1\">\n                <i class=\"fas fa-file-export\"></i>\n                {{ _i18n(\"acl_page.export_rules\") }}\n            </button>\n            \n        </div>\n        "),
        createVNode(script$m, {
          ref_key: "trafficProfileModal",
          ref: trafficProfileModal,
          onAdd: handleAddProfile,
          onEdit: handleEditProfile
        }, null, 512 /* NEED_PATCH */)
      ]))
    }
    }

    };

    script$l.__file = "http_src/vue/page-traffic-profiles.vue";

    const _hoisted_1$g = { class: "m-2 mb-3" };


    var script$k = {
      __name: 'page-networks-list',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const props = __props;

    const note_list = [
        _i18n("network_stats.note_overlapping_networks"),
        _i18n("network_stats.note_see_both_network_entries"),
        _i18n("network_stats.note_broader_network")
    ];

    props.context.areTsEnabled;
    const csrf = props.context.csrf;
    const table_networks_stats = ref(null);
    const table_id = ref('networks_list');

    const map_table_def_columns = (columns) => {

        let map_columns = {
            "networkName": (value, row) => {
                const network_url = `${http_prefix}/lua/hosts_stats.lua?network=${row.networkId}`;
                const network_config_url = `${http_prefix}/lua/network_details.lua?network=${row.networkId}&page=config`;
                const network_ts_url = `${http_prefix}/lua/network_details.lua?network=${row.networkId}&page=historical`;

                // Create href with network name and icons
                let href = `<a href="${network_url}">${value}</a>`;
                const net_config_href = `&nbsp;<a href="${network_config_url}"><i class="fas fa-cog"></i></a>`;
                const ts_icon_href = `&nbsp;<a href="${network_ts_url}"><i class="fas fa-chart-area"></i></a>`;

                href += net_config_href;
                href += ts_icon_href;
                return href
            },
            "hosts": (value, row) => {
                return formatterUtils.getFormatter("number")(value);
            },
            "score": (value, row) => {
                return formatterUtils.getFormatter("number")(value);
            },
            "hostsScoreRatio": (value, row) => {
                return formatterUtils.getFormatter("number")(value);
            },
            "alertedFlows": (value, row) => {
                return formatterUtils.getFormatter("number")(value);
            },
            "breakdown": (value, row) => {
                return NtopUtils$1.createBreakdown(row.breakdown.percentage_bytes_sent, row.breakdown.percentage_bytes_rcvd, "Sent", "Rcvd")
            },
            "throughput": (value, row) => {
                return formatterUtils.getFormatter("bps")(value);
            },
            "traffic": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value);
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
            if (c.id === "charts_enabled") {
                const visible_dict = {
                    historical_data: props.context.show_historical,
                };

                c.button_def_array.forEach((b) => {

                    if (!visible_dict[b.id]) {
                        b.class.push("disabled");
                    }
                });
            }
        });

        return columns;
    };


    function columns_sorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "network_name") {
                return sortingFunctions$1.sortByName(r0.networkName, r1.networkName, col.sort);
            } else if (col.id == "hosts") {
                return sortingFunctions$1.sortByNumber(r0.hosts, r1.hosts, col.sort);
            } else if (col.id == "score") {
                return sortingFunctions$1.sortByNumber(r0.score, r1.score, col.sort);
            } else if (col.id == "hosts_score_ratio") {
                return sortingFunctions$1.sortByNumber(r0.score, r1.score, col.sort);
            } else if (col.id == "throughput") {
                return sortingFunctions$1.sortByNumber(r0.hostsScoreRatio, r1.hostsScoreRatio, col.sort);
            } else if (col.id == "alerted_flows") {
                return sortingFunctions$1.sortByNumber(r0.alertedFlows, r1.alertedFlows, col.sort);
            } else if (col.id == "throughput") {
                return sortingFunctions$1.sortByNumber(r0.throughput, r1.throughput, col.sort);
            } else if (col.id == "traffic") {
                return sortingFunctions$1.sortByNumber(r0.traffic, r1.traffic, col.sort);
            }
        }

    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$g, [
          createVNode(script$2w, {
            ref_key: "table_networks_stats",
            ref: table_networks_stats,
            table_id: table_id.value,
            csrf: unref(csrf),
            f_map_columns: map_table_def_columns,
            f_sort_rows: columns_sorting
          }, null, 8 /* PROPS */, ["table_id", "csrf"])
        ]),
        createVNode(script$2u, { note_list: note_list })
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    script$k.__file = "http_src/vue/page-networks-list.vue";

    const _hoisted_1$f = { class: "m-2 mb-3" };


    var script$j = {
      __name: 'page-DHCP-leases',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const table_id = ref('dhcp_leases');
    const table_dhcp_leases = ref(null);
    const csrf = props.context.csrf;
    const ifid = props.context.ifid;

    const map_table_def_columns = (columns) => {

        let map_columns = {
            "macAddress": (value, row) => {
                const mac_details_url = `${http_prefix}/lua/mac_details.lua?host=${value}&ifid=${ifid}`;

                return `<a href=${mac_details_url}>${value}</a>`
            },
            "leasedIP": (value, row) => {
                const host_info_url = `${http_prefix}/lua/host_details.lua?host=${value}&mac=${row.macAddress}&ifid=${ifid}`;
                return `<a href=${host_info_url}>${value}</a>`
            }
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });

        return columns;
    };


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$f, [
        createVNode(script$2w, {
          ref_key: "table_dhcp_leases",
          ref: table_dhcp_leases,
          table_id: table_id.value,
          csrf: unref(csrf),
          f_map_columns: map_table_def_columns,
          f_sort_rows: _ctx.columns_sorting
        }, null, 8 /* PROPS */, ["table_id", "csrf", "f_sort_rows"])
      ]))
    }
    }

    };

    script$j.__file = "http_src/vue/page-DHCP-leases.vue";

    const _hoisted_1$e = { class: "button-group mb-2 d-flex align-items-center" };
    const _hoisted_2$c = { class: "dropdown me-2 d-flex" };
    const _hoisted_3$c = { class: "no-wrap d-flex align-items-center filters-label me-2" };
    const _hoisted_4$b = {
      key: 0,
      class: "w-100 d-flex align-items-center button-group"
    };
    const _hoisted_5$a = { class: "w-100 position-relative" };
    const _hoisted_6$7 = { class: "m-2 mb-3" };
    const _hoisted_7$6 = { class: "position-relative" };
    const _hoisted_8$6 = {
      class: "mb-4 d-flex flex-column",
      style: {"height":"60vh"}
    };
    const _hoisted_9$6 = { class: "card-footer" };


    var script$i = {
      __name: 'page-exporter-interface-overview',
      props: {
        context: Object,
    },
      setup(__props) {

    const props = __props;

    const _i18n = (t) => i18n(t);
    const sankey_url = `${http_prefix}/lua/pro/rest/v2/get/exporters/exporter_as_sankey.lua`;
    const sankey_chart = ref(null);
    const sankey_data = ref({});
    const loading = ref(true);
    const no_data_message = _i18n("as_overview.no_data");
    let intervalId = null;
    const table_exporter_as_stats = ref(null);
    const reRenderTable = ref(false);
    const main_epoch_interval = ref(null);
    const first_date_epoch = ref(props.context.first_date_epoch);
    const active_sankey_type = ref({});
    const toggle_slider = ref(false);
    const toggle_slider_label = ref(_i18n("db_search.time_range"));
    const time_preset_list = [
        { value: "live", label: i18n('live'), currently_active: true },
        { value: "2_hours", label: i18n('show_alerts.presets.2_hours'), currently_active: false },
        { value: "12_hours", label: i18n('show_alerts.presets.12_hours'), currently_active: false },
        { value: "day", label: i18n('show_alerts.presets.day'), currently_active: false },
        { value: "week", label: i18n('show_alerts.presets.week'), currently_active: false },
        { value: "month", label: i18n('show_alerts.presets.month'), currently_active: false },
        { value: "year", label: i18n('show_alerts.presets.year'), currently_active: false },
        { value: "custom", label: i18n('show_alerts.presets.custom'), currently_active: false, disabled: true, },
    ];
    const sankey_format_list = [
        { key: "criteria_exporter_interface_view", value: 'traffic_with_ases', label: _i18n('exporter_interface_overview.as_view') },
    ];
    const note_list = [
        _i18n("exporter_interface_overview.note_ingress_egress"),
    ];

    /* ************************************** */

    const table_id = computed$1(() => {
        return 'table_exporter_as_stats';
    });

    /* ************************************** */

    onMounted(() => {
        updateSankeyData();
    });

    /* ************************************** */

    onBeforeMount(() => {
        active_sankey_type.value = sankey_format_list[0];
        ntopng_url_manager$1.set_key_to_url("criteria_exporter_interface_view", active_sankey_type.value.value);
        toggle_slider.value = localStorage.getItem("exporter-as-overview-slider") == "true";
    });

    /* ************************************** */

    const changeTime = (selectedTimeframe) => {
        let interval = 0; // Live
        if (selectedTimeframe !== 'live') {
            const timeframesDict = ntopng_utility.get_timeframes_dict();
            interval = timeframesDict[selectedTimeframe];
        }
        const epoch_end = ntopng_utility.get_utc_seconds(Date.now());
        const epoch_begin = epoch_end - interval;
        return [epoch_begin, epoch_end]
    };

    /* ************************************** */

    /* This function is called upon changing the selected option in the dropdown */
    const changeCriteria = async (opt) => {
        ntopng_url_manager$1.set_key_to_url(opt.key, `${opt.value}`);
        updateSankeyData();
    };

    /* ************************************** */

    const onAutoRefreshToggle = (enabled) => {
        if (enabled) {
            intervalId = setInterval(() => {
                updateSankeyData();
            }, 10000 /* 10 sec refresh */);
        } else {
            clearInterval(intervalId);
        }
    };

    /* ************************************** */

    function reloadTable() {
        table_exporter_as_stats.value.refresh_table();
    }

    /* ************************************** */

    function setTimeInterval(epoch_interval) {
        // Check if it's live
        if (epoch_interval.isToday) {
            ntopng_url_manager$1.delete_key_from_url("type");
        } else {
            ntopng_url_manager$1.set_key_to_url("type", "historical");
        }

        if (epoch_interval.timeframe_id === 'live' || (epoch_interval.epoch_begin === epoch_interval.epoch_end)) {
            ntopng_url_manager$1.delete_key_from_url("type");
            ntopng_url_manager$1.delete_key_from_url("timeframe_id");
        }
        main_epoch_interval.value = epoch_interval;
        updateSankeyData();
        reloadTable();
    }

    /* ************************************** */

    function saveSwitch() {
        localStorage.setItem("exporter-as-overview-slider", toggle_slider.value);
    }

    /* ************************************** */

    const updateSankeyData = async () => {
        loading.value = true;
        const data = await getSankeyData();
        sankey_data.value = data;
        loading.value = false;
    };

    /* ************************************** */

    const getSankeyData = async () => {
        const url_request = getSankeyUrl();
        let graph = await ntopng_utility.http_request(url_request);
        graph.nodes.forEach((node, i) => {
            node.index = i;
        });
        graph.links.forEach((link, i) => {
            if (link.value === 0) {
                link.value = 1;
            }
            let node = graph.nodes.find((el) => el.node_id == link.source_node_id);
            link.source = node.index;
            node = graph.nodes.find((el) => el.node_id == link.target_node_id);
            link.target = node.index;
        });

        return graph
    };

    /* ************************************** */

    const getSankeyUrl = () => {
        let params = {
            ifid: props.context.ifid,
            ...getExtraParameters()
        };
        let url_params = ntopng_url_manager$1.obj_to_url_params(params);
        let url_request = `${sankey_url}?${url_params}`;
        return url_request;
    };

    /* ************************************** */

    function onNodeClick(_, node) {
        if (node.link) {
            ntopng_url_manager$1.go_to_url(node.link);
        }
    }

    /* ***************************************************** */

    const getExtraParameters = () => {
        let extra_params = ntopng_url_manager$1.get_url_object();
        return extra_params;
    };

    /* ************************************** */

    function columnsSorting(col, r0, r1) {
        if (col != null) {
            if (col.id == "device") {
                return sortingFunctions$1.sortByName(r0.device.name, r1.device.name, col.sort);
            } else if (col.id == "interface") {
                return sortingFunctions$1.sortByName(r0.interface.name, r1.interface.name, col.sort);
            } else if (col.id == "customer") {
                return sortingFunctions$1.sortByName(r0.customer.name, r1.customer.name, col.sort);
            } else if (col.id == "as") {
                return sortingFunctions$1.sortByName(r0.as.name, r1.as.name, col.sort);
            } else if (col.id == "dst_as") {
                return sortingFunctions$1.sortByName(r0.dst_as.name, r1.dst_as.name, col.sort);
            } else if (col.id == "src_as") {
                return sortingFunctions$1.sortByName(r0.src_as.name, r1.src_as.name, col.sort);
            } else if (col.id == "src_transit_as") {
                return sortingFunctions$1.sortByName(r0.src_transit_as?.name || "", r1.src_transit_as?.name || "", col.sort);
            } else if (col.id == "dst_transit_as") {
                return sortingFunctions$1.sortByName(r0.dst_transit_as?.name || "", r1.dst_transit_as?.name || "", col.sort);
            } else if (col.id == "transit_as") {
                return sortingFunctions$1.sortByName(r0.transit_as?.name || "", r1.transit_as?.name || "", col.sort);
            } else if (col.id == "bytes_sent") {
                return sortingFunctions$1.sortByNumber(r0.bytes_sent, r1.bytes_sent, col.sort);
            } else if (col.id == "bytes_rcvd") {
                return sortingFunctions$1.sortByNumber(r0.bytes_rcvd, r1.bytes_rcvd, col.sort);
            } else if (col.id == "total_bytes") {
                return sortingFunctions$1.sortByNumber(r0.total_bytes, r1.total_bytes, col.sort);
            }
        }
        /* Default sorting */
        return sortingFunctions$1.sortByNumber(r0.total_bytes, r1.total_bytes, col.sort);
    }

    /* ************************************** */

    const formatAS = (value) => {
        if (!value) {
            return ''
        }
        let addTitle = true;
        let importantASNIcon = '';
        const title = `data-bs-toggle="tooltip" data-bs-placement="top" title="${value.id}"`;
        if (value.is_customer_asn) {
            importantASNIcon = customerIcon;
        } else if (value.is_sub_customer_asn) {
            importantASNIcon = subCustomerIcon;
        } else if (value.is_remote_asn) {
            importantASNIcon = remoteIcon;
        }
        if (dataUtils.isEmptyString(value.name)) {
            addTitle = false;
        }
        if (!dataUtils.isEmptyString(value.url)) {
            return `<a href="${value.url}" ${addTitle ? title : ""}>${value.name}</a> ${importantASNIcon}`;
        }
        return `<span ${addTitle ? title : ""}>${value.name}</span> ${importantASNIcon}`;
    };

    /* ************************************** */

    const mapTableColumns = (columns) => {
        let map_columns = {
            "device": (value, row) => {
                if (dataUtils.isEmptyString(value.name)) {
                    return value.id
                }
                if (!dataUtils.isEmptyString(value.url)) {
                    return `<a href="${value.url}" title="${value.id}">${value.name}</a>`
                }
                return `<span data-bs-toggle="tooltip" data-bs-placement="top" title="${value.id}">${value.name}</span>`;
            },
            "interface": (value, row) => {
                if (dataUtils.isEmptyString(value.name)) {
                    return value.id
                }
                if (!dataUtils.isEmptyString(value.url)) {
                    return `<a href="${value.url}" title="${value.id}">${value.name}</a>`
                }
                return `<span data-bs-toggle="tooltip" data-bs-placement="top" title="${value.id}">${value.name}</span>`;
            },
            "as": (value, row) => {
                return formatAS(value);
            },
            "transit_as": (value, row) => {
                return formatAS(value);
            },
            "bytes_sent": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value || 0);
            },
            "bytes_rcvd": (value, row) => {
                return formatterUtils.getFormatter("bytes")(value || 0);
            },
            "total_bytes": (value, row) => {
                if (value) {
                    return formatterUtils.getFormatter("bytes")(value);
                }

                if (row.bytes_rcvd || row.bytes_sent) {
                    let bytes_sent = 0;
                    let bytes_rcvd = 0;
                    if (row.bytes_rcvd) { bytes_rcvd = row.bytes_rcvd; }
                    if (row.bytes_sent) { bytes_sent = row.bytes_sent; }
                    return formatterUtils.getFormatter("bytes")(bytes_rcvd + bytes_sent);
                }

                return formatterUtils.getFormatter("bytes")(0);
            },
        };

        columns.forEach((c) => {
            c.render_func = map_columns[c.data_field];
        });

        return columns;
    };

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$e, [
          createBaseVNode("div", _hoisted_2$c, [
            createBaseVNode("span", _hoisted_3$c, [
              createBaseVNode("b", null, toDisplayString(_i18n("view_options")) + ": ", 1 /* TEXT */)
            ]),
            createVNode(script$2H, {
              selected_option: active_sankey_type.value,
              "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((active_sankey_type).value = $event)),
              options: sankey_format_list,
              onSelect_option: changeCriteria
            }, null, 8 /* PROPS */, ["selected_option"])
          ]),
          createBaseVNode("div", {
            class: normalizeClass(["button-group d-flex align-items-center", { 'w-100': !toggle_slider.value, 'w-25': toggle_slider.value }])
          }, [
            (props.context.isEnterpriseL)
              ? (openBlock(), createElementBlock("div", _hoisted_4$b, [
                  createVNode(script$2k, {
                    value: toggle_slider.value,
                    "onUpdate:value": _cache[1] || (_cache[1] = $event => ((toggle_slider).value = $event)),
                    change_label_side: true,
                    label: toggle_slider_label.value,
                    style: {},
                    class: "me-1",
                    icon: "fa-calendar-days",
                    title: toggle_slider_label.value,
                    onChange_value: saveSwitch
                  }, null, 8 /* PROPS */, ["value", "label", "title"]),
                  createBaseVNode("div", _hoisted_5$a, [
                    createVNode(Transition, {
                      name: "add-effect",
                      mode: "out-in"
                    }, {
                      default: withCtx(() => [
                        (!toggle_slider.value)
                          ? (openBlock(), createBlock(script$2G, {
                              key: 0,
                              class: "dontprint",
                              id: "as-date-time-picker",
                              round_time: true,
                              custom_time_interval_list: time_preset_list,
                              min_time_interval_id: "live",
                              custom_change_select_time: changeTime,
                              onEpoch_change: setTimeInterval
                            }))
                          : createCommentVNode("v-if", true)
                      ]),
                      _: 1 /* STABLE */
                    }),
                    createVNode(Transition, {
                      name: "add-effect",
                      mode: "out-in"
                    }, {
                      default: withCtx(() => [
                        (toggle_slider.value)
                          ? (openBlock(), createBlock(script$T, {
                              key: 0,
                              id: "as-date-slider",
                              min_epoch: first_date_epoch.value,
                              onEpoch_change: setTimeInterval,
                              style: {"width":"100%"}
                            }, null, 8 /* PROPS */, ["min_epoch"]))
                          : createCommentVNode("v-if", true)
                      ]),
                      _: 1 /* STABLE */
                    })
                  ])
                ]))
              : createCommentVNode("v-if", true)
          ], 2 /* CLASS */)
        ]),
        createBaseVNode("div", _hoisted_6$7, [
          createVNode(Transition, {
            name: "add-effect",
            mode: "out-in"
          }, {
            default: withCtx(() => [
              createBaseVNode("div", _hoisted_7$6, [
                createBaseVNode("div", _hoisted_8$6, [
                  createVNode(script$2y, { isLoading: loading.value }, null, 8 /* PROPS */, ["isLoading"]),
                  createVNode(script$1T, {
                    ref_key: "sankey_chart",
                    ref: sankey_chart,
                    no_data_message: unref(no_data_message),
                    sankey_data: sankey_data.value,
                    onNode_click: onNodeClick,
                    onAutorefresh_toggle: onAutoRefreshToggle
                  }, null, 8 /* PROPS */, ["no_data_message", "sankey_data"])
                ])
              ])
            ]),
            _: 1 /* STABLE */
          }),
          createVNode(Transition, {
            name: "add-effect",
            mode: "out-in"
          }, {
            default: withCtx(() => [
              (props.context.isEnterpriseL)
                ? (openBlock(), createElementBlock("div", {
                    class: "position-relative",
                    key: reRenderTable.value,
                    style: {"min-height":"614px"}
                  }, [
                    createVNode(script$2w, {
                      ref_key: "table_exporter_as_stats",
                      ref: table_exporter_as_stats,
                      table_id: table_id.value,
                      showLoading: true,
                      f_map_columns: mapTableColumns,
                      f_sort_rows: columnsSorting,
                      get_extra_params_obj: getExtraParameters
                    }, null, 8 /* PROPS */, ["table_id"])
                  ]))
                : createCommentVNode("v-if", true)
            ]),
            _: 1 /* STABLE */
          }),
          createBaseVNode("div", _hoisted_9$6, [
            createVNode(script$2u, { note_list: note_list })
          ])
        ])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    var css_248z$b = "\n.add-effect-move[data-v-28083fb0],\n\n.add-effect-enter-active[data-v-28083fb0],\n.add-effect-leave-active[data-v-28083fb0] {\n    transition: all 0.35s ease;\n}\n\n/* Transform: positive pixels, the effects let enters the component\n * from the right, negative pixels from the left\n */\n.add-effect-enter-from[data-v-28083fb0] {\n    opacity: 0;\n    transform: translateX(-60px);\n}\n.add-effect-leave-to[data-v-28083fb0] {\n    opacity: 0;\n    transform: translateX(0px);\n}\n\n/* ensure leaving items are taken out of layout flow so that moving\n   animations can be calculated correctly. */\n.add-effect-leave-active[data-v-28083fb0] {\n    position: absolute;\n}\n.slider-connect[data-v-28083fb0] {\n    background: none !important;\n}\n";
    styleInject(css_248z$b);

    script$i.__scopeId = "data-v-28083fb0";
    script$i.__file = "http_src/vue/page-exporter-interface-overview.vue";

    const _hoisted_1$d = { class: "m-3" };
    const _hoisted_2$b = { class: "m-4 card card-shadow" };
    const _hoisted_3$b = { class: "card-body" };
    const _hoisted_4$a = { class: "table table-striped table-bordered col-sm-12" };
    const _hoisted_5$9 = { class: "table_length" };
    const _hoisted_6$6 = { class: "d-flex align-items-center" };
    const _hoisted_7$5 = { class: "col-11" };
    const _hoisted_8$5 = { class: "col-1 form-group d-flex justify-content-end" };
    const _hoisted_9$5 = { class: "form-check form-switch" };
    const _hoisted_10$5 = { class: "d-flex justify-content-end me-1" };
    const _hoisted_11$4 = ["disabled"];

    /* ******************************************************************** */

    /* Consts */

    var script$h = {
      __name: 'page-snmp-config',
      props: {
      context: Object,
    },
      setup(__props) {

    /* Imports */
    const _i18n = (t) => i18n(t);

    const get_url = `${http_prefix}/lua/pro/rest/v2/get/snmp/device/config.lua`;
    const post_url = `${http_prefix}/lua/pro/rest/v2/edit/snmp/device/config.lua`;

    const disable_save = ref(true);
    const toggle_snmp_qos_mib_polling_changed = ref(false);
    const toggle_snmp_qos_mib_polling = ref(null);
    const props = __props;

    onMounted(async () => {
      const extra_params = ntopng_url_manager$1.get_url_object();
      const url_params = ntopng_url_manager$1.obj_to_url_params(extra_params);
      const device_config = await ntopng_utility.http_request(`${get_url}?${url_params}`);
      if (device_config.toggle_snmp_qos_mib_polling == '0' ||
        device_config.toggle_snmp_qos_mib_polling == '') {
        toggle_snmp_qos_mib_polling.value.value = '0';
        toggle_snmp_qos_mib_polling.value.removeAttribute('checked');
      } else {
        toggle_snmp_qos_mib_polling.value.value = '1';
        toggle_snmp_qos_mib_polling.value.setAttribute('checked', 'checked');
      }
    });

    function change_toggle_snmp_qos_mib_polling() {
      toggle_snmp_qos_mib_polling_changed.value = !toggle_snmp_qos_mib_polling_changed.value;
      toggle_snmp_qos_mib_polling.value.value == '1' ?
        toggle_snmp_qos_mib_polling.value.value = '0' :
        toggle_snmp_qos_mib_polling.value.value = '1';
      update_save_button_state();
    }

    function update_save_button_state() {
      disable_save.value = !toggle_snmp_qos_mib_polling_changed.value;
    }

    async function reload_page() {
      const extra_params = ntopng_url_manager$1.get_url_object();
      const params = {
        csrf: props.context.csrf,
        toggle_snmp_qos_mib_polling: toggle_snmp_qos_mib_polling.value.value,
        ...extra_params
      };
      const headers = {
        'Content-Type': 'application/json'
      };
      ntopng_utility.http_request(`${post_url}`, { method: 'post', headers: headers, body: JSON.stringify(params) });
      ntopng_url_manager$1.reload_url();
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$d, [
        createBaseVNode("h5", null, toDisplayString(_i18n('prefs.snmp_device_config')), 1 /* TEXT */),
        _cache[1] || (_cache[1] = createBaseVNode("hr", null, null, -1 /* CACHED */)),
        createBaseVNode("div", _hoisted_2$b, [
          createBaseVNode("div", _hoisted_3$b, [
            createBaseVNode("table", _hoisted_4$a, [
              createBaseVNode("tbody", _hoisted_5$9, [
                createBaseVNode("tr", null, [
                  createBaseVNode("td", null, [
                    createBaseVNode("div", _hoisted_6$6, [
                      createBaseVNode("div", _hoisted_7$5, [
                        createBaseVNode("b", null, toDisplayString(_i18n('prefs.toggle_snmp_port_qos_mib_polling_title')), 1 /* TEXT */),
                        _cache[0] || (_cache[0] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                        createBaseVNode("small", null, toDisplayString(_i18n('prefs.toggle_snmp_port_qos_mib_polling_description')), 1 /* TEXT */)
                      ]),
                      createBaseVNode("div", _hoisted_8$5, [
                        createBaseVNode("div", _hoisted_9$5, [
                          createBaseVNode("input", {
                            ref_key: "toggle_snmp_qos_mib_polling",
                            ref: toggle_snmp_qos_mib_polling,
                            class: "form-check-input",
                            type: "checkbox",
                            value: "0",
                            onClick: change_toggle_snmp_qos_mib_polling
                          }, null, 512 /* NEED_PATCH */)
                        ])
                      ])
                    ])
                  ])
                ])
              ])
            ]),
            createBaseVNode("div", _hoisted_10$5, [
              createBaseVNode("button", {
                class: "btn btn-primary",
                disabled: disable_save.value,
                onClick: reload_page
              }, toDisplayString(_i18n('save_settings')), 9 /* TEXT, PROPS */, _hoisted_11$4)
            ])
          ])
        ])
      ]))
    }
    }

    };

    var css_248z$a = "\n.table_length[data-v-6936f360] {\n  display: table;\n  width: 100%;\n}\n";
    styleInject(css_248z$a);

    script$h.__scopeId = "data-v-6936f360";
    script$h.__file = "http_src/vue/page-snmp-config.vue";

    const _hoisted_1$c = { class: "m-3" };
    const _hoisted_2$a = { class: "card card-shadow" };
    const _hoisted_3$a = { class: "card-body" };
    const _hoisted_4$9 = { class: "table table-striped table-bordered col-sm-12" };
    const _hoisted_5$8 = { class: "table_length" };
    const _hoisted_6$5 = { class: "d-flex align-items-center" };
    const _hoisted_7$4 = { class: "col-8" };
    const _hoisted_8$4 = { class: "col-4 form-group d-flex justify-content-end" };
    const _hoisted_9$4 = { class: "form-check w-75" };
    const _hoisted_10$4 = ["placeholder"];
    const _hoisted_11$3 = { class: "d-flex align-items-center" };
    const _hoisted_12$3 = { class: "col-8" };
    const _hoisted_13$3 = { class: "col-4 form-group d-flex justify-content-end" };
    const _hoisted_14$3 = { class: "form-check w-75" };
    const _hoisted_15$3 = ["placeholder"];
    const _hoisted_16$3 = { class: "d-flex align-items-center" };
    const _hoisted_17$3 = { class: "col-10" };
    const _hoisted_18$3 = { key: 0 };
    const _hoisted_19$3 = { class: "d-flex align-items-center" };
    const _hoisted_20$3 = { class: "col-11" };
    const _hoisted_21$3 = { class: "col-1 form-group d-flex justify-content-end" };
    const _hoisted_22$3 = { class: "form-check form-switch" };
    const _hoisted_23$3 = { class: "d-flex justify-content-end me-1" };
    const _hoisted_24$3 = ["disabled"];

    /* ******************************************************************** */

    /* Consts */

    var script$g = {
      __name: 'page-host-config',
      props: {
      context: Object,
    },
      setup(__props) {

    /* Imports */
    const _i18n = (t) => i18n(t);

    const get_url = `${http_prefix}/lua/rest/v2/get/host/config.lua`;
    const post_url = `${http_prefix}/lua/rest/v2/set/host/config.lua`;
    const pool_url = `${http_prefix}/lua/rest/v2/get/pools.lua`;

    const disable_save = ref(true);
    const props = __props;

    const initial_host_alias = ref(false);
    const host_alias = ref(null);

    const initial_host_notes = ref(false);
    const host_notes = ref(null);

    const show_drop_host_traffic = ref(true);
    const toggle_drop_host_traffic_changed = ref(false);
    const toggle_drop_host_traffic = ref(null);

    const host_pools = ref(null);
    const selected_pool = ref({});
    const initial_selected_pool = ref(0);
    const host_pools_list = ref([]);
    const host_pool_title = ref(i18n("host_config.host_pool"));
    const host_pool_description = ref(i18n("host_config.host_pool_description"));
    const pool_key = ref(0);

    onMounted(async () => {
      const extra_params = ntopng_url_manager$1.get_url_object();
      const url_params = ntopng_url_manager$1.obj_to_url_params(extra_params);
      const host_config = await ntopng_utility$1.http_request(`${get_url}?${url_params}`);
      show_drop_host_traffic.value = host_config.has_traffic_policies;
      if (host_config.drop_traffic == '0' ||
        host_config.drop_traffic == '') {
        toggle_drop_host_traffic.value.value = '0';
        toggle_drop_host_traffic.value.removeAttribute('checked');
      } else {
        toggle_drop_host_traffic.value.value = '1';
        toggle_drop_host_traffic.value.setAttribute('checked', 'checked');
      }

      host_alias.value.value = host_config.alias;
      initial_host_alias.value = host_config.alias;
      host_notes.value.value = host_config.notes;
      initial_host_notes.value = host_config.notes;
      
      const rsp = await ntopng_utility$1.http_request(pool_url);
      rsp.forEach((item) => {
        host_pools_list.value.push({
          value: item.pool_id,
          label: item.name
        });
      });
      selected_pool.value = host_pools_list.value.filter((item) => item.value === host_config.host_pool_id)[0];
      initial_selected_pool.value = selected_pool.value.value;
      if(show_drop_host_traffic.value) {
        host_pool_title.value = i18n("nedge.user");
        host_pool_description.value = i18n("host_config.nedge_user_description");
      }
      pool_key.value = 1; /* Trick used to re-render the dropdown */
    });

    function update_selected_pool(item) {
      update_save_button_state();
    }

    function update_save_button_state() {
      disable_save.value = (!toggle_drop_host_traffic_changed.value) &&
        (initial_host_alias.value === host_alias.value.value) &&
        (initial_host_notes.value === host_notes.value.value) &&
        (initial_selected_pool.value === selected_pool.value.value);
    }

    function change_toggle_drop_host_traffic() {
      toggle_drop_host_traffic_changed.value = !toggle_drop_host_traffic_changed.value;
      toggle_drop_host_traffic.value.value == '1' ?
        toggle_drop_host_traffic.value.value = '0' :
        toggle_drop_host_traffic.value.value = '1';
      update_save_button_state();
    }

    async function reload_page() {
      const extra_params = ntopng_url_manager$1.get_url_object();
      const params = {
        csrf: props.context.csrf,
        drop_host_traffic: toggle_drop_host_traffic?.value?.value,
        pool: selected_pool.value.value,
        custom_name: host_alias.value.value,
        custom_notes: host_notes.value.value,
        ...extra_params
      };
      const headers = {
        'Content-Type': 'application/json'
      };
      await ntopng_utility$1.http_request(`${post_url}`, { method: 'post', headers, body: JSON.stringify(params) });
      ntopng_url_manager$1.reload_url();
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$c, [
        createBaseVNode("h5", null, toDisplayString(_i18n('host_config.host_config')), 1 /* TEXT */),
        _cache[5] || (_cache[5] = createBaseVNode("hr", null, null, -1 /* CACHED */)),
        createBaseVNode("div", _hoisted_2$a, [
          createBaseVNode("div", _hoisted_3$a, [
            createBaseVNode("table", _hoisted_4$9, [
              createBaseVNode("tbody", _hoisted_5$8, [
                createBaseVNode("tr", null, [
                  createBaseVNode("td", null, [
                    createBaseVNode("div", _hoisted_6$5, [
                      createBaseVNode("div", _hoisted_7$4, [
                        createBaseVNode("b", null, toDisplayString(_i18n('host_config.host_alias')), 1 /* TEXT */),
                        _cache[1] || (_cache[1] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                        createBaseVNode("small", null, toDisplayString(_i18n('host_config.host_alias_description')), 1 /* TEXT */)
                      ]),
                      createBaseVNode("div", _hoisted_8$4, [
                        createBaseVNode("div", _hoisted_9$4, [
                          createBaseVNode("input", {
                            ref_key: "host_alias",
                            ref: host_alias,
                            class: "form-control",
                            placeholder: _i18n('host_config.custom_name'),
                            value: "",
                            onInput: update_save_button_state
                          }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_10$4)
                        ])
                      ])
                    ])
                  ])
                ]),
                createBaseVNode("tr", null, [
                  createBaseVNode("td", null, [
                    createBaseVNode("div", _hoisted_11$3, [
                      createBaseVNode("div", _hoisted_12$3, [
                        createBaseVNode("b", null, toDisplayString(_i18n('host_notes')), 1 /* TEXT */),
                        _cache[2] || (_cache[2] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                        createBaseVNode("small", null, toDisplayString(_i18n('host_config.host_notes_description')), 1 /* TEXT */)
                      ]),
                      createBaseVNode("div", _hoisted_13$3, [
                        createBaseVNode("div", _hoisted_14$3, [
                          createBaseVNode("input", {
                            ref_key: "host_notes",
                            ref: host_notes,
                            type: "text",
                            class: "form-control",
                            placeholder: _i18n('host_config.custom_notes'),
                            value: "",
                            onInput: update_save_button_state
                          }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_15$3)
                        ])
                      ])
                    ])
                  ])
                ]),
                createBaseVNode("tr", null, [
                  createBaseVNode("td", null, [
                    createBaseVNode("div", _hoisted_16$3, [
                      createBaseVNode("div", _hoisted_17$3, [
                        createBaseVNode("b", null, toDisplayString(host_pool_title.value), 1 /* TEXT */),
                        _cache[3] || (_cache[3] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                        createBaseVNode("small", null, toDisplayString(host_pool_description.value), 1 /* TEXT */)
                      ]),
                      (openBlock(), createElementBlock("div", {
                        class: "col-2 form-group d-flex justify-content-end",
                        key: pool_key.value
                      }, [
                        createVNode(script$2H, {
                          ref_key: "host_pools",
                          ref: host_pools,
                          onSelect_option: update_selected_pool,
                          selected_option: selected_pool.value,
                          "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_pool).value = $event)),
                          options: host_pools_list.value
                        }, null, 8 /* PROPS */, ["selected_option", "options"])
                      ]))
                    ])
                  ])
                ]),
                (show_drop_host_traffic.value)
                  ? (openBlock(), createElementBlock("tr", _hoisted_18$3, [
                      createBaseVNode("td", null, [
                        createBaseVNode("div", _hoisted_19$3, [
                          createBaseVNode("div", _hoisted_20$3, [
                            createBaseVNode("b", null, toDisplayString(_i18n('host_config.drop_all_host_traffic')), 1 /* TEXT */),
                            _cache[4] || (_cache[4] = createBaseVNode("br", null, null, -1 /* CACHED */)),
                            createBaseVNode("small", null, toDisplayString(_i18n('host_config.drop_all_host_traffic_description')), 1 /* TEXT */)
                          ]),
                          createBaseVNode("div", _hoisted_21$3, [
                            createBaseVNode("div", _hoisted_22$3, [
                              createBaseVNode("input", {
                                ref_key: "toggle_drop_host_traffic",
                                ref: toggle_drop_host_traffic,
                                class: "form-check-input",
                                type: "checkbox",
                                value: "0",
                                onClick: change_toggle_drop_host_traffic
                              }, null, 512 /* NEED_PATCH */)
                            ])
                          ])
                        ])
                      ])
                    ]))
                  : createCommentVNode("v-if", true)
              ])
            ]),
            createBaseVNode("div", _hoisted_23$3, [
              createBaseVNode("button", {
                class: "btn btn-primary",
                disabled: disable_save.value,
                onClick: reload_page
              }, toDisplayString(_i18n('save_settings')), 9 /* TEXT, PROPS */, _hoisted_24$3)
            ])
          ])
        ])
      ]))
    }
    }

    };

    var css_248z$9 = "\n.table_length[data-v-247fe8b6] {\n  display: table;\n  width: 100%;\n}\n";
    styleInject(css_248z$9);

    script$g.__scopeId = "data-v-247fe8b6";
    script$g.__file = "http_src/vue/page-host-config.vue";

    const _hoisted_1$b = { style: {"min-height":"8.5rem"} };
    const _hoisted_2$9 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_3$9 = { class: "col-form-label col-sm-4" };
    const _hoisted_4$8 = { class: "col-sm-6" };
    const _hoisted_5$7 = ["pattern"];
    const _hoisted_6$4 = ["disabled"];


    var script$f = {
      __name: 'modal-file-save',
      props: {
        csrf: String,
        title: String,
        get_suggested_file_name: Function,
        store_file: Function,
        allow_spaces: Boolean
    },
      emits: ['file_stored'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const showed = () => {};
    const file_name = ref("");
    ref("date"); // name / date

    const props = __props;

    const emit = __emit;

    let pattern = NtopUtils.REGEXES.singleword;

    const disable_add = computed$1(() => {
        let rg = new RegExp(pattern);
        return !rg.test(file_name.value);
    });

    const show = () => {
        init();
        modal_id.value.show();
    };

    async function init() {
        file_name.value = props.get_suggested_file_name();
    }

    const store_file = async () => {
        props.store_file(file_name.value);
        emit('file_stored', file_name.value);
        close();
    };

    const close = () => {
        modal_id.value.close();
    };

    __expose({ show, close });

    onMounted(() => {
        if (props.allow_spaces) {
            pattern = NtopUtils.REGEXES.multiword;
        } else {
            pattern = NtopUtils.REGEXES.singleword;
        }
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[1] || (_cache[1] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$b, [
            createBaseVNode("div", _hoisted_2$9, [
              createBaseVNode("label", _hoisted_3$9, [
                createBaseVNode("b", null, toDisplayString(_i18n("name")) + ":", 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_4$8, [
                withDirectives(createBaseVNode("input", {
                  pattern: unref(pattern),
                  placeholder: "",
                  required: "",
                  type: "text",
                  class: "form-control",
                  "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((file_name).value = $event))
                }, null, 8 /* PROPS */, _hoisted_5$7), [
                  [vModelText, file_name.value]
                ])
              ])
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: store_file,
            disabled: disable_add.value,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("save")), 9 /* TEXT, PROPS */, _hoisted_6$4)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$8 = "\ninput[data-v-87fda19e]:invalid {\n  border-color: #ff0000;\n}\n.not-allowed[data-v-87fda19e] {\n  cursor: not-allowed;\n}\n";
    styleInject(css_248z$8);

    script$f.__scopeId = "data-v-87fda19e";
    script$f.__file = "http_src/vue/modal-file-save.vue";

    const _hoisted_1$a = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$8 = { class: "col-form-label col-sm-4" };
    const _hoisted_3$8 = { class: "col-sm-8" };
    const _hoisted_4$7 = { value: "name" };
    const _hoisted_5$6 = { value: "date" };
    const _hoisted_6$3 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_7$3 = { class: "col-form-label col-sm-4" };
    const _hoisted_8$3 = { class: "col-sm-8" };
    const _hoisted_9$3 = ["disabled"];
    const _hoisted_10$3 = ["disabled"];


    var script$e = {
      __name: 'modal-file-open',
      props: {
        csrf: String,
        title: String,
        file_title: String,
        list_files: Function,
        open_file: Function,
        delete_file: Function,
    },
      emits: ['file_deleted'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const showed = () => {};
    const file_selected = ref({});
    const file_name = ref("");
    const files = ref([]);
    const order_by = ref("date"); // name / date

    const props = __props;

    const emit = __emit;

    NtopUtils.REGEXES.singleword;

    const disable_select = computed$1(() => {
        return file_selected.value == "";
    });

    const show = () => {
        init();
        modal_id.value.show();
    };

    function display_name(file) {
        let utc_ms = file.epoch * 1000;
        let date = ntopng_utility.from_utc_to_server_date_format(utc_ms, "DD/MM/YYYY");
        return `${file.name} (${date})`
    }

    function sort_files_by() {
        files.value = files.value.sort((a, b) => {
    	if (order_by.value == "name") {
                /* Name asc */
    	    return a.name.localeCompare(b.name);
    	} else {
                /* Date desc (last first) */
    	    return b.epoch - a.epoch;
            }
        });
        if (files.value.length > 0) {
    	file_selected.value = files.value[0];
        }
    }

    async function init() {
        file_name.value = "";
        files.value = await props.list_files();
        files.value.forEach((f) => f.label = display_name(f));
        sort_files_by();
        if (files.value.length > 0) {
    	file_selected.value = files.value[0];
        }
    }

    const select_file = () => {
        close();
        props.open_file(file_selected.value.name);
    };

    const delete_file = async (delete_all) => {
        let name = file_selected.value.name;
        if (delete_all == true) { name = "*"; }
        if (props.delete_file(name)) {
            emit('file_deleted', name);
        }
        close();
    };

    const close = () => {
        modal_id.value.close();
    };

    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[4] || (_cache[4] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$a, [
            createBaseVNode("label", _hoisted_2$8, [
              createBaseVNode("b", null, toDisplayString(_i18n("order_by")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$8, [
              withDirectives(createBaseVNode("select", {
                class: "form-select",
                onChange: _cache[0] || (_cache[0] = $event => (sort_files_by())),
                "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((order_by).value = $event))
              }, [
                createBaseVNode("option", _hoisted_4$7, toDisplayString(_i18n("name")), 1 /* TEXT */),
                createBaseVNode("option", _hoisted_5$6, toDisplayString(_i18n("date")), 1 /* TEXT */)
              ], 544 /* NEED_HYDRATION, NEED_PATCH */), [
                [vModelSelect, order_by.value]
              ])
            ])
          ]),
          createBaseVNode("div", _hoisted_6$3, [
            createBaseVNode("label", _hoisted_7$3, [
              createBaseVNode("b", null, toDisplayString(__props.file_title), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_8$3, [
              createVNode(script$2H, {
                selected_option: file_selected.value,
                "onUpdate:selected_option": _cache[2] || (_cache[2] = $event => ((file_selected).value = $event)),
                options: files.value
              }, null, 8 /* PROPS */, ["selected_option", "options"])
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            onClick: _cache[3] || (_cache[3] = $event => (delete_file(true))),
            type: "button",
            style: {"text-align":"left","margin-left":"0px"},
            class: "btn btn-danger start-0 position-absolute ms-3"
          }, toDisplayString(_i18n("delete_all_entries")), 1 /* TEXT */),
          createBaseVNode("button", {
            type: "button",
            onClick: delete_file,
            disabled: disable_select.value,
            class: "btn btn-danger"
          }, toDisplayString(_i18n("delete")), 9 /* TEXT, PROPS */, _hoisted_9$3),
          createBaseVNode("button", {
            type: "button",
            onClick: select_file,
            disabled: disable_select.value,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("open")), 9 /* TEXT, PROPS */, _hoisted_10$3)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$7 = "\ninput[data-v-1976657e]:invalid {\n  border-color: #ff0000;\n}\n.not-allowed[data-v-1976657e] {\n  cursor: not-allowed;\n}\n";
    styleInject(css_248z$7);

    script$e.__scopeId = "data-v-1976657e";
    script$e.__file = "http_src/vue/modal-file-open.vue";

    const _hoisted_1$9 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$7 = { class: "col-form-label col-sm-4" };
    const _hoisted_3$7 = { class: "col-sm-8" };
    const _hoisted_4$6 = ["disabled"];


    var script$d = {
      __name: 'modal-file-upload',
      props: {
        csrf: String,
        title: String,
        file_title: String,
        upload_file: Function,
    },
      emits: ['file_uploaded'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const showed = () => {};
    const file_content = ref("");
    const file_name = ref("");
    const file_input = ref(null);

    const props = __props;

    const emit = __emit;

    const show = () => {
        init();
        modal_id.value.show();
    };

    async function init() {
        file_name.value = "";
        file_content.value = "";
    }

    function on_pick_file () {
        file_input.value.click();
    }

    function on_file_picked (event) {
        const files = event.target.files;

        file_name.value = files[0].name;

        const fileReader = new FileReader();
        fileReader.addEventListener('load', () => {
            file_content.value = fileReader.result;
        });
        fileReader.readAsText(files[0]);

        emit('file_uploaded', file_name.value);
    }

    const select_file = () => {
        close();
        props.upload_file(file_content.value);
    };

    const close = () => {
        modal_id.value.close();
    };

    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[0] || (_cache[0] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$9, [
            createBaseVNode("label", _hoisted_2$7, [
              createBaseVNode("b", null, toDisplayString(__props.file_title), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$7, [
              createBaseVNode("button", {
                type: "button",
                onClick: on_pick_file,
                class: "btn btn-primary"
              }, toDisplayString(_i18n("upload")), 1 /* TEXT */),
              _cache[1] || (_cache[1] = createBaseVNode("span", null, " ", -1 /* CACHED */)),
              withDirectives(createBaseVNode("small", null, "No file selected", 512 /* NEED_PATCH */), [
                [vShow, !file_content.value]
              ]),
              withDirectives(createBaseVNode("small", null, toDisplayString(file_name.value), 513 /* TEXT, NEED_PATCH */), [
                [vShow, file_name.value]
              ]),
              createBaseVNode("input", {
                ref_key: "file_input",
                ref: file_input,
                type: "file",
                accept: "application/JSON",
                onChange: on_file_picked,
                style: {"display":"none"}
              }, null, 544 /* NEED_HYDRATION, NEED_PATCH */)
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            onClick: select_file,
            disabled: !file_content.value,
            class: "btn btn-primary"
          }, toDisplayString(_i18n("open")), 9 /* TEXT, PROPS */, _hoisted_4$6)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$6 = "\ninput[data-v-9db8ba96]:invalid {\n  border-color: #ff0000;\n}\n.not-allowed[data-v-9db8ba96] {\n  cursor: not-allowed;\n}\n";
    styleInject(css_248z$6);

    script$d.__scopeId = "data-v-9db8ba96";
    script$d.__file = "http_src/vue/modal-file-upload.vue";

    const _hoisted_1$8 = {
      class: "form-group ms-2 me-2 mt-3 row",
      style: {"overflow-y":"scroll","height":"45vh"}
    };
    const _hoisted_2$6 = ["onClick"];
    const _hoisted_3$6 = { class: "card-body" };
    const _hoisted_4$5 = { class: "form-group wizard-form-group" };
    const _hoisted_5$5 = { class: "form-text text-muted" };


    var script$c = {
      __name: 'modal-select-component',
      props: {
        csrf: String,
        title: String,
        list_components: Function,
        add_component: Function
    },
      emits: [],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    const showed = () => {};
    const selected_component = ref({});
    const components = ref([]);
    ref("date"); // name / date

    const props = __props;

    const show = () => {
        init();
        modal_id.value.show();
    };

    async function init() {
        components.value = await props.list_components();
        if (components.value.length > 0) {
            selected_component.value = components.value[0];
        }
    }

    function get_component_type_icon(component_type) {
        switch (component_type) {
          case 'pie':
            return "fa-solid fa-chart-pie";
          case 'table':
            return "fa-solid fa-table";
          case 'timeseries':
            return "fa-solid fa-chart-line";
          default:
            return "";
        }
    }

    function onModalChange(e) {
        close();
        props.add_component(selected_component.value);
    }

    const close = () => {
        modal_id.value.close();
    };

    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[0] || (_cache[0] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createBaseVNode("div", _hoisted_1$8, [
            (openBlock(true), createElementBlock(Fragment, null, renderList(components.value, (c) => {
              return (openBlock(), createElementBlock("div", {
                class: normalizeClass(["card w-100 wizard-card", { 'wizard-selected': selected_component.value == c }])
              }, [
                createBaseVNode("a", {
                  class: "wizard-link",
                  href: "#",
                  onClick: $event => {selected_component.value = c; onModalChange();}
                }, [
                  createBaseVNode("div", _hoisted_3$6, [
                    createBaseVNode("div", _hoisted_4$5, [
                      createBaseVNode("h5", null, [
                        createBaseVNode("i", {
                          class: normalizeClass(get_component_type_icon(c.component))
                        }, null, 2 /* CLASS */),
                        createTextVNode(" " + toDisplayString(_i18n(c.i18n_name)), 1 /* TEXT */)
                      ]),
                      createBaseVNode("small", _hoisted_5$5, toDisplayString(_i18n(c.i18n_descr)), 1 /* TEXT */)
                    ])
                  ])
                ], 8 /* PROPS */, _hoisted_2$6)
              ], 2 /* CLASS */))
            }), 256 /* UNKEYED_FRAGMENT */))
          ])
        ]),
        footer: withCtx(() => [...(_cache[1] || (_cache[1] = []))]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$5 = "\ninput[data-v-16713f39]:invalid {\n  border-color: #ff0000;\n}\n.not-allowed[data-v-16713f39] {\n  cursor: not-allowed;\n}\n";
    styleInject(css_248z$5);

    script$c.__scopeId = "data-v-16713f39";
    script$c.__file = "http_src/vue/modal-select-component.vue";

    const _hoisted_1$7 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_2$5 = { class: "col-sm-2 mt-2" };
    const _hoisted_3$5 = { class: "col-sm-10" };
    const _hoisted_4$4 = { class: "form-group ms-2 me-2 mt-4 row" };
    const _hoisted_5$4 = { class: "col-sm-2 mt-3" };
    const _hoisted_6$2 = { class: "col-sm-6 mt-2" };
    const _hoisted_7$2 = { class: "range" };
    const _hoisted_8$2 = { class: "sliderticks" };
    const _hoisted_9$2 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_10$2 = { class: "col-sm-2 mt-2" };
    const _hoisted_11$2 = { class: "col-sm-9 mt-2" };
    const _hoisted_12$2 = { class: "range" };
    const _hoisted_13$2 = { class: "sliderticks" };
    const _hoisted_14$2 = {
      key: 0,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_15$2 = { class: "col-sm-2 mt-2" };
    const _hoisted_16$2 = { class: "col-sm-5" };
    const _hoisted_17$2 = { class: "form-group ms-2 me-2 mt-3 row" };
    const _hoisted_18$2 = { class: "col-sm-2 mt-2" };
    const _hoisted_19$2 = { class: "col-sm-5" };
    const _hoisted_20$2 = { class: "ms-2 mb-3 mt-4 row" };
    const _hoisted_21$2 = {
      class: "col-form-label col-sm-3 pe-0",
      id: "advanced-view"
    };
    const _hoisted_22$2 = { class: "col-sm-2 ps-0" };
    const _hoisted_23$2 = {
      class: "form-check form-switch mt-2",
      id: "advanced-view"
    };
    const _hoisted_24$2 = {
      key: 1,
      class: "form-group ms-2 me-2 mt-3 row"
    };
    const _hoisted_25$2 = { class: "col-sm-12" };
    const _hoisted_26$2 = {
      key: 0,
      style: {"color":"rgba(255, 0, 0, 0.797)"}
    };
    const _hoisted_27$2 = ["disabled"];
    /* **************************************************************************** */

    /* Function to retrieve the local word */

    var script$b = {
      __name: 'modal-edit-component',
      props: {
        csrf: String,
    },
      emits: ["edit"],
      setup(__props, { expose: __expose, emit: __emit }) {


    /* Imports */
    const _i18n = (t) => i18n(t);

    /* Events Emitted */
    const emit = __emit;

    /* Consts */
    const title = ref(i18n("dashboard.edit_component"));
    const modal_id = ref(null);
    const slider_values = [
        { id: 1, size_component_value: 4  },
        { id: 2, size_component_value: 6  },
        { id: 3, size_component_value: 12 },
        { id: 4, size_component_value: -1 }
    ];

    /* Time Window options */
    const time_window_list = ref([
        { value:'current', label: _i18n("dashboard.current_time"), default: true },    
        { value:'5_min', label: _i18n("dashboard.time_window_list.5_min")  },
        { value:'30_min', label: _i18n("dashboard.time_window_list.30_min") },
        { value:'hour', label: _i18n("dashboard.time_window_list.hour") },
        { value:'2_hours', label: _i18n("dashboard.time_window_list.2_hours") },
        { value:'12_hours', label: _i18n("dashboard.time_window_list.12_hours") },
        { value:'day', label: _i18n("dashboard.time_window_list.day") },
        { value:'week', label: _i18n("dashboard.time_window_list.week") },
        { value:'month', label: _i18n("dashboard.time_window_list.month") },
        { value:'year', label: _i18n("dashboard.time_window_list.year") },
    ]);

    /* Time Offset options */
    const time_offset_list = ref([
        { value:'current', label: _i18n("dashboard.current_time"), default: true }, 
        { value:'hour', label: _i18n("dashboard.time_offset_list.hour") },
        { value:'day', label: _i18n("dashboard.time_offset_list.day") },
        { value:'week', label: _i18n("dashboard.time_offset_list.week") },
        { value:'month', label: _i18n("dashboard.time_offset_list.month") },
        { value:'year', label: _i18n("dashboard.time_offset_list.year") },
    ]);

    /* Modal Ref Consts */
    const old_component_to_edit = ref(null);
    const component_title = ref(null);
    const width_selected = ref(null);
    const height_selected = ref(null);
    const params_json_data = ref(null);
    const selected_time_window = ref(null);
    const selected_time_offset = ref(null);
    const show_advanced_settings = ref(false); // bool used to enable the advanced view
    const isNotJsonParamsValid = ref(false); // bool used to validate the REST Params in textarea
    const hiddenOnReport = ref(false); // bool used to hide time_window when the page is report

    /* **************************************************************************** */

    /**
     * 
     * @brief This method change the value of the show_advanced_settings
     *        in order to enable the advanced view or disable it.
     */
    const updateAdvancedSettingsView = function() {
        show_advanced_settings.value = !show_advanced_settings.value;
    };

    /**
     * 
     * @brief This method changes the value of the show_advanced_settings 
     *        boolean to enable or disable the advanced view.
     */
    const formatJson = function() {
        
        isNotJsonParamsValid.value = false;
        try {
            params_json_data.value = JSON.stringify(JSON.parse(params_json_data.value), null, 2);
        } catch (e) {
            isNotJsonParamsValid.value = true;
        }
        return params_json_data.value;
    };

    /**
     * 
     * @brief This method selects from the time_array 
     *        (either time_offset_list or time_window_list) 
     *        the object with a value equal to time_value (the old values). 
     *        In case time_value is empty or null, 
     *        the method returns the default object ('Current').
     * @param time_value The time value (5_min, hour, ...) to search for
     * @param time_array The array of time objects 
     *                   (either from time_offset_list or time_window_list).
     */
    const find_time_object = function(time_value, time_array) {
        if (dataUtils.isEmptyOrNull(time_value)) {
            return time_array.find((t) => t.default);
        } else {
            return time_array.find((t) => t.value == time_value);
        }
    };

    /**
     * 
     *  @brief This method is called to reset the modal ref constants.
     *  */ 
    const reset_modal_form = function (hidden) {
        old_component_to_edit.value = null;
        isNotJsonParamsValid.value = false;
        hiddenOnReport.value = hidden;
        component_title.value = "";
        height_selected.value = 1;
        width_selected.value = 1;
    };

    /* **************************************************************************** */

    /**
     * 
     *  @brief This method is called to set the old data of the selected component 
     *         for editing.
     *  @param old_component_to_edit selected component to edit
     *  */ 
    const set_old_component_values = (old_component) => {
        old_component_to_edit.value = old_component;

        component_title.value = old_component.custom_name;
        width_selected.value = convert_size_to_slider_value(old_component.width);
        height_selected.value = convert_size_to_slider_value(old_component.height);
        
        selected_time_window.value = find_time_object(old_component.time_window,time_window_list.value);
        selected_time_offset.value = find_time_object(old_component.time_offset, time_offset_list.value);

        params_json_data.value = JSON.stringify(old_component.params, null, 2);
    };

    /* **************************************************************************** */

    /**
     * 
     *  @brief This method is called whenever the modal is opened 
     *  @param old_component_to_edit selected component to edit
     *  */ 
    const show = (old_component_to_edit, hiddenOnReport) => {
        /* First of all reset all the data */
        reset_modal_form(hiddenOnReport);
        /* Set the old data of the component */
        set_old_component_values(old_component_to_edit);
        modal_id.value.show();
    };

    /* **************************************************************************** */

    /**
     * 
     *  @brief Convert a slider value           (1 (Small), 2 (Medium), 3 (Large),   4 (Auto)) 
     *                 to component size value  (4 (Small), 6 (Medium), 12 (Large), -1 (Auto))
     *  @param s_v slider value 
     *  */ 
    const convert_slider_value_to_size = (s_v) => {
        return slider_values.find((c) => c.id == Number(s_v)).size_component_value;
    }; 

    /**
     * 
     *  @brief Convert a component size value (4 (Small), 6 (Medium), 12 (Large), -1(Auto))
     *         to slider value                (1 (Small), 2 (Medium), 3 (Large),  4 (Auto))  
     *  @param size component size value 
     *  */ 
    const convert_size_to_slider_value = (size) => {
        return slider_values.find((c) => c.size_component_value == Number(size)).id;
    }; 

    /* **************************************************************************** */

    /**
     * 
     *  @brief Function called when the 'Apply' button is clicked 
     *         to emit an 'edit' event and close the modal.
     *  */ 
    const edit_ = () => {
        const new_width     = convert_slider_value_to_size(width_selected.value);
        const new_height    = convert_slider_value_to_size(height_selected.value);
        const new_params    = `${params_json_data.value}`;

        emit("edit", {
            id: old_component_to_edit.value.id,
            title: component_title.value,
            height: new_height,
            width: new_width,
            time_offset: selected_time_offset.value.value,
            time_window: selected_time_window.value.value,
            rest_params: new_params
        });
        modal_id.value.close();
    };

    /* **************************************************************************** */

    __expose({ show });

    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(title.value), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          createCommentVNode(" Title "),
          createBaseVNode("div", _hoisted_1$7, [
            createBaseVNode("label", _hoisted_2$5, [
              createBaseVNode("b", null, toDisplayString(_i18n("dashboard.component_title")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_3$5, [
              withDirectives(createBaseVNode("input", {
                "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((component_title).value = $event)),
                class: "form-control",
                type: "text",
                required: ""
              }, null, 512 /* NEED_PATCH */), [
                [vModelText, component_title.value]
              ])
            ])
          ]),
          createCommentVNode(" Width "),
          createBaseVNode("div", _hoisted_4$4, [
            createBaseVNode("label", _hoisted_5$4, [
              createBaseVNode("b", null, toDisplayString(_i18n("dashboard.component_width")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_6$2, [
              createBaseVNode("div", _hoisted_7$2, [
                withDirectives(createBaseVNode("input", {
                  type: "range",
                  id: "slider",
                  "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((width_selected).value = $event)),
                  style: {"width":"100%"},
                  min: "1",
                  max: "3",
                  step: "1"
                }, null, 512 /* NEED_PATCH */), [
                  [vModelText, width_selected.value]
                ]),
                createBaseVNode("div", _hoisted_8$2, [
                  createBaseVNode("p", null, toDisplayString(_i18n("dashboard.component_sizes.small")), 1 /* TEXT */),
                  createBaseVNode("p", null, toDisplayString(_i18n("dashboard.component_sizes.medium")), 1 /* TEXT */),
                  createBaseVNode("p", null, toDisplayString(_i18n("dashboard.component_sizes.large")), 1 /* TEXT */)
                ])
              ])
            ])
          ]),
          createCommentVNode(" Height "),
          createBaseVNode("div", _hoisted_9$2, [
            createBaseVNode("label", _hoisted_10$2, [
              createBaseVNode("b", null, toDisplayString(_i18n("dashboard.component_height")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_11$2, [
              createBaseVNode("div", _hoisted_12$2, [
                withDirectives(createBaseVNode("input", {
                  type: "range",
                  id: "slider",
                  "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((height_selected).value = $event)),
                  style: {"width":"100%"},
                  min: "1",
                  max: "4",
                  step: "1"
                }, null, 512 /* NEED_PATCH */), [
                  [vModelText, height_selected.value]
                ]),
                createBaseVNode("div", _hoisted_13$2, [
                  createBaseVNode("p", null, toDisplayString(_i18n("dashboard.component_sizes.small")), 1 /* TEXT */),
                  createBaseVNode("p", null, toDisplayString(_i18n("dashboard.component_sizes.medium")), 1 /* TEXT */),
                  createBaseVNode("p", null, toDisplayString(_i18n("dashboard.component_sizes.large")), 1 /* TEXT */),
                  createBaseVNode("p", null, toDisplayString(_i18n("dashboard.component_sizes.auto")), 1 /* TEXT */)
                ])
              ])
            ])
          ]),
          createCommentVNode(" Time Window "),
          (!hiddenOnReport.value)
            ? (openBlock(), createElementBlock("div", _hoisted_14$2, [
                createBaseVNode("label", _hoisted_15$2, [
                  createBaseVNode("b", null, toDisplayString(_i18n("dashboard.time_window")), 1 /* TEXT */)
                ]),
                createBaseVNode("div", _hoisted_16$2, [
                  createVNode(script$2H, {
                    selected_option: selected_time_window.value,
                    "onUpdate:selected_option": _cache[3] || (_cache[3] = $event => ((selected_time_window).value = $event)),
                    options: time_window_list.value
                  }, null, 8 /* PROPS */, ["selected_option", "options"])
                ])
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Time Offset "),
          createBaseVNode("div", _hoisted_17$2, [
            createBaseVNode("label", _hoisted_18$2, [
              createBaseVNode("b", null, toDisplayString(_i18n("dashboard.time_offset")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_19$2, [
              createVNode(script$2H, {
                selected_option: selected_time_offset.value,
                "onUpdate:selected_option": _cache[4] || (_cache[4] = $event => ((selected_time_offset).value = $event)),
                options: time_offset_list.value
              }, null, 8 /* PROPS */, ["selected_option", "options"])
            ])
          ]),
          createCommentVNode(" Advanced Settings "),
          createBaseVNode("div", _hoisted_20$2, [
            createBaseVNode("label", _hoisted_21$2, [
              createBaseVNode("b", null, toDisplayString(_i18n("dashboard.advanced_settings")), 1 /* TEXT */)
            ]),
            createBaseVNode("div", _hoisted_22$2, [
              createBaseVNode("div", _hoisted_23$2, [
                createBaseVNode("input", {
                  name: "show_advanced_settings",
                  class: "form-check-input",
                  type: "checkbox",
                  onInput: updateAdvancedSettingsView,
                  role: "switch"
                }, null, 32 /* NEED_HYDRATION */)
              ])
            ])
          ]),
          createCommentVNode(" REST Params "),
          (show_advanced_settings.value)
            ? (openBlock(), createElementBlock("div", _hoisted_24$2, [
                createBaseVNode("div", _hoisted_25$2, [
                  (isNotJsonParamsValid.value)
                    ? (openBlock(), createElementBlock("p", _hoisted_26$2, toDisplayString(_i18n("dashboard.component_json_error")), 1 /* TEXT */))
                    : createCommentVNode("v-if", true),
                  withDirectives(createBaseVNode("textarea", {
                    class: "highlighted-json",
                    "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((params_json_data).value = $event)),
                    onFocusout: formatJson,
                    style: {"width":"100%","height":"100%"},
                    rows: "16",
                    cols: "10"
                  }, null, 544 /* NEED_HYDRATION, NEED_PATCH */), [
                    [vModelText, params_json_data.value]
                  ])
                ])
              ]))
            : createCommentVNode("v-if", true)
        ]),
        footer: withCtx(() => [
          createBaseVNode("div", null, [
            createBaseVNode("button", {
              type: "button",
              onClick: edit_,
              disabled: isNotJsonParamsValid.value,
              class: "btn btn-primary"
            }, toDisplayString(_i18n("apply")), 9 /* TEXT, PROPS */, _hoisted_27$2)
          ])
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    script$b.__file = "http_src/vue/modal-edit-component.vue";

    const _hoisted_1$6 = {
      class: "mb-1 me-3",
      style: {"position":"absolute","bottom":"0px","right":"0px"}
    };


    var script$a = {
      __name: 'dashboard-box',
      props: {
      color: String,
      width: Number,
      height: Number,
    },
      setup(__props) {

    const props = __props;

    const width_class = computed$1(() => {
      return `col-sm-${props.width || 4}`;
    });

    const height_class = computed$1(() => {
      let color_class = ``;

      if (props.color) {
        /* Accepted colors: primary, secondary, success, danger, warning, info, light, dark, white */
        color_class = `bg-${props.color}`;
      }

      return `row-${props.height || 4} ${color_class}`;
    });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", {
        class: normalizeClass([width_class.value, "widget-box-main-dashboard"])
      }, [
        createBaseVNode("div", {
          class: normalizeClass([height_class.value, "widget-box"]),
          style: {"position":"relative"}
        }, [
          createCommentVNode(" title "),
          renderSlot(_ctx.$slots, "box_title"),
          createCommentVNode(" content "),
          renderSlot(_ctx.$slots, "box_content"),
          createCommentVNode(" footer "),
          createBaseVNode("div", _hoisted_1$6, [
            renderSlot(_ctx.$slots, "box_footer")
          ])
        ], 2 /* CLASS */)
      ], 2 /* CLASS */))
    }
    }

    };

    script$a.__file = "http_src/vue/dashboard-box.vue";

    var script$9 = {
      __name: 'dashboard-empty',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        filters: Object
    },
      setup(__props) {

    const props = __props;

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], (cur_value, old_value) => {
        refresh_component();
    }, { flush: 'pre', deep: true });

    onBeforeMount(() => {
        init();
    });

    onMounted(() => {
    });

    function init() {
        refresh_component();
    }

    async function refresh_component() {
        /* Refresh component */
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div"))
    }
    }

    };

    script$9.__file = "http_src/vue/dashboard-empty.vue";

    const _hoisted_1$5 = {
      class: "table-responsive",
      style: {"margin-left":"-1rem","margin-right":"-1rem"}
    };


    var script$8 = {
      __name: 'dashboard-table',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        filters: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const table_id = ref('simple_table');
    const table_rows = ref([]);

    const props = __props;

    const hide_head = computed$1(() => {
        return props.params.no_bg;
    });

    const no_background = computed$1(() => {
        return props.params.no_bg;
    });

    const columns = computed$1(() => {
        let columns = props.params.columns.map((c) => {
            if (!c.style && c.data_type) {
                if (c.data_type == "bytes" || c.data_type == "date") {
                    c.style = "text-align: right";
                } else if (c.data_type == "count_score") {
                    c.style = "text-align: center";
                }
            }

            return {
                ...c,
            };
        });

        columns[0].class = (columns[0].class ? (columns[0].class + " ") : "")
            + "first-col-width";

        return columns;
    });

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], (cur_value, old_value) => {
        refresh_table();
    }, { flush: 'pre', deep: true });

    onBeforeMount(() => {
        init();
    });

    onMounted(() => {
    });

    function init() {
        refresh_table();
    }

    const render_column = function (column) {
        if (column.i18n_name) { return _i18n(column.i18n_name); }
        return "";
    };

    const row_render_functions = {

        /* Render function for 'throughput' table type */
        throughput: function (column, row) {
            if (column.id == 'name') {
                let name = row.name;
                if (row['instance_name'])
                    name = `${row.name} [${row.instance_name}]`;
                if (row['url'])
                    return `<a href='${row.url}'>${name}</a>`;
                else
                    return name;
            } else if (column.id == 'throughput') {
                if (row['throughput_type'] && row['throughput_type'] == 'pps') {
                    return NtopUtils$1.fpackets(row[column.id]);
                } else if (row['throughput_type'] && row['throughput_type'] == 'bps') {
                    return NtopUtils$1.bitsToSize(row[column.id]);
                } else {
                    return row['throughput'];
                }
            } else {
                return "";
            }
        },

        /* Render function for 'throughput' table type */
        assets: function (column, row) {
            if (column.id == 'server') {
                let name = row.server;
                if (row['url'])
                    return `<a href='${row.url}'>${name}</a>`;
                else
                    return name;
            } else {
                return NtopUtils$1.formatValue(row[column.id]);
            }
        },

        /* Render function for 'total_throughput' table type */
        total_throughput: function (column, row) {
            if (column.id == 'name') {
                let name = row.name;
                if (row['url'])
                    return `<a href='${row.url}'>${name}</a>`;
                else
                    return name;
            } else if (column.id == 'counters') {
                let label = '';
                if (row.counters) {
                    if (row.counters.throughput) {
                        label += `<i class="fas fa-arrow-up"></i> ${NtopUtils$1.bitsToSize(row.counters.throughput.upload || 0)}</span></a> `;
                        label += `<i class="fas fa-arrow-down"></i> ${NtopUtils$1.bitsToSize(row.counters.throughput.download || 0)}</span></a> `;
                    } else {
                        label += NtopUtils$1.bitsToSize(row.counters.throughput_bps || 0);
                    }
                }
                return label;
            } else {
                return "";
            }
        },

        /* Render function for 'db_search' table type */
        db_search: function (column, row) {
            if (column.data_type == 'host') {
                return NtopUtils$1.formatHost(row[column.id], row, (column.id == 'cli_ip'));
            } else if (column.data_type == 'network') {
                return NtopUtils$1.formatNetwork(row[column.id], row);
            } else if (column.data_type == 'asn') {
                return NtopUtils$1.formatASN(row[column.id], row);
            } else if (column.data_type == 'country') {
                return NtopUtils$1.formatCountry(row[column.id], row);
            } else if (column.data_type == "l7_proto") {
                return row[column.id].label;
            } else if (formatterUtils.types[column.data_type]) {
                // 'bytes', 'bps', 'pps', ...
                let formatter = formatterUtils.getFormatter(column.data_type);
                return formatter(row[column.id]);
            } else if (typeof row[column.id] === 'object') {
                return NtopUtils$1.formatGenericObj(row[column.id], row);
            } else {
                return row[column.id];
            }
        },

        vs_scan_result: function (column, row) {
            if (column.id == "host") {
                return host_f(row[column.id], row, props.ifid);
            } else if (column.id == "last_scan") {
                return last_scan_f(row[column.id], row);
            } else if (column.id == "duration") {
                return duration_f(row[column.id], row);
            } else if (column.id == "scan_frequency") {
                return scan_frequency_f(row[column.id]);
            } else if (column.id == "is_ok_last_scan") {
                return is_ok_last_scan_f(row[column.id]);
            } else if (column.id == "tcp_ports") {
                return tcp_ports_f(row[column.id], row);
            } else if (column.id == "udp_ports") {
                return udp_ports_f(row[column.id], row);
            } else if (column.id == "scan_type") {
                return scan_type_f(row[column.id], true, row);
            } else if (column.id == "hosts") {
                return hosts_f(row[column.id]);
            } else if (column.id == "cve" || column.id == "cve_list") {
                return cves_f(row[column.id], row);
            } else if (column.id == "port") {
                return tcp_port_f(row[column.id], row);
            } else if (column.id == "max_score_cve") {
                return max_score_cve_f(row[column.id], row);
            } else if (column.id == "num_vulnerabilities_found") {
                return num_vuln_found_f(row[column.id], row);
            } else if (column.id == "tcp_udp_ports_list") {
                return tcp_udp_ports_list_f(row["tcp_ports_list"], row["udp_ports_list"], row);
            } else if (column.id == "discovered_hosts") {
                return discoverd_hosts_list_f(row[column.id]);
            } else {
                return row[column.id];
            }
        },

        /* Render function for 'alert_count' table type */
        alert_count: function (column, row) {
            if (column.id == 'name') {
                let name = row.name;
                if (row['url'])
                    return `<a href='${row.url}'>${name}</a>`;
                else
                    return name;
            } else if (column.id == 'counters') {
                let total = row.counters.engaged_alerts || 0;
                let errors = row.counters.engaged_alerts_error || 0;
                let warnings = row.counters.engaged_alerts_warning || 0;
                let infos = 0;
                if (total > errors + warnings) infos = total - (errors + warnings);
                let label = '';
                if (infos > 0)
                    label += `<a href="${row.url}"><span class="badge bg-success"><i class="fas fa-exclamation-triangle"></i> ${infos}</span></a> `;
                if (warnings > 0)
                    label += `<a href="${row.url}"><span class="badge bg-warning"><i class="fas fa-exclamation-triangle"></i> ${warnings}</span></a> `;
                if (errors > 0)
                    label += `<a href="${row.url}"><span class="badge bg-danger"><i class="fas fa-exclamation-triangle"></i> ${errors}</span></a> `;
                return label;
            } else {
                return "";
            }
        },

        /* Render function for 'uptime' table type */
        uptime: function (column, row) {
            if (column.id == 'name') {
                let name = row.name;
                if (row['url'])
                    return `<a href='${row.url}'>${name}</a>`;
                else
                    return name;
            } else if (column.id == 'counters') {
                let label = `${NtopUtils$1.secondsToTime(row.counters.uptime_sec || 0)}`;
                return label;
            } else {
                return "";
            }
        }

    };

    const render_row = function (column, row) {
        if (props.params &&
            props.params.table_type &&
            row_render_functions[props.params.table_type]) {
            const render_func = row_render_functions[props.params.table_type];
            return render_func(column, row);
        } else if (row[column.id]) {
            return row[column.id];
        } else {
            return "";
        }
    };

    async function refresh_table() {
        const url_params = {
            ifid: props.ifid,
            epoch_begin: props.epoch_begin,
            epoch_end: props.epoch_end,
            ...props.params.url_params,
            ...props.filters
        };

        let data = await props.get_component_data(`${http_prefix}${props.params.url}`, url_params, undefined, props.epoch_begin);

        let rows = [];
        if (props.params.table_type == 'db_search') {
            rows = data.records; /* db_search: read data from data.records */
        } else {
            rows = data; /* default: data is the array of records */
        }

        if (props.params.table_type != 'vs_scan_result') {
            const max_rows = props.max_height ? ((props.max_height / 4) * 6) : 6;
            rows = rows.slice(0, max_rows);
        }

        table_rows.value = rows;
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$5, [
        createVNode(script$2d, {
          id: table_id.value,
          columns: columns.value,
          rows: table_rows.value,
          hide_head: hide_head.value,
          no_background: no_background.value,
          print_html_column: render_column,
          print_html_row: render_row,
          wrap_columns: true
        }, null, 8 /* PROPS */, ["id", "columns", "rows", "hide_head", "no_background"])
      ]))
    }
    }

    };

    var css_248z$4 = "\n.first-col-width {\n    /* max-width: 100% !important; */\n}\n@media print and (max-width: 210mm) {\ntd.first-col-width {\n        max-width: 55mm !important;\n}\n}\n@media print and (min-width: 211mm) {\ntd.first-col-width {\n        max-width: 95mm !important;\n}\n}\n\n/* @media print and (max-width: 148mm){ */\n/* } */\n";
    styleInject(css_248z$4);

    script$8.__file = "http_src/vue/dashboard-table.vue";

    var script$7 = {
      __name: 'dashboard-pie',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        filters: Object
    },
      setup(__props) {

    const chart_type = ref(ntopChartApex.typeChart.DONUT);
    const chart = ref(null);

    const props = __props;

    const base_url = computed$1(() => {
        return `${http_prefix}${props.params.url}`;
    });

    const get_url_params = () => {
        const url_params = {
            ifid: props.ifid,
            epoch_begin: props.epoch_begin,
            epoch_end: props.epoch_end,
            new_charts: true,
            ...props.params.url_params,
            ...props.filters
        };
        return url_params;
    };

    function get_chart_options() {
        const url = base_url.value;
        const url_params = get_url_params();
        return props.get_component_data(url, url_params, undefined, props.epoch_begin);
    }

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], (cur_value, old_value) => {
        refresh_chart();
    }, { flush: 'pre', deep: true });

    onBeforeMount(() => {
    });

    onMounted(() => {
    });

    async function refresh_chart() {
        chart.value.update_chart();
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", null, [
        createVNode(script$2I, {
          ref_key: "chart",
          ref: chart,
          id: __props.id,
          chart_type: chart_type.value,
          base_url_request: base_url.value,
          get_custom_chart_options: get_chart_options,
          register_on_status_change: false
        }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"])
      ]))
    }
    }

    };

    script$7.__file = "http_src/vue/dashboard-pie.vue";

    const _hoisted_1$4 = {
      key: 1,
      style: {"position":"relative","display":"flex","flex-direction":"column","justify-content":"center","align-items":"center","width":"100%","height":"100%","min-height":"250px","padding":"5% 20px","color":"#666"}
    };
    const _hoisted_2$4 = { style: {"font-size":"clamp(16px, 2vw, 18px)","margin-bottom":"2vh"} };
    const _hoisted_3$4 = { style: {"font-size":"clamp(14px, 1.5vw, 16px)"} };


    var script$6 = {
      __name: 'dashboard-bar',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        filters: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    const chart_type = ref(ntopChartApex.typeChart.BAR);
    const chart = ref(null);
    const url_list = ref(null);
    const isDataAvailable = ref(false);

    const props = __props;

    const base_url = computed$1(() => {
        return `${http_prefix}${props.params.url}`;
    });

    const get_url_params = () => {
        return {
            ifid: props.ifid,
            epoch_begin: props.epoch_begin,
            epoch_end: props.epoch_end,
            new_charts: true,
            ...props.params.url_params,
            ...props.filters
        }
    };

    async function checkDataAvailability() {
        try {
            const url = base_url.value;
            const url_params = get_url_params();
            const options = await props.get_component_data(url, url_params, undefined, props.epoch_begin);
            
            // check if data is available
            const hasData = options && 
                          options.series && 
                          options.series[0] && 
                          options.series[0].data && 
                          options.series[0].data.filter(value => value > 0).length > 0;
            
            isDataAvailable.value = hasData;
            return hasData;
        } catch (error) {
            console.error("Error checking data availability:", error);
            isDataAvailable.value = false;
            return false;
        }
    }

    function get_chart_options() {
        const url = base_url.value;
        const url_params = get_url_params();
        const options = props.get_component_data(url, url_params, undefined, props.epoch_begin);

        Promise.resolve(options).then(response => {
            // process if there is data
            if (!response || !response.series || !response.series[0] || !response.series[0].data) {
                isDataAvailable.value = false;
                return options;
            }
            
            // remove values that are 0 to not render them
            response.series[0].data = response.series[0].data.filter((value) => value > 0);
            
            // no value after filtering
            if (response.series[0].data.length === 0) {
                isDataAvailable.value = false;
                return options;
            }
            
            isDataAvailable.value = true;
            let data_count = response.series[0].data.length;

            // remove labels and links of values that are 0
            if (response.urls && response.urls.length) {
                response.urls = response.urls.splice(0, data_count);
            }
            
            if (response.xaxis && response.xaxis.categories) {
                response.xaxis.categories = response.xaxis.categories.splice(0, data_count);
            }

            if (response.chart) {
                response.chart = {
                    ...response.chart,
                    events: {
                        dataPointSelection: function (event, chartContext, config) {
                            const selectedIndex = config.dataPointIndex;
                            if (url_list.value && url_list.value[selectedIndex] && 
                                !dataUtils.isEmptyString(url_list.value[selectedIndex])) {
                                window.location.href = url_list.value[selectedIndex];
                            }
                        }
                    }
                };
            }

            // Y-axis configuration
            response.yaxis = {
                forceNiceScale: true,
                showForNullSeries: true,
                showAlways: true,
                labels: {
                    show: true
                }
            };

            if (response.urls) {
                url_list.value = response.urls;
            }
        }).catch(error => {
            console.error("Error processing chart data:", error);
            isDataAvailable.value = false;
        });
        
        return options;
    }

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], async (cur_value, old_value) => {
        await checkDataAvailability();
        if (isDataAvailable.value && chart.value) {
            refresh_chart();
        }
    }, { flush: 'pre', deep: true });

    onBeforeMount(async () => {
        await checkDataAvailability();
    });

    onMounted(() => {
        // No additional initialization needed here
    });

    async function refresh_chart() {
        if (isDataAvailable.value && chart.value) {
            chart.value.update_chart();
        }
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", null, [
        (isDataAvailable.value)
          ? (openBlock(), createBlock(script$2I, {
              key: 0,
              ref_key: "chart",
              ref: chart,
              id: __props.id,
              chart_type: chart_type.value,
              base_url_request: base_url.value,
              get_custom_chart_options: get_chart_options,
              register_on_status_change: false
            }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"]))
          : (openBlock(), createElementBlock("div", _hoisted_1$4, [
              createBaseVNode("div", _hoisted_2$4, [
                _cache[0] || (_cache[0] = createBaseVNode("i", { class: "fas fa-search" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString(_i18n("dashboard.no_assets_discovered")), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_3$4, toDisplayString(_i18n("dashboard.waiting_assets_discovery")), 1 /* TEXT */)
            ]))
      ]))
    }
    }

    };

    script$6.__file = "http_src/vue/dashboard-bar.vue";

    const _hoisted_1$3 = ["id"];
    const _hoisted_2$3 = { key: 0 };
    const _hoisted_3$3 = {
      key: 1,
      style: {"position":"relative","display":"flex","flex-direction":"column","justify-content":"center","align-items":"center","width":"100%","height":"100%","min-height":"250px","padding":"5% 20px","color":"#666"}
    };
    const _hoisted_4$3 = { style: {"font-size":"clamp(16px, 2vw, 18px)","margin-bottom":"2vh"} };
    const _hoisted_5$3 = { style: {"font-size":"clamp(14px, 1.5vw, 16px)"} };

    const maxPieSectors = 5;


    var script$5 = {
      __name: 'dashboard-lateral-pie',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Height (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        filters: Object,

    },
      setup(__props, { expose: __expose }) {


    const d3 = d3v7;
    const _i18n = (t) => i18n(t);

    const chart_data_available = ref(true);
    const chartContainer = ref(null);
    const chartParent = ref(null);
    const url_list = ref(null);
    const chartData = ref(null);
    let resizeObserver = null;
    const props = __props;

    const base_url = computed$1(() => {
        return `${http_prefix}${props.params.url}`;
    });

    const get_url_params = () => {
        const url_params = {
            ifid: props.ifid,
            epoch_begin: props.epoch_begin,
            epoch_end: props.epoch_end,
            new_charts: true,
            ...props.params.url_params,
            ...props.filters
        };
        return url_params;
    };

    async function get_chart_data() {
        const url = base_url.value;
        const url_params = get_url_params();
        const options = props.get_component_data(url, url_params, undefined, props.epoch_begin);

        return Promise.resolve(options).then(response => {
            url_list.value = response.urls || [];
            return response;
        });
    }

    function drawChart(data) {
        const container = chartContainer.value;
        
        // Check that container exists before proceeding
        if (!container) {
            chart_data_available.value = false;
            return;
        }

        if (!data || !data.series || !data.labels || data.series.length === 0) {
            chart_data_available.value = false;
            return;
        }

        // Clear previous content
        container.innerHTML = '';

        // get container dimension
        const containerWidth = Math.max(container.offsetWidth || 300, 300);
        const containerHeight = Math.max(container.offsetHeight || 300, 300);

        // make space for title, chart and legend
        const titleHeight = container.querySelector('h3') ?
            container.querySelector('h3').offsetHeight : 0;

        // Legend space is 20% of height
        const legendHeight = containerHeight * 0.2;
        const chartHeight = containerHeight - titleHeight - legendHeight;

        // margins
        const margin = {
            top: 5,
            right: 5,
            bottom: 5,
            left: 5
        };

        // chart dimensions
        const width = containerWidth - margin.left - margin.right;
        const height = chartHeight - margin.top - margin.bottom;

        // chart radius
        const radius = Math.min(width, height) / 2 * 0.9;

        // Pie chart
        const svg = d3.select(container)
            .append("svg")
            .attr("width", "100%")
            .attr("height", `${chartHeight}px`)
            .attr("viewBox", `0 0 ${containerWidth} ${chartHeight}`)
            .append("g")
            .attr("class", "chart-group")
            .attr("transform", `translate(${width / 2 + margin.left}, ${height / 2 + margin.top})`);

        const sanitizedSeries = data.series.map(val => {
            const numVal = Number(val);
            return isNaN(numVal) ? 0 : numVal;
        });

        // Process data
        const chartPairs = data.labels.map((label, i) => ({
            label: label || "Unnamed",
            value: sanitizedSeries[i] || 0,
            color: (data.colors && data.colors[i]) || d3.schemeCategory10[i % 10],
            url: (url_list.value && url_list.value[i]) || ''
        }));

        // show first maxPieSectors sectors, group the remaining in others
        const topItems = [...chartPairs].slice(0, maxPieSectors);
        const otherItems = [...chartPairs].slice(maxPieSectors);
        
        const othersSum = otherItems.reduce((sum, item) => sum + item.value, 0);
        
        let finalChartItems = [...topItems];
        
        // add others to the chart if there is at least an element
        if (otherItems.length > 0 && othersSum > 0) {
            finalChartItems.push({
                label: `Others (${othersSum})`, // othersSum is the total of devices count
                value: othersSum,
                color: "#999999", // gray for others
                url: '' 
            });
        }

        if (finalChartItems.length === 0 || finalChartItems.every(item => item.value <= 0)) {
            chart_data_available.value = false;
            return;
        }

        // if label is empty add Unknown
        const chartLabels = finalChartItems.map((item) => {
            if (item.label.startsWith(" ")) {
                return "Unknown" + item.label;
            }
            return item.label;
        });

        const chartValues = finalChartItems.map(item => item.value);
        const chartColors = finalChartItems.map(item => item.color);
        const chartUrls = finalChartItems.map(item => item.url);

        
        let legendItems = [...finalChartItems];

        // Prepare legend labels adding unknown to not known manufacturers
        const legendLabels = legendItems.map((item) => {
            if (item.label.startsWith(" ")) {
                return "Unknown" + item.label;
            }
            return item.label;
        });
        const legendColors = legendItems.map(item => item.color);
        const legendUrls = legendItems.map(item => item.url);

        const pie = d3.pie()
            .value(d => d)
            .sort(null);

        const pieData = pie(chartValues);

        const arc = d3.arc()
            .innerRadius(radius * 0.5) // For donut chart
            .outerRadius(radius * 0.8);

        const outerArc = d3.arc()
            .innerRadius(radius * 0.5)
            .outerRadius(radius * 0.85);

        // Add tooltip
        const tooltip = d3.select("body")
            .append("div")
            .attr("class", "d3-tooltip")
            .style("opacity", 0)
            .style("position", "absolute")
            .style("background-color", "white")
            .style("border", "1px solid #ddd")
            .style("border-radius", "3px")
            .style("padding", "5px")
            .style("pointer-events", "none")
            .style("z-index", "100");

        // Generate pie slices
        const slices = svg.selectAll(".arc")
            .data(pieData)
            .enter()
            .append("g")
            .attr("class", "arc");

        // Draw paths
        slices.append("path")
            .attr("d", arc)
            .attr("fill", (d, i) => chartColors[i])
            .attr("stroke", "white")
            .style("stroke-width", "2px")
            .style("cursor", (d, i) => chartUrls[i] && !dataUtils.isEmptyString(chartUrls[i]) ? "pointer" : "default")
            .on("mouseover", function (event, d) {
                d3.select(this)
                    .transition()
                    .duration(100)
                    .attr("d", outerArc);

                tooltip.transition()
                    .duration(200)
                    .style("opacity", 0.9);

                tooltip.html(`${chartLabels[d.index]}`)
                    .style("left", (event.pageX) + "px")
                    .style("top", (event.pageY - 28) + "px");
            })
            .on("mouseout", function () {
                d3.select(this)
                    .transition()
                    .duration(100)
                    .attr("d", arc);

                tooltip.transition()
                    .duration(500)
                    .style("opacity", 0);
            })
            .on("click", function (event, d) {
                if (chartUrls[d.index] && !dataUtils.isEmptyString(chartUrls[d.index])) {
                    window.location.href = chartUrls[d.index];
                }
            });

        // Create the legend container
        const legendContainer = d3.select(container)
            .append("div")
            .attr("class", "d3-legend-container")
            .style("margin-top", "2px")
            .style("width", "100%")
            .style("max-height", `${legendHeight}px`)
            .style("overflow", "hidden")
            .style("display", "flex")
            .style("flex-wrap", "wrap")
            .style("justify-content", "center");

        // Add legend items
        legendLabels.forEach((label, i) => {
            const legendItem = legendContainer
                .append("div")
                .style("display", "inline-flex")
                .style("align-items", "center")
                .style("margin", "1px 4px")
                .style("max-width", `${containerWidth / 3 - 10}px`)
                .style("cursor", legendUrls[i] && !dataUtils.isEmptyString(legendUrls[i]) ? "pointer" : "default")
                .on("click", function () {
                    if (legendUrls[i] && !dataUtils.isEmptyString(legendUrls[i])) {
                        window.location.href = legendUrls[i];
                    }
                });

            legendItem.append("div")
                .style("width", "10px")
                .style("height", "10px")
                .style("background-color", legendColors[i])
                .style("margin-right", "3px")
                .style("flex-shrink", "0");

            legendItem.append("div")
                .attr("class", "legend-text")
                .text(label)
                .style("font-size", "12px")
                .style("white-space", "nowrap")
                .style("overflow", "hidden")
                .style("text-overflow", "ellipsis");
        });

        // Return tooltip cleanup function
        return () => {
            tooltip.remove();
        };
    }

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], () => {
        refresh_chart();
    }, { flush: 'pre', deep: true });


    onMounted(() => {
        // set initial dimension
        if (chartContainer.value && chartParent.value) {

            chartContainer.value.style.width = '100%';
            chartContainer.value.style.height = '100%';
            chartContainer.value.style.minHeight = '300px';


            resizeObserver = new ResizeObserver(() => {
                if (chartData.value) {
                    // redraw pie chart if resize happens
                    nextTick(() => {
                        redraw_chart();
                    });
                }
            });
            resizeObserver.observe(chartParent.value);

            setTimeout(() => {
                refresh_chart();
            }, 100);
        }
    });

    onBeforeUnmount(() => {
        // Clean up resize observer
        if (resizeObserver) {
            resizeObserver.disconnect();
            resizeObserver = null;
        }
    });

    async function refresh_chart() {
        try {
            // First ensure chartContainer exists
            if (!chartContainer.value) {
                return;
            }
            
            const data = await get_chart_data();
            if (!data) {
                chart_data_available.value = false;
                return;
            }
            
            chartData.value = data;
            
            // Use await with nextTick to ensure DOM is updated
            await nextTick();
            
            // Only proceed if chartContainer still exists
            if (chartContainer.value) {
                chartContainer.value.innerHTML = '';
                drawChart(data);
            }
        } catch (error) {
            console.error("Error fetching chart data:", error);
            chart_data_available.value = false;
        }
    }

    // for external usage
    function redraw_chart() {
        if (chartData.value) {

            if (chartContainer.value) {
                chartContainer.value.innerHTML = '';
            }
            drawChart(chartData.value);
        }
    }

    const mountComponent = () => {
        // set initial dimension
        if (chartContainer.value && chartParent.value) {
            chartContainer.value.style.width = '100%';
            chartContainer.value.style.height = '100%';
            chartContainer.value.style.minHeight = '300px';

            resizeObserver = new ResizeObserver(() => {
                if (chartData.value) {
                    // Use nextTick with error handling
                    nextTick().then(() => {
                        if (chartContainer.value) {
                            redraw_chart();
                        }
                    }).catch(err => {
                        console.error("Error in resize redraw:", err);
                    });
                }
            });
            
            resizeObserver.observe(chartParent.value);

            // Use a slightly longer timeout to ensure DOM is fully rendered
            setTimeout(() => {
                refresh_chart().catch(err => {
                    console.error("Error during initial chart refresh:", err);
                });
            }, 200);
        } else {
            console.warn("Chart container or parent not available on mount");
            // Try again in a moment
            setTimeout(mountComponent, 100);
        }
    };


    onMounted(() => {
        mountComponent();
    });


    // Expose methods for external use (Match the Sankey component's approach)
    __expose({
        update_chart: refresh_chart,
        redraw_chart: redraw_chart
    });

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", {
        class: "d-flex flex-column flex-grow-1 position-relative",
        ref_key: "chartParent",
        ref: chartParent
      }, [
        (chart_data_available.value)
          ? (openBlock(), createElementBlock("div", {
              key: 0,
              id: __props.id,
              class: "d3-chart-container",
              ref_key: "chartContainer",
              ref: chartContainer
            }, [
              (__props.i18n_title)
                ? (openBlock(), createElementBlock("h3", _hoisted_2$3, toDisplayString(_i18n(__props.i18n_title)), 1 /* TEXT */))
                : createCommentVNode("v-if", true)
            ], 8 /* PROPS */, _hoisted_1$3))
          : (openBlock(), createElementBlock("div", _hoisted_3$3, [
              createBaseVNode("div", _hoisted_4$3, [
                _cache[0] || (_cache[0] = createBaseVNode("i", { class: "fas fa-search" }, null, -1 /* CACHED */)),
                createTextVNode(" " + toDisplayString(_i18n("dashboard.no_assets_discovered")), 1 /* TEXT */)
              ]),
              createBaseVNode("div", _hoisted_5$3, toDisplayString(_i18n("dashboard.waiting_assets_discovery")), 1 /* TEXT */)
            ]))
      ], 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z$3 = "\n/* Using scoped style like in the Sankey component */\n.d3-chart-container[data-v-4cccd7c0] {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    min-height: 300px;\n    display: block;\n}\n\n/* Use a flex container approach like in Sankey */\n.d-flex[data-v-4cccd7c0] {\n    display: flex;\n}\n.flex-column[data-v-4cccd7c0] {\n    flex-direction: column;\n}\n.flex-grow-1[data-v-4cccd7c0] {\n    flex-grow: 1;\n}\n.position-relative[data-v-4cccd7c0] {\n    position: relative;\n}\n.d3-legend-container[data-v-4cccd7c0] {\n    display: flex;\n    flex-wrap: wrap;\n    justify-content: center;\n    margin-top: 10px;\n}\n.legend-text[data-v-4cccd7c0] {\n    font-size: 12px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    max-width: 150px;\n}\n.d3-tooltip[data-v-4cccd7c0] {\n    position: absolute;\n    z-index: 1070;\n    font-size: 12px;\n    background-color: white;\n    border: 1px solid #ddd;\n    border-radius: 3px;\n    padding: 5px;\n    pointer-events: none;\n}\n";
    styleInject(css_248z$3);

    script$5.__scopeId = "data-v-4cccd7c0";
    script$5.__file = "http_src/vue/dashboard-lateral-pie.vue";

    const _hoisted_1$2 = { class: "row d-flex align-items-center flex-wrap" };
    const _hoisted_2$2 = {
      key: 0,
      class: "hr col-sm-12 mt-2"
    };
    const _hoisted_3$2 = { class: "responsive-title mb-1" };
    const _hoisted_4$2 = {
      key: 0,
      class: "responsive-title mb-0"
    };
    const _hoisted_5$2 = {
      key: 1,
      class: "hr col-sm-12 mt-2"
    };


    var script$4 = {
      __name: 'dashboard-box-overview',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        set_component_attr: Function, /* Callback to set component attributes (e.g. Box active color) */
        filters: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);

    ref('');
    const name = ref('');
    const icon = ref('');
    ref('#');
    const box_data = ref([]);

    const props = __props;

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], (cur_value, old_value) => {
        refresh_component();
    }, { flush: 'pre', deep: true });

    onBeforeMount(() => {
        init();
    });

    onMounted(() => {
    });

    function init() {
        if (props.params.i18n_name) {
            name.value = _i18n(props.params.i18n_name);
        }

        if (props.params.icon) {
            icon.value = props.params.icon + ' fa-2xl';
        }

        refresh_component();
    }

    async function refresh_component() {
        /* Refresh component */

        if (props.params.url) {

            const url_params = {
                ifid: props.ifid,
                epoch_begin: props.epoch_begin,
                epoch_end: props.epoch_end,
                ...props.params.url_params,
                ...props.filters
            };

            let data = await props.get_component_data(`${http_prefix}${props.params.url}`, url_params, undefined, props.epoch_begin);
            box_data.value = data;
        }
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", _hoisted_1$2, [
        (openBlock(true), createElementBlock(Fragment, null, renderList(box_data.value, (data, index) => {
          return (openBlock(), createElementBlock(Fragment, null, [
            (data.add_separator_above)
              ? (openBlock(), createElementBlock("hr", _hoisted_2$2))
              : createCommentVNode("v-if", true),
            createBaseVNode("div", {
              class: normalizeClass(["text-center", [data.text_color || '', data.text_width ? 'col-sm-' + data.text_width : 'col-sm-12']])
            }, [
              createBaseVNode("h5", _hoisted_3$2, toDisplayString(data.label), 1 /* TEXT */),
              (data.num_elements != null)
                ? (openBlock(), createElementBlock("h5", _hoisted_4$2, toDisplayString(data.num_elements), 1 /* TEXT */))
                : createCommentVNode("v-if", true)
            ], 2 /* CLASS */),
            (data.add_separator_below)
              ? (openBlock(), createElementBlock("hr", _hoisted_5$2))
              : createCommentVNode("v-if", true)
          ], 64 /* STABLE_FRAGMENT */))
        }), 256 /* UNKEYED_FRAGMENT */))
      ]))
    }
    }

    };

    var css_248z$2 = "\n.responsive-title[data-v-ac072efe] {\n    /* clamp(MIN, VALORE_FLUIDO, MAX) */\n    font-size: clamp(0.8rem, 2vw, 1.2rem);\n}\n";
    styleInject(css_248z$2);

    script$4.__scopeId = "data-v-ac072efe";
    script$4.__file = "http_src/vue/dashboard-box-overview.vue";

    /* *************************************************** */

    const height_per_row$1 = 62; /* px */

    var script$3 = {
      __name: 'dashboard-top-timeseries',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        csrf: String,
        filters: Object,
    },
      setup(__props, { expose: __expose }) {

    const chart_type = ref(ntopChartApex.typeChart.TS_LINE);
    const chart = ref(null);
    const timeseries_groups = ref([]);
    const group_option_mode = timeseriesUtils.getGroupOptionMode('1_chart_x_yaxis');
    const height = ref(null);
    const ts_request = ref([]);
    const multi_ts_requests = ref([]);

    /* *************************************************** */

    const props = __props;

    /* *************************************************** */

    /* Return the base url of the REST API */
    const base_url = computed$1(() => {
        return `${http_prefix}${props.params.url}`;
    });

    /* *************************************************** */

    function substitute_ifid(params_to_format, current_ifid) {
        let new_formatted_params = {};
        for (const param in (params_to_format)) {
            if (params_to_format[param].contains('$IFID$')) {
                /* Contains $IFID$, substitute with the interface id */
                new_formatted_params[param] = params_to_format[param].replace('$IFID$', current_ifid);
            } else {
                /* does NOT Contains $IFID$, add the plain param */
                new_formatted_params[param] = params_to_format[param];
            }
        }

        return new_formatted_params;
    }

    /* *************************************************** */

    function substitute_exporter(params_to_format, current_exporter) {
        let new_formatted_params = {};
        for (const param in (params_to_format)) {
            if (params_to_format[param].contains('$EXPORTER$')) {
                /* Contains $EXPORTER$, substitute with the interface id */
                new_formatted_params[param] = params_to_format[param].replace('$EXPORTER$', current_exporter);
            } else {
                /* does NOT Contains $EXPORTER$, add the plain param */
                new_formatted_params[param] = params_to_format[param];
            }
        }

        return new_formatted_params;
    }

    /* *************************************************** */

    function substitute_network(params_to_format, current_network) {
        let new_formatted_params = {};
        for (const param in (params_to_format)) {
            if (params_to_format[param].contains('$NETWORK$')) {
                /* Contains $NETWORK$, substitute with the interface id */
                new_formatted_params[param] = params_to_format[param].replace('$NETWORK$', current_network);
            } else {
                /* does NOT Contains $NETWORK$, add the plain param */
                new_formatted_params[param] = params_to_format[param];
            }
        }

        return new_formatted_params;
    }

    /* *************************************************** */

    /* This function is used to substitute to the $IFID$ found in the
     * configuration the correct interface id
     */
    async function format_current_ifid(params_to_format) {
        if (ts_request.value.length > 0) {
            /* Already populated, return */
            return;
        }
        // The ifid is already available from the props, no request is needed
        let new_formatted_params = substitute_ifid(params_to_format, props.ifid);
        new_formatted_params.source_def = [props.ifid];
        ts_request.value.push(new_formatted_params);
    }

    /* *************************************************** */

    /* This function is used to substitute to the $EXPORTER$ found in the
     * configuration the correct flow exporter
     */
    async function format_exporters(params_to_format) {
        if (ts_request.value.length > 0) {
            /* Already populated, return */
            return;
        }

        const exporters_url = "lua/pro/rest/v2/get/flowdevices/stats.lua";
        const exporters_list = await ntopng_utility.http_request(`${http_prefix}/${exporters_url}?ifid=${props.ifid}&gui=true`) || [];
        if (exporters_list) {
            exporters_list.forEach((exporter) => {
                if (exporter) {
                    let new_formatted_params = substitute_exporter(params_to_format, exporter.probe_ip);
                    new_formatted_params = substitute_ifid(new_formatted_params, exporter.ifid);
                    new_formatted_params.source_def = [exporter.ifid, exporter.probe_ip];
                    ts_request.value.push(new_formatted_params);
                }
            });
        }
    }

    /* *************************************************** */

    /* This function is used to substitute to the $NETWORK$ found in the
     * configuration in the correct networks
     */
    async function format_networks(params_to_format) {
        if (ts_request.value.length > 0) {
            /* Already populated, return */
            return;
        }
        const networks_url = "lua/rest/v2/get/network/networks.lua";
        const networks_list = await ntopng_utility.http_request(`${http_prefix}/${networks_url}?ifid=${props.ifid}`) || [];
        if (networks_list) {
            networks_list.forEach((network) => {
                if (network) {
                    let new_formatted_params = substitute_network(params_to_format, network.id);
                    new_formatted_params = substitute_ifid(new_formatted_params, props.ifid);
                    new_formatted_params.source_def = [props.ifid, network.id];
                    ts_request.value.push(new_formatted_params);
                }
            });
        }
    }

    /* *************************************************** */

    /* This function is used to transform the $ANY$ params in the 
     * correct value (e.g. $ANY_IFID$ -> list of all ifid)
     */
    async function resolve_any_params() {
        /* Clear the Array */
        ts_request.value = [];
        /* Here possible ANY, can be found in the post_params */
        const params = props.params.post_params?.ts_requests;
        for (const any_param in (params || {})) {
            switch (any_param) {
                case '$CURRENT_IFID$':
                    await format_current_ifid(params[any_param]);
                    break;
                case '$ANY_EXPORTER$':
                    await format_exporters(params[any_param]);
                    break;
                case '$ANY_NETWORK$':
                    await format_networks(params[any_param]);
                    break;
                default:
                    let new_formatted_params = substitute_ifid(params[any_param], props.ifid);
                    new_formatted_params.source_def = [props.ifid];
                    ts_request.value.push(new_formatted_params);
                    break;
            }
        }
    }

    /* *************************************************** */

    /* The source_type can be found on the json and the source_array is automatically generated
     * by using the source_type
     */
    async function get_timeseries_groups_from_metric(metric_schema, source_def) {
        const status = {
            epoch_begin: props.epoch_begin,
            epoch_end: props.epoch_end,
        };
        const source_type = metricsManager.get_source_type_from_id(props.params?.source_type);
        const source_array = await metricsManager.get_source_array_from_value_array(http_prefix, source_type, source_def);
        const metric = await metricsManager.get_metric_from_schema(http_prefix, source_type, source_array, metric_schema, null, status, true /* Include empty TS */);
        const ts_group = metricsManager.get_ts_group(source_type, source_array, metric, { past: false });
        return ts_group;
    }

    /* *************************************************** */

    async function retrieve_basic_info(request) {
        /* Return the timeseries group, info found in the json */
        if (timeseries_groups.value.length == 0) {
            for (const value of request) {
                const metric_schema = value?.ts_schema;
                const source_def = value.source_def;
                const group = await get_timeseries_groups_from_metric(metric_schema, source_def);
                timeseries_groups.value.push(group);
            }
        }
    }

    /* *************************************************** */

    /* Remove the property otherwise it's going to be added to the REST */
    function remove_extra_params() {
        for (const value of ts_request.value) {
            if (value.source_def) {
                delete value.source_def;
            }
        }
    }

    /* *************************************************** */

    /* This function run the REST API with the data */
    async function get_chart_options() {
        // Also initialize the Tops, in this way just a request when opening
        // the chart has to be done
        await init();
        const post_params = {
            csrf: props.csrf,
            ifid: props.ifid,
            epoch_begin: props.epoch_begin,
            epoch_end: props.epoch_end,
            ...props.params.post_params,
            ...{
                ts_requests: ts_request.value
            }
        };
        //ts_request.value.source_def = [props.ifid]
        post_params.ts_requests = multi_ts_requests.value;
        const data_url = `${http_prefix}/lua/pro/rest/v2/get/timeseries/ts_multi.lua`;
        props.get_component_data();
        let result = await ntopng_utility.http_post_request(data_url, post_params);
        
        /* Format the result in the format needed by Dygraph */
        result = timeseriesUtils.tsArrayToOptionsArray(result, timeseries_groups.value, group_option_mode, '');
        if (result[0]) {
            result[0].height = height.value;
        }
        return result?.[0];
    }

    /* *************************************************** */

    /* Run the init here */
    onBeforeMount(async () => {
        // Initialize the height of the chart
        height.value = (props.max_height || 4) * height_per_row$1;
    });

    /* *************************************************** */

    onMounted(async () => { });

    /* *************************************************** */

    /* Defining the needed info by the get_chart_options function */
    async function init() {
        await resolve_any_params();
        remove_extra_params();
        await getTopInfo();
    }

    /* *************************************************** */

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], (cur_value, old_value) => {
        refreshChart();
    }, { flush: 'pre', deep: true });

    /* *************************************************** */

    async function getTopInfo() {
        // In this way this request is done only the first time
        if (multi_ts_requests.value.length > 0) {
            return;
        }
        // Format the GET request
        const url = base_url.value;
        const ts_source = [];
        const query_params = props.params.url_params;
        query_params.csrf = props.csrf;
        query_params.ifid = props.ifid;
        query_params.epoch_begin = props.epoch_begin;
        query_params.epoch_end = props.epoch_end;
        const url_params = ntopng_url_manager.obj_to_url_params(query_params);
        const top_url = `${http_prefix}${url}?${url_params}`;
        const top_data = await ntopng_utility.http_request(top_url);
        // Retrieve the top data and update the ts_requests used by the ts_multi.lua
        let ts_query = {};
        if (ts_request.value[0].ts_schema === "top:flowdev_port:traffic") {
            ts_query = {
                ts_query: `ifid:$IFID$,device:$DEVICE$,port:$PORT$`,
                ts_schema: `flowdev_port:traffic`,
            };
        } else if (ts_request.value[0].ts_schema === "top:asn:traffic") {
            ts_query = {
                ts_query: `ifid:$IFID$,asn:$ASN$`,
                ts_schema: `asn:traffic`,
            };
        }
        top_data?.forEach((el, i) => {
            const tmp_query = { ...ts_query };
            // Substitute the parameters
            if (ts_request.value[0].ts_schema === "top:flowdev_port:traffic") {
                tmp_query.ts_query = tmp_query.ts_query.replace('$IFID$', el.ifid);
                tmp_query.ts_query = tmp_query.ts_query.replace('$DEVICE$', el.exporter_ip);
                tmp_query.ts_query = tmp_query.ts_query.replace('$PORT$', el.interface_id);
                tmp_query.tskey = `${el.interface_id}`;
            } else if (ts_request.value[0].ts_schema === "top:asn:traffic") {
                tmp_query.ts_query = tmp_query.ts_query.replace('$IFID$', props.ifid);
                tmp_query.ts_query = tmp_query.ts_query.replace('$ASN$', el.asn);
                tmp_query.tskey = `${el.asn}`;
            }
            if (i == 0) {
                const val = ts_request.value.find((source) =>
                    source.ts_query === `ifid:${props.ifid}`
                );
                val.source_def = [props.ifid];
                ts_source.push(val);
            }
            tmp_query.ts_unify = true;
            multi_ts_requests.value.push(tmp_query);
        });
        await retrieve_basic_info(ts_source);
    }

    /* *************************************************** */

    /* Refresh function */
    async function refreshChart() {
        if (chart.value) {
            const result = await get_chart_options();
            if (result?.data) {
                chart.value.update_chart_series(result.data);
            }
        }
    }

    __expose({ refreshChart });


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", null, [
        createVNode(script$2i, {
          ref_key: "chart",
          ref: chart,
          id: __props.id,
          chart_type: chart_type.value,
          base_url_request: base_url.value,
          get_custom_chart_options: get_chart_options,
          register_on_status_change: false,
          disable_fixed_height: true
        }, null, 8 /* PROPS */, ["id", "chart_type", "base_url_request"])
      ]))
    }
    }

    };

    script$3.__file = "http_src/vue/dashboard-top-timeseries.vue";

    const height_per_row = 62.5; /* px */


    var script$2 = {
      __name: 'dashboard-sankey',
      props: {
        id: String,          /* Component ID */
        i18n_title: String,  /* Title (i18n) */
        ifid: String,        /* Interface ID */
        epoch_begin: Number, /* Time interval begin */
        epoch_end: Number,   /* Time interval end */
        max_width: Number,   /* Component Width (4, 8, 12) */
        max_height: Number,  /* Component Hehght (4, 8, 12)*/
        params: Object,      /* Component-specific parameters from the JSON template definition */
        get_component_data: Function, /* Callback to request data (REST) */
        filters: Object
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const no_data_message = _i18n('ports_analysis.no_data');

    const sankey_chart = ref(null);
    const sankey_data = ref({});
    const body_div = ref(null);
    const width = ref(null);
    const height = ref(null);
    const props = __props;

    /* Watch - detect changes on epoch_begin / epoch_end and refresh the component */
    watch(() => [props.epoch_begin, props.epoch_end, props.filters], (cur_value, old_value) => {
        update_sankey();
    }, { flush: 'pre', deep: true });

    onBeforeMount(() => {
    });

    onMounted(() => {
        update_height();
        update_width();

        init();
    });

    function init() {
        update_sankey();
    }

    const update_sankey = function() {
      set_sankey_data();
    };

    async function set_sankey_data() {
        let data = await get_sankey_data();    
        sankey_data.value = data;
    }

    async function get_sankey_data() {
        const url = `${http_prefix}${props.params.url}`;

        const query_params = {
            ifid: props.ifid,
             epoch_begin: props.epoch_begin,
             epoch_end: props.epoch_end,
             sankey_version: 3,
             ...props.params.url_params,
            ...props.filters
        };

        let graph = await props.get_component_data(url, query_params, undefined, props.epoch_begin);

        graph = make_complete_graph(graph);

        const sankey_data = get_sankey_data_from_rest_data(graph);
      
        return sankey_data;
    }

    // remove all links with a not existing node
    function make_complete_graph(graph) {
        let f_log_link = (l) => console.error(`link (source: ${l.source_node_id}, target: ${l.target_node_id}) removed for not existing source/target node`);    
        let links = get_links_with_existing_node(graph, f_log_link);
        return { nodes: graph.nodes, links };
    }

    function get_links_with_existing_node(graph, f_log) {
        let node_dict = {};
        graph.nodes.forEach((n) => node_dict[n.node_id] = true);
        let f_filter = (l) => node_dict[l.source_node_id] != null && node_dict[l.target_node_id] != null;    
        let links = filter_log(graph.links, f_filter, f_log);
        return links;
    }

    // log elements deleted if f_log != null
    function filter_log(elements, f_filter, f_log) {
        return elements.filter((e) => {
    	const take_element = f_filter(e);
    	if (take_element == false && f_log != null) {
    	    f_log(e);
    	}
    	return take_element;
        });
    }

    function get_sankey_data_from_rest_data(res) {
        let node_dict = {}, link_to_nodes_dict = {};
        // create a node dict
        res.nodes.forEach((node) => node_dict[node.node_id] = node);
        
        let f_get_link_node_id = (link) => {
    	return `${link.source_node_id}_${link.label}`; 
        };
        // merge all links by label
        res.links.forEach((link) => {
    	let link_node_id = f_get_link_node_id(link);
    	let link_to_nodes = link_to_nodes_dict[link_node_id];
    	if (link_to_nodes == null) {
    	    link_to_nodes = {
    		id: link_node_id,
    		label: link.label,
        link: link.optional_info.link,
    		node_links: [],		
    	    };
    	    link_to_nodes_dict[link_node_id] = link_to_nodes;
    	}
    	link_to_nodes.node_links.push({
    	    source: node_dict[link.source_node_id],
    	    target: node_dict[link.target_node_id],
    	    value: link.value,
    	});	
        });
        
        // create nodes and links
        let nodes = res.nodes.map((n) => n), links = [];
        for (let link_node_id in link_to_nodes_dict) {
    	let link_to_nodes = link_to_nodes_dict[link_node_id];
    	let link_node = {
    	    node_id: link_to_nodes.id,
    	    label: link_to_nodes.label,
          link: link_to_nodes.link,
    	};
    	nodes.push(link_node);
    	link_to_nodes.node_links.forEach((link) => {
    	    links.push({
    		source_node_id: link.source.node_id,
    		target_node_id: link_node.node_id,
    		label: `${link.source.label} - ${link.target.label}: ${link_node.label}`,
    		value: link.value,
    	    });
    	    links.push({
    		source_node_id: link_node.node_id,
    		target_node_id: link.target.node_id,
    		label: `${link.source.label} - ${link.target.label}: ${link_node.label}`,
    		value: link.value,
    	    });
    	});
        }
        let sankey_nodes = nodes.map((n, index) => {
    	return { index, label: n.label, data: n };
        });
        let sankey_node_dict = {};    
        sankey_nodes.forEach((sn, index) => sankey_node_dict[sn.data.node_id] = sn);
        let sankey_links = links.map((l) => {
    	let source_index = sankey_node_dict[l.source_node_id].index;
    	let target_index = sankey_node_dict[l.target_node_id].index;
    	return {
    	    source: source_index,
    	    target: target_index,
    	    value: l.value,
    	    label: l.label,
    	};
        });
        return { nodes: sankey_nodes, links: sankey_links };
    }

    function on_node_click(node) {
        if (node.is_link_node == true) { return; }
        if (node.link) { ntopng_url_manager.go_to_url(node.link); }
    }

    function update_height() {
        height.value = height_per_row * props.max_height;
    }

    function update_width() {
        width.value = $(body_div.value).width();
    }

    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock("div", {
        ref_key: "body_div",
        ref: body_div
      }, [
        createVNode(script$1T, {
          ref_key: "sankey_chart",
          ref: sankey_chart,
          width: width.value,
          height: height.value,
          no_data_message: unref(no_data_message),
          sankey_data: sankey_data.value,
          onNode_click: on_node_click
        }, null, 8 /* PROPS */, ["width", "height", "no_data_message", "sankey_data"])
      ], 512 /* NEED_PATCH */))
    }
    }

    };

    script$2.__file = "http_src/vue/dashboard-sankey.vue";

    const _hoisted_1$1 = { class: "row" };
    const _hoisted_2$1 = { class: "me-2" };
    const _hoisted_3$1 = ["title"];
    const _hoisted_4$1 = ["title"];
    const _hoisted_5$1 = ["title"];
    const _hoisted_6$1 = ["title"];
    const _hoisted_7$1 = ["title"];
    const _hoisted_8$1 = ["title"];
    const _hoisted_9$1 = ["title"];
    const _hoisted_10$1 = { class: "dropdown me-3 d-inline-block" };
    const _hoisted_11$1 = { class: "ms-1 my-auto me-2 filters-label" };
    const _hoisted_12$1 = {
      key: 0,
      class: "d-flex justify-content-center align-items-center"
    };
    const _hoisted_13$1 = { class: "me-2" };
    const _hoisted_14$1 = { class: "mt-4" };
    const _hoisted_15$1 = {
      key: 1,
      class: "me-auto mt-2 flex-wrap d-flex"
    };
    const _hoisted_16$1 = ["title"];
    const _hoisted_17$1 = ["title"];
    const _hoisted_18$1 = {
      class: "text-warning",
      style: {"margin-top":"0.5rem","margin-left":"1rem"}
    };
    const _hoisted_19$1 = {
      key: 2,
      class: "mt-3",
      style: {"margin-bottom":"-0.5rem","display":"inline"}
    };
    const _hoisted_20$1 = { style: {"text-align":"center"} };
    const _hoisted_21$1 = { key: 0 };
    const _hoisted_22$1 = ["title"];
    const _hoisted_23$1 = ["title"];
    const _hoisted_24$1 = ["title"];
    const _hoisted_25$1 = {
      key: 0,
      class: "col-sm mt-1"
    };
    const _hoisted_26$1 = { class: "alert alert-warning" };
    const _hoisted_27$1 = {
      key: 1,
      class: "col-sm mt-1"
    };
    const _hoisted_28$1 = {
      class: "alert alert-secondary sm-1 text-center",
      style: {"width":"40%","margin":"auto","margin-top":"5vh","margin-bottom":"5vh"}
    };
    const _hoisted_29$1 = { class: "alert-heading" };
    const _hoisted_30$1 = { class: "mb-0" };
    const _hoisted_31$1 = {
      key: 0,
      class: "mb-2 modal-header"
    };
    const _hoisted_32$1 = { class: "modal-title" };
    const _hoisted_33$1 = { style: {"color":"gray"} };
    const _hoisted_34$1 = {
      key: 0,
      class: "modal-close"
    };
    const _hoisted_35$1 = { class: "btn-group" };
    const _hoisted_36$1 = ["data-component-id"];
    const _hoisted_37$1 = ["data-component-id"];
    const _hoisted_38$1 = {
      key: 0,
      style: {"color":"lightgray","font-size":"12px"}
    };
    const REFRESH_INTERVAL_SEC = 10;


    var script$1 = {
      __name: 'dashboard',
      props: {
        context: Object,
    },
      setup(__props) {

    const _i18n = (t) => i18n(t);
    const timeframes_dict = ntopng_utility$1.get_timeframes_dict();

    const props = __props;

    const components_dict = {
        "badge": script$P,
        "empty": script$9,
        "pie": script$7,
        "lateral-pie": script$5,
        "box-overview": script$4,
        "timeseries": script$X,
        "top-timeseries": script$3,
        "bar": script$6,
        "table": script$8,
        "sankey": script$2,
    };

    const loading_filters = ref(false);
    const second_load = ref(false);
    const allow_edit = props.context.allow_edit || false;
    const report_box = ref(null);

    const modal_store_report = ref(null);
    const modal_open_report = ref(null);
    const modal_upload_report = ref(null);

    const modal_add_template_component = ref(null);
    const modal_edit_template_component = ref(null);
    const modal_delete_template = ref(null);

    const main_epoch_interval = ref(null);

    const components = ref([]);
    const components_loaded = ref(false);

    const selected_filters = ref({});
    const all_available_filters = ref({});
    const filtered_filters = ref({});
    const filters_to_show = ref([]);
    const template_filters = ref([]);

    const reports_templates = ref([]);
    const selected_report_template = ref({});

    const warning_message = ref("");

    let components_info = {};
    let data_from_backup = false;
    let printable = false;

    const edit_mode = ref(false);
    let template_sortable = null;
    const modal_new_template = ref(null);
    const isFirstLoading = ref(false);

    const is_live = computed$1(() => {
        return props.context.page != "report" && props.context.page != "vs-report";
    });

    const enable_date_time_range_picker = computed$1(() => {
        return props.context.page == "report"
            && !printable;
    });

    const enable_small_picker = computed$1(() => {
        return props.context.page == "vs-report";
    });

    const enable_small_picker_actions = computed$1(() => {
        return true; // Set to false for hiding open/save actions in the small picker
    });

    const disable_date = computed$1(() => {
        return (selected_report_template.value?.toolbox?.time?.hide == true) || props.context.disable_date;
    });

    const disable_date_time_picker = computed$1(() => {
        const disabled = selected_report_template.value.is_open_report == true
            || disable_date.value;
        return disabled;
    });

    const enable_report_title = computed$1(() => {
        const enable = selected_report_template.value.is_open_report == true
            || props.context.page == "vs-report";
        return enable;
    });

    const report_title = computed$1(() => {
        let title = "";

        if (selected_report_template.value.is_open_report) {
            title = `Report: ${selected_report_template.value.value}`;
        } else if (props.context.title) {
            title = props.context.title;
        }

        return title;
    });

    const component_custom_style = computed$1(() => {
        return (c) => {
            if (c.params.custom_style != null && (!printable || c.params.custom_print_style)) {
                return c.params.custom_style;
            } else if (c.params.custom_print_style && printable == true) {
                return c.params.custom_print_style;
            }
            return "";
        };
    });

    const component_interval = computed$1(() => {
        return (c) => {
            const time_interval_string = get_time_interval_string(c.epoch_begin, c.epoch_end);
            return time_interval_string;
        };
    });

    /* Param report_template is optional (uses url report_template or props.context.template otherwise) */
    async function set_template(report_template) {
        let epoch_interval = null;

        printable = ntopng_url_manager$1.get_url_entry("printable") == "true";

        if (props.context.page == "report" || props.context.page == "vs-report") {
            if (props.context.page == "report") {
                epoch_interval = ntopng_utility$1.check_and_set_default_time_interval(undefined, undefined, true, "min");
            } else if (props.context.page == "vs-report") {
                epoch_interval = ntopng_utility$1.check_and_set_default_time_interval(undefined, undefined, true);
            }
            main_epoch_interval.value = epoch_interval;
        }

        await set_templates_list(report_template);

        let report_name = ntopng_url_manager$1.get_url_entry("report_name");
        if (report_name != null && report_name != "") {
            /* Report name provided - open a report backup */
            await open_report(report_name);
        } else {
            /* Load a template and build a new report */
            await load_components(epoch_interval, selected_report_template.value.value);
        }
        if (printable == true) {
            set_report_title();
            // await nextTick();
            // ntopng_sync.ready("print_report");
        }

        components_loaded.value = true;
    }

    onBeforeMount(async () => {
        set_template();
    });

    onMounted(async () => {
        if (props.context.page == "dashboard") {
            start_dashboard_refresh_loop();
        }
        // if (printable == true) {
        //     await ntopng_sync.on_ready("print_report");
        // }
    });

    async function set_templates_list(report_template) {
        const url_request = props.context.template_list_endpoint;
        let res = await ntopng_utility$1.http_request(url_request);
        if (res?.list == null) { return; }

        let templates_list = res.list.map((t) => {
            return {
                value: t.name,
                label: t.label,
                disabled: false,
                toolbox: t.toolbox,
                is_open_report: false,
                allow_edit: t.allow_edit,
            };
        });

        templates_list.sort((a, b) => a.label.toUpperCase().localeCompare(b.label.toUpperCase()));

        reports_templates.value = templates_list;

        const report_template_value = report_template ||
            ntopng_url_manager$1.get_url_entry("report_template") ||
            props.context.template;
        props.context.template = report_template_value;
        selected_report_template.value = reports_templates.value.find((t) => t.value == report_template_value);

        if (selected_report_template.value == null) {
            selected_report_template.value = reports_templates.value[0];
        }

        if (!selected_report_template.value.allow_edit && edit_mode.value) {
            toggle_edit_mode();
        }
    }

    /* Dashboard update interval/frequency */
    function start_dashboard_refresh_loop() {
        const loop_interval = REFRESH_INTERVAL_SEC * 1000;
        setInterval(() => {
            set_components_epoch_interval();
        }, loop_interval);
    }

    function set_components_filter(filter_id, filter_value) {
        if (filter_value) { filter_value = filter_value + ";eq"; }
        ntopng_url_manager$1.set_key_to_url(filter_id, filter_value);
        components.value.forEach((c, i) => {
            update_component_filters(c, filter_id, filter_value);
        });
    }

    function set_components_epoch_interval(epoch_interval) {
        if (epoch_interval) {
            main_epoch_interval.value = epoch_interval;
        }

        components.value.forEach((c, i) => {
            update_component_epoch_interval(c, epoch_interval);
        });
    }

    /* This is used to reset the filters putting all of them to the ALL value */
    async function reset_filters() {
        /* Iterate all the filters available */
        for (const [filter, value] of Object.entries(all_available_filters.value)) {
            /* Set each filter to the ALL value (first value) */
            set_components_filter(filter, value[0].value);
            selected_filters.value[filter] = value[0];
            /* Hide all the needed filters */
        }
        filters_to_show.value = await load_filters(template_filters.value, null, true);
    }

    /* This function loads the filters */
    async function load_filters(filters_available, res, show_second_load) {
        (!show_second_load) ? loading_filters.value = true : second_load.value = true;
        const added_filters_list = [];
        if (!res && (props.context.report_filters_endpoint != null)) {
            let retrieve_filters = '';
            let extra_params = '';
            let tmp = {};
            filters_available.forEach((element) => {
                if (element.show_only_if_selected) {
                    const selected = ntopng_url_manager$1.get_url_entry(element.show_only_if_selected) || '';
                    if (selected !== '') {
                        retrieve_filters = `${retrieve_filters}${element.name},`;
                        tmp[element.show_only_if_selected] = selected;
                    }
                } else {
                    retrieve_filters = `${retrieve_filters}${element.name},`;
                }
            });
            for (const [key, value] of Object.entries(tmp)) {
                extra_params = `${key}=${value}&${extra_params}`;
            }
            retrieve_filters = retrieve_filters.slice(0, -1);
            /* Being a mix of flows, historical/hourly, keep in mind
             * that not all the filters are available in the hourly, so retrieve only the
             * ones available in both
             */
            res = await ntopng_utility$1.http_request(`${props.context.report_filters_endpoint}?hide_exporters_name=true&filters_to_display=${retrieve_filters}&aggregated=true&${extra_params}`);
        }
        filters_available.forEach((element) => {
            const id = element?.name || "";
            const filter_options = res.find((el) => el.id == id)?.options;
            /* Check the filters available, if no filter or only 1 filter is provided, hide the dropdown */
            if (filter_options && filter_options.length > 0) {
                const selected = ntopng_url_manager$1.get_url_entry(id);
                let all_label = i18n('db_search.all.' + id);
                if (dataUtils.isEmptyOrNull(all_label)) {
                    all_label = i18n('all') + " " + i18n('db_search.' + id);
                }
                /* Add the 'All' filter */
                /* To be safe, add a default name */
                filter_options.unshift({
                    value: null,
                    label: all_label,
                    display_more_filters: filter_options[0].display_more_filters
                });

                if (selected && selected !== '') {
                    const tmp_value = filter_options.find((el) => selected.startsWith(el.value));
                    selected_filters.value[id] = tmp_value;
                } else {
                    selected_filters.value[id] = filter_options[0];
                }

                all_available_filters.value[id] = filter_options;
                filtered_filters.value[id] = filter_options;
                added_filters_list.push(id);
            }
        });
        (!show_second_load) ? loading_filters.value = false : second_load.value = false;
        return added_filters_list;
    }

    async function load_components(epoch_interval, template_name) {
        /* Enable REST calls */
        data_from_backup = false;

        let url_request = `${props.context.template_endpoint}?template=${template_name}`;
        let res = await ntopng_utility$1.http_request(url_request);
        components.value = res.list.filter((c) => components_dict[c.component] != null)
            .map((c, index) => {
                let c_ext = {
                    filters: {},
                    component_id: `auto_${c.id}_${index}`,
                    isLoading: true,
                    ...c
                };
                update_component_epoch_interval(c_ext, epoch_interval);
                return c_ext;
            });
        reset_filters();
        if (res.filters) {
            template_filters.value = res.filters;
            filters_to_show.value = await load_filters(res.filters);
        }
        await nextTick();
    }

    function update_component_epoch_interval(c, epoch_interval) {
        const interval_seconds = timeframes_dict[c.time_window || "5_min"];
        if (epoch_interval == null) {
            const epoch_end = ntopng_utility$1.get_utc_seconds();
            epoch_interval = { epoch_begin: epoch_end - interval_seconds, epoch_end: epoch_end };
        }
        const utc_offset = timeframes_dict[c.time_offset] || 0;
        c.epoch_begin = epoch_interval.epoch_begin - utc_offset;
        c.epoch_end = epoch_interval.epoch_end - utc_offset;
    }

    function update_component_filters(c, filter_id, filter_value) {
        c.filters[filter_id] = filter_value;
    }

    /* ********************************************* */

    /* This function is called whenever a filter is clicked */
    async function select_filter(option, filter_id) {
        /* Set the filter, ready for the rest */
        set_components_filter(filter_id, option.value);
        if (option.display_more_filters) {
            filters_to_show.value = await load_filters(template_filters.value, null, true);
        }
    }

    /* ********************************************* */

    function select_report_template() {
        if (!selected_report_template.value.allow_edit && edit_mode.value) {
            toggle_edit_mode();
        }

        if (printable == true) {
            set_report_title();
        }
        if (selected_report_template.value.is_open_report == true) {
            return;
        }
        components_info = {};
        update_templates_list();
        const global_status = ntopng_status_manager$1.get_status(true);
        let epoch_interval = { epoch_begin: global_status.epoch_begin, epoch_end: global_status.epoch_end };
        if (data_from_backup == true) { // last report selected it was a saved report and then we must to restore default timestamp
            epoch_interval = ntopng_utility$1.set_default_time_interval(undefined, "min");
            ntopng_events_manager$1.emit_event(ntopng_events.EPOCH_CHANGE, epoch_interval, props.context.page);
        }
        load_components(epoch_interval, selected_report_template.value.value);
    }

    function show_store_report_modal() {
        modal_store_report.value.show();
    }

    function show_open_report_modal() {
        modal_open_report.value.show();
    }

    function show_upload_report_modal() {
        modal_upload_report.value.show();
    }

    function get_suggested_report_name() {
        let name = "report";
        if (props.context.page == "vs-report") {
            name = props.context.title;
        } else if (main_epoch_interval.value &&
            main_epoch_interval.value.epoch_end) {
            name += "-" + ntopng_utility$1.from_utc_to_server_date_format(main_epoch_interval.value.epoch_end * 1000, 'DD-MM-YYYY');
        }
        return name;
    }

    const upload_report = async (content_string) => {
        let content = JSON.parse(content_string);
        set_report(content, content.name);
        ntopng_url_manager$1.delete_key_from_url("report_name");
    };

    function set_report(content, name) {
        update_templates_list(name);
        const epoch_status = { epoch_begin: content.epoch_begin, epoch_end: content.epoch_end };
        ntopng_events_manager$1.emit_event(ntopng_events.EPOCH_CHANGE, epoch_status, props.context.page);
        load_report(content);
    }

    const list_reports = async () => {
        let url = `${props.context.report_list_endpoint}?ifid=${props.context.ifid}`;
        let files_obj = await ntopng_utility$1.http_request(url);
        let files = ntopng_utility$1.object_to_array(files_obj);

        /* Return array of [{ name: String, epoch: Number }, ...] */

        return files;
    };

    const load_report = async (content) => {
        let tmp_epoch_interval = {
            epoch_begin: content.epoch_begin,
            epoch_end: content.epoch_end
        };
        let tmp_template = content.template;
        let tmp_components_data = content.data;

        let tmp_components_info = {};
        for (let key in tmp_components_data) {
            let info = {
                data: tmp_components_data[key],
            };
            tmp_components_info[key] = info;
        }

        /* Disable REST calls */
        data_from_backup = true;

        /* Set the cached data from the backup */
        components_info = tmp_components_info;

        /* Change the components (template) from the backup */
        components.value = tmp_template;

        /* Change the time interval on components */
        set_components_epoch_interval(tmp_epoch_interval);
    };

    const open_report = async (file_name) => {
        let url = `${props.context.report_open_endpoint}?ifid=${props.context.ifid}&report_name=${file_name}`;
        let content = await ntopng_utility$1.http_request(url);
        if (content) {
            set_report(content, file_name);
            warning_message.value = "";
        } else {
            warning_message.value = _i18n("report.unable_to_open");
        }
    };

    function update_templates_list(report_name_to_open) {
        reports_templates.value = reports_templates.value.filter((t) => t.is_open_report == false);
        if (report_name_to_open == null) { // in this case is selected a report_template
            ntopng_url_manager$1.set_key_to_url("report_template", selected_report_template.value.value);
            props.context.template = selected_report_template.value.value;
            ntopng_url_manager$1.delete_key_from_url("report_name");
            return;
        }

        let t_entry = {
            value: report_name_to_open,
            label: _i18n("dashboard.custom"),
            disabled: false,
            toolbox: null,
            is_open_report: true,
            allow_edit: false
        };
        reports_templates.value.push(t_entry);
        selected_report_template.value = t_entry;
        props.context.template = report_name_to_open;
        ntopng_url_manager$1.set_key_to_url("report_name", selected_report_template.value.value);
        ntopng_url_manager$1.delete_key_from_url("report_template");
    }

    const delete_report = async (file_name) => {
        let success = false;

        let params = {
            csrf: props.context.csrf,
            ifid: props.context.ifid,
            report_name: file_name
        };

        let url = `${props.context.report_delete_endpoint}`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
            success = true;
        } catch (err) {
            console.error(err);
        }

        return success;
    };

    /* Dump report content - keep in sync with dashboard_utils.build_report (lua) */
    const serialize_report = async (name) => {

        let tmp_components_data = {};
        for (var key in components_info) {
            tmp_components_data[key] = await components_info[key].data;
        }

        let content = {
            version: "1.0", // Report dump version
            name: name,
            template: components.value,
            data: tmp_components_data
        };

        if (main_epoch_interval.value &&
            main_epoch_interval.value.epoch_begin &&
            main_epoch_interval.value.epoch_end) {
            content.epoch_begin = main_epoch_interval.value.epoch_begin;
            content.epoch_end = main_epoch_interval.value.epoch_end;
        }

        return JSON.stringify(content);
    };

    const store_report = async (file_name) => {
        let success = false;

        let data = {
            csrf: props.context.csrf,
            ifid: props.context.ifid,
            report_name: file_name,
            content: await serialize_report(file_name)
        };

        let url = `${props.context.report_store_endpoint}`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(data) });
            success = true;
        } catch (err) {
            console.error(err);
        }

        return success;
    };

    async function download_report() {
        var name = get_suggested_report_name();
        var filename = name + '.json';
        var content = await serialize_report(name);
        var element = document.createElement('a');
        element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content));
        element.setAttribute('download', filename);
        element.style.display = 'none';
        document.body.appendChild(element);
        element.click();
        document.body.removeChild(element);
    }

    function print_report() {
        if (printable == true) {
            window.print();
            return false;
        }

        let url_params_obj = ntopng_url_manager$1.get_url_object();
        url_params_obj.printable = true;
        const params = ntopng_url_manager$1.obj_to_url_params(url_params_obj);

        let url = `${window.location.origin}${window.location.pathname}?${params}`;

        //const print_key = "printable";
        //ntopng_url_manager.set_key_to_url(print_key, true);

        ntopng_url_manager$1.open_new_window(url);

        //ntopng_url_manager.delete_key_from_url(print_key);
        // $(report_box.value).print({mediaPrint: true, timeout: 1000}); 
        // $(report_box.value).print();
    }

    function get_time_interval_string(epoch_begin, epoch_end) {
        if (disable_date.value == true) { return ""; }

        const epoch_begin_msec = epoch_begin * 1000;
        const epoch_end_msec = epoch_end * 1000;

        const begin_date = ntopng_utility$1.from_utc_to_server_date_format(epoch_begin_msec, 'DD/MM/YYYY');
        const begin_time = ntopng_utility$1.from_utc_to_server_date_format(epoch_begin_msec, 'HH:mm:ss');

        const end_date = ntopng_utility$1.from_utc_to_server_date_format(epoch_end_msec, 'DD/MM/YYYY');
        const end_time = ntopng_utility$1.from_utc_to_server_date_format(epoch_end_msec, 'HH:mm:ss');

        const begin = `${begin_date} ${begin_time}`;
        const end = (begin_date == end_date) ? `${end_time}` : `${end_date} ${end_time}`;
        return `${begin} - ${end}`;
    }

    function set_report_title() {
        const epoch_interval = ntopng_status_manager$1.get_status(true);
        const time_interval_string = get_time_interval_string(epoch_interval.epoch_begin, epoch_interval.epoch_end);
        let title = `ntopng - Report ${selected_report_template.value.label} ${time_interval_string}`;
        document.title = title;
    }

    /* *************************************************** */

    /* This function returns true if the previous parameters and the current one
     * are different. This is used in order to do not make the same request multiple times
     * e.g. a component is not yet loaded and the same request is resent
     */
    function check_diff_params(previous_params, current_params) {
        /* Empty params, return true, they are different */
        if (!previous_params || !current_params)
            return true;
        /* Check the length, if it's different return true, a new/removed filter/param */
        if (Object.keys(previous_params).length != Object.keys(current_params).length)
            return true;

        /* Same length, so check that all the key - value are present in both, if not, they are different */
        for (const [key, value] of Object.entries(previous_params)) {
            /* Check for different params */
            if (!current_params[key] || (current_params[key] !== value))
                return true;
        }

        /* The for is ended, meaning that all parameters were equal */
        return false;
    }

    /* *************************************************** */

    /* Callback to request REST data from components */
    function get_component_data_func(component) {
        const get_component_data = async (url, query_params, post_params, refresh_epoch, skip) => {
            if (skip === true) {
                component.isLoading = false;
                return;
            }
            if (!isFirstLoading.value) {
                component.isLoading = true;
            }
            let info = {};
            if (data_from_backup) {
                // backward compatibility (component_id was not defined)
                if (component.component_id == null) component.component_id = component.id;

                if (!components_info[component.component_id]) { /* Safety check */

                    console.error("No data for " + component.component_id);
                    info.data = {};
                } else {
                    info = components_info[component.component_id];
                }
                component.isLoading = false;
            } else {
                /* datasource_id is an optimization for components getting live data
                 * from the same endpoint (e.g. multiple badges in the infrastructure dashboard) */
                let datasource_id = component.datasource_id ? component.datasource_id : component.component_id;

                /* Check if there is already a promise for the same request */
                let pending = false;
                if (components_info[datasource_id]) {
                    info = components_info[datasource_id];
                    if (info.data && !info.data.done) {
                        pending = true;
                        await info.data; /* wait in case of previous pending requests */
                        component.isLoading = false;
                    }
                }

                if (pending /* pending request from previous iteration (slow) */
                    /* or other component calling the same endpoint (same time slot) */
                    || !check_diff_params(info.filters, query_params)) ; else {
                    /* Request fresh data */
                    if (!url) {
                        component.isLoading = false;
                        return;
                    }

                    /* If infrastructure monitor, call the aggregator endpoint */
                    if (props.context.is_infrastructure && !url.includes("infrastructure")) {
                        const infrastructure_proxy_url = "/lua/pro/rest/v2/get/infrastructure/aggregate.lua";

                        query_params['endpoint'] = url;
                        query_params['component'] = component.component;

                        url = infrastructure_proxy_url;
                    }

                    let url_params = ntopng_url_manager$1.obj_to_url_params(query_params);

                    /* Push ifid to the parameters (e.g. "ts_query=ifid:$IFID$" */
                    url_params = url_params.replaceAll("%24IFID%24" /* $IFID$ */, props.context.ifid);

                    const data_url = `${url}?${url_params}`;


                    if (post_params) {
                        info.data = ntopng_utility$1.http_post_request(data_url, post_params);
                    } else {
                        info.data = ntopng_utility$1.http_request(data_url);
                    }

                    info.filters = query_params;
                    components_info[datasource_id] = info;

                    info.data.then(() => {
                        info.data.done = true;
                        component.isLoading = false;
                        /* Disable the periodic loading component in case of Dashboard */
                        if (!isFirstLoading.value && props.context.page !== "report") {
                            isFirstLoading.value = true;
                        }
                    });

                }
            }
            return info.data;
        };
        return get_component_data
    }

    /* Callback to request REST data from components */
    function set_component_attr_func(component) {
        const set_component_attr = async (attr, value) => {
            component[attr] = value;
        };
        return set_component_attr;
    }

    /* ********************************************* */
    /* ************** Template Editor ************** */

    function show_new_template_modal() {
        modal_new_template.value.show();
    }

    function get_suggested_template_name() {
        let name = "New Template";
        return name;
    }

    const new_template = async (template_name) => {
        let success = false;

        let data = {
            csrf: props.context.csrf,
            template_name: template_name,
        };

        let url = `${props.context.template_add_endpoint}`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            let res = await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(data) });

            if (res && res.template_id) {
                await set_template(res.template_id);
            }

            success = true;
        } catch (err) {
            console.error(err);
        }

        return success;
    };

    async function commit_template_change(e) {

        let components_ids = components.value.map((c) => { return c.id; });

        let data = {
            csrf: props.context.csrf,
            template: selected_report_template.value.value,
            components: components_ids.toString()
        };

        let success = false;
        let url = `${props.context.template_edit_endpoint}`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            let res = await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(data) });
            success = true;
        } catch (err) {
            console.error(err);
        }

        return success;
    }

    async function component_dragged(e) {

        /* console.log(template_sortable.toArray().toString()); */

        let new_components = template_sortable.toArray().map((id) => {
            return components.value.find((c) => c.id == id);
        });

        unset_sortable_template();

        components.value = new_components;

        await nextTick();
        set_sortable_template();

        commit_template_change();
    }

    function set_sortable_template() {
        if (template_sortable) {
            return;
        }

        var drag_zone = document.getElementById('drag-zone');
        template_sortable = window.Sortable.create(drag_zone, {
            draggable: ".drag-item",
            dataIdAttr: "id",
            onUpdate: component_dragged
        });

        //console.log("Sortable ON");
    }

    function unset_sortable_template() {
        if (template_sortable) {
            template_sortable.destroy();
            template_sortable = null;
            //console.log("Sortable OFF");
        }
    }

    function toggle_edit_mode() {
        edit_mode.value = !edit_mode.value;

        if (edit_mode.value) {
            set_sortable_template();
        } else {
            unset_sortable_template();
        }
    }

    const list_template_components = async () => {
        let url = `${props.context.template_list_widgets_endpoint}`;
        let res = await ntopng_utility$1.http_request(url);
        if (res?.list == null) { return {}; }
        let widgets = res.list;

        widgets.sort((a, b) => _i18n(a.i18n_name).toUpperCase().localeCompare(_i18n(b.i18n_name).toUpperCase()));

        return widgets;
    };

    function show_add_template_component_modal() {
        modal_add_template_component.value.show();
    }

    function show_delete_template_modal() {
        modal_delete_template.value.show();
    }

    const add_template_component = async (c) => {
        unset_sortable_template();

        let url = `${props.context.template_component_add_endpoint}`;
        let params = {
            csrf: props.context.csrf,
            template: props.context.template,
            component: c.id
        };
        let headers = {
            'Content-Type': 'application/json'
        };
        try {
            let content = await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
            warning_message.value = "";
            await set_template(selected_report_template.value.value);
        } catch (err) {
            warning_message.value = _i18n("report.unable_to_open");
        }

        await nextTick();
        set_sortable_template();
    };

    async function remove_template_component(e) {
        unset_sortable_template();

        const component_id = e.target.dataset.componentId;
        components.value = components.value.filter(c => c.id !== component_id);

        await nextTick();
        set_sortable_template();

        commit_template_change();
    }

    /**
     * @brief The method used to open the edit component modal 
     *        involves triggering a click event on the 'Edit' (pencil icon) button.
     * @param e event object
     */
    function show_edit_template_component(e) {
        const component_id = e.target.dataset.componentId;
        const component = components.value.find(c => c.id === component_id);
        const is_report_page = props.context.page == 'report';
        modal_edit_template_component.value.show(component, is_report_page);
    }

    /**
     * 
     * @brief The method used to update the selected component with new settings 
     *        involves making a REST call.
     * @param new_component An object with new settings for the component to edit. 
     */
    async function edit_template_component(new_component) {
        unset_sortable_template();

        let url = `${props.context.template_component_edit_endpoint}`;
        let params = {
            csrf: props.context.csrf,
            template: props.context.template,
            component: new_component.id,
            component_title: new_component.title,
            component_height: new_component.height,
            component_width: new_component.width,
            component_time_offset: new_component.time_offset,
            component_time_window: new_component.time_window,
            component_params: new_component.rest_params
        };

        let headers = {
            'Content-Type': 'application/json'
        };
        try {
            let content = await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(params) });
            warning_message.value = "";
            await set_template(selected_report_template.value.value);
        } catch (err) {
            warning_message.value = _i18n("report.unable_to_open");
        }

        await nextTick();
        set_sortable_template();
    }

    async function delete_template() {
        let success = false;

        let data = {
            csrf: props.context.csrf,
            template: selected_report_template.value.value,
        };

        let url = `${props.context.template_delete_endpoint}`;
        try {
            let headers = {
                'Content-Type': 'application/json'
            };
            let res = await ntopng_utility$1.http_request(url, { method: 'post', headers, body: JSON.stringify(data) });

            await set_template();

            success = true;
        } catch (err) {
            console.error(err);
        }

        return success;
    }

    /* ********************************************* */


    return (_ctx, _cache) => {
      return (openBlock(), createElementBlock(Fragment, null, [
        createBaseVNode("div", _hoisted_1$1, [
          createCommentVNode(" <Dropdown v-for=\"(t, t_index) in top_table_array\" "),
          createCommentVNode("           :ref=\"el => { top_table_dropdown_array[t_index] = el }\"> "),
          createCommentVNode("   <template v-slot:title> "),
          createCommentVNode("     <Spinner :show=\"t.show_spinner\" size=\"1rem\" class=\"me-1\"></Spinner> "),
          createCommentVNode("     <a class=\"ntopng-truncate\" :title=\"t.title\">{{ t.label }}</a> "),
          createCommentVNode("   </template> "),
          createCommentVNode("   <template v-slot:menu> "),
          createCommentVNode("     <a v-for=\"opt in t.options\" style=\"cursor:pointer; display: block;\" "),
          createCommentVNode("        @click=\"add_top_table_filter(opt, $event)\" class=\"ntopng-truncate tag-filter \" "),
          createCommentVNode("        :title=\"opt.value\">{{ opt.label }}</a> "),
          createCommentVNode("   </template>     "),
          createCommentVNode(" </Dropdown> "),
          (enable_date_time_range_picker.value)
            ? (openBlock(), createBlock(script$2G, {
                key: 0,
                class: "dontprint",
                disabled_date_picker: disable_date_time_picker.value,
                id: "dashboard-date-time-picker",
                round_time: true,
                min_time_interval_id: "min",
                onEpoch_change: set_components_epoch_interval
              }, {
                begin: withCtx(() => [
                  createBaseVNode("div", _hoisted_2$1, [
                    createVNode(script$2H, {
                      selected_option: selected_report_template.value,
                      "onUpdate:selected_option": _cache[0] || (_cache[0] = $event => ((selected_report_template).value = $event)),
                      options: reports_templates.value,
                      onSelect_option: select_report_template
                    }, null, 8 /* PROPS */, ["selected_option", "options"])
                  ])
                ]),
                extra_buttons: withCtx(() => [
                  createBaseVNode("button", {
                    class: "btn btn-link btn-sm",
                    type: "button",
                    onClick: show_store_report_modal,
                    title: _i18n('dashboard.store')
                  }, [...(_cache[1] || (_cache[1] = [
                    createBaseVNode("i", { class: "fa-solid fa-floppy-disk" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_3$1),
                  createBaseVNode("button", {
                    class: "btn btn-link btn-sm",
                    type: "button",
                    onClick: show_open_report_modal,
                    title: _i18n('dashboard.open')
                  }, [...(_cache[2] || (_cache[2] = [
                    createBaseVNode("i", { class: "fa-solid fa-folder-open" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_4$1),
                  createBaseVNode("button", {
                    class: "btn btn-link btn-sm",
                    type: "button",
                    onClick: download_report,
                    title: _i18n('download')
                  }, [...(_cache[3] || (_cache[3] = [
                    createBaseVNode("i", { class: "fa-solid fa-file-arrow-down" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_5$1),
                  createBaseVNode("button", {
                    class: "btn btn-link btn-sm",
                    type: "button",
                    onClick: show_upload_report_modal,
                    title: _i18n('upload')
                  }, [...(_cache[4] || (_cache[4] = [
                    createBaseVNode("i", { class: "fa-solid fa-file-arrow-up" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_6$1),
                  createBaseVNode("button", {
                    class: "btn btn-link btn-sm",
                    type: "button",
                    onClick: print_report,
                    title: _i18n('dashboard.print')
                  }, [...(_cache[5] || (_cache[5] = [
                    createBaseVNode("i", { class: "fas fa-print" }, null, -1 /* CACHED */)
                  ]))], 8 /* PROPS */, _hoisted_7$1),
                  (unref(allow_edit))
                    ? (openBlock(), createElementBlock("button", {
                        key: 0,
                        class: "btn btn-link btn-sm",
                        type: "button",
                        onClick: show_new_template_modal,
                        title: _i18n('dashboard.new_template')
                      }, [...(_cache[6] || (_cache[6] = [
                        createBaseVNode("i", { class: "fas fa-folder-plus" }, null, -1 /* CACHED */)
                      ]))], 8 /* PROPS */, _hoisted_8$1))
                    : createCommentVNode("v-if", true),
                  (unref(allow_edit) && selected_report_template.value.allow_edit)
                    ? (openBlock(), createElementBlock("button", {
                        key: 1,
                        class: normalizeClass(["btn btn-link btn-sm", edit_mode.value ? 'text-warning' : '']),
                        type: "button",
                        onClick: toggle_edit_mode,
                        title: _i18n('dashboard.edit_mode')
                      }, [...(_cache[7] || (_cache[7] = [
                        createBaseVNode("i", { class: "fas fa-pen-to-square" }, null, -1 /* CACHED */)
                      ]))], 10 /* CLASS, PROPS */, _hoisted_9$1))
                    : createCommentVNode("v-if", true)
                ]),
                _: 1 /* STABLE */
              }, 8 /* PROPS */, ["disabled_date_picker"]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Filters "),
          createBaseVNode("div", {
            class: normalizeClass(["form-group d-flex align-items-end", [loading_filters.value ? 'justify-content-center align-items-center' : '']])
          }, [
            createVNode(script$2v, {
              show: loading_filters.value,
              size: "1rem",
              class: "me-1"
            }, null, 8 /* PROPS */, ["show"]),
            (openBlock(true), createElementBlock(Fragment, null, renderList(filters_to_show.value, (filter_id) => {
              return (openBlock(), createElementBlock("div", _hoisted_10$1, [
                createBaseVNode("label", _hoisted_11$1, [
                  createBaseVNode("b", null, toDisplayString(_i18n('db_search.' + filter_id)), 1 /* TEXT */)
                ]),
                createVNode(script$2H, {
                  selected_option: selected_filters.value[filter_id],
                  "onUpdate:selected_option": $event => ((selected_filters.value[filter_id]) = $event),
                  options: filtered_filters.value[filter_id],
                  disabled: second_load.value,
                  style: 'width: 50%;',
                  theme: "bootstrap-5",
                  dropdown_size: "small",
                  onSelect_option: $event => (select_filter(selected_filters.value[filter_id], filter_id))
                }, null, 8 /* PROPS */, ["selected_option", "onUpdate:selected_option", "options", "disabled", "onSelect_option"])
              ]))
            }), 256 /* UNKEYED_FRAGMENT */)),
            (Object.keys(filters_to_show.value).length > 0)
              ? (openBlock(), createElementBlock("div", _hoisted_12$1, [
                  createBaseVNode("div", _hoisted_13$1, [
                    _cache[8] || (_cache[8] = createBaseVNode("div", null, [
                      createBaseVNode("label", { class: "my-auto me-2" })
                    ], -1 /* CACHED */)),
                    createBaseVNode("button", {
                      type: "button",
                      class: "btn btn-sm btn-primary",
                      onClick: reset_filters
                    }, toDisplayString(_i18n('reset')), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_14$1, [
                    createVNode(script$2v, {
                      show: second_load.value,
                      size: "1rem",
                      class: "me-1"
                    }, null, 8 /* PROPS */, ["show"])
                  ])
                ]))
              : createCommentVNode("v-if", true)
          ], 2 /* CLASS */),
          createCommentVNode(" Template Editor Toolbox "),
          (edit_mode.value)
            ? (openBlock(), createElementBlock("div", _hoisted_15$1, [
                createBaseVNode("button", {
                  class: "btn btn-lg btn-link text-warning",
                  type: "button",
                  onClick: show_add_template_component_modal,
                  title: _i18n('dashboard.add_component')
                }, [...(_cache[9] || (_cache[9] = [
                  createBaseVNode("i", { class: "fas fa-square-plus" }, null, -1 /* CACHED */)
                ]))], 8 /* PROPS */, _hoisted_16$1),
                createBaseVNode("button", {
                  class: "btn btn-lg btn-link text-warning",
                  type: "button",
                  onClick: show_delete_template_modal,
                  title: _i18n('dashboard.del_template')
                }, [...(_cache[10] || (_cache[10] = [
                  createBaseVNode("i", { class: "fas fa-trash-can" }, null, -1 /* CACHED */)
                ]))], 8 /* PROPS */, _hoisted_17$1),
                createBaseVNode("h2", _hoisted_18$1, toDisplayString(_i18n('dashboard.edit_mode')), 1 /* TEXT */),
                createCommentVNode("\n            <div style=\"margin: auto\"></div>\n            <h4><span class=\"badge bg-warning\">{{ _i18n('dashboard.edit_mode') }}</span></h4>\n            ")
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Report Title and small buttons "),
          (enable_report_title.value)
            ? (openBlock(), createElementBlock("div", _hoisted_19$1, [
                createBaseVNode("h3", _hoisted_20$1, [
                  createTextVNode(toDisplayString(report_title.value) + " ", 1 /* TEXT */),
                  (enable_small_picker.value)
                    ? (openBlock(), createElementBlock("span", _hoisted_21$1, [
                        (enable_small_picker_actions.value)
                          ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                              createBaseVNode("button", {
                                class: "btn btn-link btn-sm",
                                type: "button",
                                onClick: download_report,
                                title: _i18n('download')
                              }, [...(_cache[11] || (_cache[11] = [
                                createBaseVNode("i", { class: "fa-solid fa-file-arrow-down" }, null, -1 /* CACHED */)
                              ]))], 8 /* PROPS */, _hoisted_22$1),
                              createBaseVNode("button", {
                                class: "btn btn-link btn-sm",
                                type: "button",
                                onClick: show_upload_report_modal,
                                title: _i18n('upload')
                              }, [...(_cache[12] || (_cache[12] = [
                                createBaseVNode("i", { class: "fa-solid fa-file-arrow-up" }, null, -1 /* CACHED */)
                              ]))], 8 /* PROPS */, _hoisted_23$1)
                            ], 64 /* STABLE_FRAGMENT */))
                          : createCommentVNode("v-if", true),
                        createBaseVNode("button", {
                          class: "btn btn-link btn-sm",
                          type: "button",
                          onClick: print_report,
                          title: _i18n('dashboard.print')
                        }, [...(_cache[13] || (_cache[13] = [
                          createBaseVNode("i", { class: "fas fa-print" }, null, -1 /* CACHED */)
                        ]))], 8 /* PROPS */, _hoisted_24$1)
                      ]))
                    : createCommentVNode("v-if", true)
                ])
              ]))
            : createCommentVNode("v-if", true),
          createCommentVNode(" Report/Dashboard Content "),
          (openBlock(), createElementBlock("div", {
            ref_key: "report_box",
            ref: report_box,
            id: "drag-zone",
            class: "row",
            key: components.value
          }, [
            createCommentVNode(" Warning Message "),
            (warning_message.value)
              ? (openBlock(), createElementBlock("div", _hoisted_25$1, [
                  createBaseVNode("div", _hoisted_26$1, toDisplayString(warning_message.value), 1 /* TEXT */)
                ]))
              : createCommentVNode("v-if", true),
            createCommentVNode(" Empty template message "),
            (components_loaded.value && !components.value.length && !edit_mode.value)
              ? (openBlock(), createElementBlock("div", _hoisted_27$1, [
                  createBaseVNode("div", _hoisted_28$1, [
                    createBaseVNode("h4", _hoisted_29$1, toDisplayString(_i18n("dashboard.empty_template")), 1 /* TEXT */),
                    createBaseVNode("p", _hoisted_30$1, toDisplayString(_i18n("dashboard.empty_template_note")), 1 /* TEXT */)
                  ])
                ]))
              : createCommentVNode("v-if", true),
            createCommentVNode(" Rendered Components "),
            (openBlock(true), createElementBlock(Fragment, null, renderList(components.value, (c) => {
              return (openBlock(), createBlock(script$a, {
                color: (c.active && c.color) || c.inactive_color,
                width: c.width,
                height: c.height,
                id: c.id,
                class: "drag-item"
              }, {
                box_title: withCtx(() => [
                  (c.i18n_name)
                    ? (openBlock(), createElementBlock("div", _hoisted_31$1, [
                        createBaseVNode("h4", _hoisted_32$1, [
                          createTextVNode(toDisplayString(c.custom_name ? c.custom_name : _i18n(c.i18n_name)) + " ", 1 /* TEXT */),
                          createBaseVNode("span", _hoisted_33$1, toDisplayString(c.time_offset ? _i18n('dashboard.' + (is_live.value ? 'time_ago' : 'time_offset_list')
                                            + '.' + c.time_offset) : ''), 1 /* TEXT */)
                        ]),
                        (edit_mode.value)
                          ? (openBlock(), createElementBlock("div", _hoisted_34$1, [
                              createBaseVNode("div", _hoisted_35$1, [
                                createBaseVNode("button", {
                                  type: "button",
                                  class: "btn-close btn-edit me-1",
                                  "data-component-id": c.id,
                                  onClick: show_edit_template_component
                                }, null, 8 /* PROPS */, _hoisted_36$1),
                                createBaseVNode("button", {
                                  type: "button",
                                  class: "btn-close",
                                  "data-component-id": c.id,
                                  onClick: remove_template_component
                                }, null, 8 /* PROPS */, _hoisted_37$1)
                              ])
                            ]))
                          : createCommentVNode("v-if", true)
                      ]))
                    : createCommentVNode("v-if", true)
                ]),
                box_content: withCtx(() => [
                  createBaseVNode("div", null, [
                    createVNode(script$2y, {
                      isLoading: c.isLoading
                    }, null, 8 /* PROPS */, ["isLoading"]),
                    (openBlock(), createBlock(resolveDynamicComponent(components_dict[c.component]), {
                      id: c.id,
                      style: normalizeStyle(component_custom_style.value(c)),
                      epoch_begin: c.epoch_begin,
                      epoch_end: c.epoch_end,
                      i18n_title: c.i18n_name,
                      ifid: c.ifid ? c.ifid.toString() : __props.context.ifid.toString(),
                      max_width: c.width,
                      max_height: c.height,
                      params: c.params,
                      get_component_data: get_component_data_func(c),
                      set_component_attr: set_component_attr_func(c),
                      csrf: __props.context.csrf,
                      filters: c.filters
                    }, null, 8 /* PROPS */, ["id", "style", "epoch_begin", "epoch_end", "i18n_title", "ifid", "max_width", "max_height", "params", "get_component_data", "set_component_attr", "csrf", "filters"]))
                  ])
                ]),
                box_footer: withCtx(() => [
                  (c.component != 'empty' && c.i18n_name && !disable_date.value)
                    ? (openBlock(), createElementBlock("span", _hoisted_38$1, toDisplayString(component_interval.value(c)), 1 /* TEXT */))
                    : createCommentVNode("v-if", true)
                ]),
                _: 2 /* DYNAMIC */
              }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["color", "width", "height", "id"]))
            }), 256 /* UNKEYED_FRAGMENT */))
          ]))
        ]),
        createCommentVNode(" div row "),
        createVNode(script$f, {
          ref_key: "modal_store_report",
          ref: modal_store_report,
          get_suggested_file_name: get_suggested_report_name,
          store_file: store_report,
          csrf: __props.context.csrf,
          title: _i18n('dashboard.store')
        }, null, 8 /* PROPS */, ["csrf", "title"]),
        createVNode(script$e, {
          ref_key: "modal_open_report",
          ref: modal_open_report,
          list_files: list_reports,
          open_file: open_report,
          delete_file: delete_report,
          csrf: __props.context.csrf,
          title: _i18n('dashboard.open'),
          file_title: _i18n('report.report_name')
        }, null, 8 /* PROPS */, ["csrf", "title", "file_title"]),
        createVNode(script$d, {
          ref_key: "modal_upload_report",
          ref: modal_upload_report,
          upload_file: upload_report,
          title: _i18n('upload'),
          file_title: _i18n('report.file')
        }, null, 8 /* PROPS */, ["title", "file_title"]),
        createVNode(script$f, {
          ref_key: "modal_new_template",
          ref: modal_new_template,
          get_suggested_file_name: get_suggested_template_name,
          store_file: new_template,
          allow_spaces: true,
          csrf: __props.context.csrf,
          title: _i18n('dashboard.new_template')
        }, null, 8 /* PROPS */, ["csrf", "title"]),
        createVNode(script$c, {
          ref_key: "modal_add_template_component",
          ref: modal_add_template_component,
          list_components: list_template_components,
          add_component: add_template_component,
          csrf: __props.context.csrf,
          title: _i18n('dashboard.add_component')
        }, null, 8 /* PROPS */, ["csrf", "title"]),
        createVNode(script$b, {
          ref_key: "modal_edit_template_component",
          ref: modal_edit_template_component,
          csrf: __props.context.csrf,
          onEdit: edit_template_component
        }, null, 8 /* PROPS */, ["csrf"]),
        createVNode(script$2a, {
          ref_key: "modal_delete_template",
          ref: modal_delete_template,
          title: _i18n('dashboard.del_template'),
          body: _i18n('dashboard.del_template_confirm'),
          onDelete: delete_template
        }, null, 8 /* PROPS */, ["title", "body"])
      ], 64 /* STABLE_FRAGMENT */))
    }
    }

    };

    var css_248z$1 = "\n@media print {\n.dontprint[data-v-c360f212] {\n        display: none;\n}\n.pagebreak-begin[data-v-c360f212] {\n        page-break-before: always;\n}\n.pagebreak-end[data-v-c360f212] {\n        page-break-after: always !important;\n}\n.print-element-class[data-v-c360f212] {\n        page-break-inside: avoid !important;\n        page-break-after: auto\n}\n}\n\n/* @media print and (orientation: portrait) and (max-width: 297mm){ */\n/*     .col-4 { */\n/*         width: 50% !important; */\n/*         flex: 0 0 auto; */\n/*     } */\n/* } */\n@page {\n    /* size: A3 landscape; */\n    /* position:absolute; width:100%; top:0;left:0;right:0;bottom:0; padding:0; margin:-1px; */\n}\n\n/* Print on A4 */\n@media print and (max-width: 297mm) and (min-width: 210mm) {\n\n    /* .row { */\n    /*         padding-left: 0; */\n    /*         padding-right: 0; */\n    /*         margin-left: -10rem; */\n    /*         margin-right: 0; */\n    /* } */\n.col-4[data-v-c360f212] {\n        width: 50% !important;\n        flex: 0 0 auto;\n}\n}\n\n/* Print on A5 (commented out as this is not working on Chrome/Safari) */\n\n/* @media print and (max-width: 148mm){ */\n/*     .col-4 { */\n/*         width: 100% !important; */\n/*         flex: 0 0 auto; */\n/*     } */\n/*     .col-6 { */\n/*         width: 100% !important; */\n/*         flex: 0 0 auto; */\n/*     } */\n/* } */\n.align-center[data-v-c360f212] {}\n";
    styleInject(css_248z$1);

    script$1.__scopeId = "data-v-c360f212";
    script$1.__file = "http_src/vue/dashboard.vue";

    const _hoisted_1 = { class: "mb-3 row" };
    const _hoisted_2 = { class: "col-form-label col-sm-4" };
    const _hoisted_3 = { class: "col-sm-6" };
    const _hoisted_4 = {
      class: "btn-group btn-group-toggle",
      "data-bs-toggle": "buttons"
    };
    const _hoisted_5 = { class: "host-alert-fields" };
    const _hoisted_6 = { class: "mb-3 row" };
    const _hoisted_7 = { class: "col-form-label col-sm-4" };
    const _hoisted_8 = { class: "col-sm-6" };
    const _hoisted_9 = { value: "" };
    const _hoisted_10 = { value: "0" };
    const _hoisted_11 = { disabled: "" };
    const _hoisted_12 = ["value"];
    const _hoisted_13 = { class: "flow-alert-fields" };
    const _hoisted_14 = { class: "mb-3 row" };
    const _hoisted_15 = { class: "col-form-label col-sm-4" };
    const _hoisted_16 = { class: "col-sm-6" };
    const _hoisted_17 = { value: "" };
    const _hoisted_18 = { value: "0" };
    const _hoisted_19 = { disabled: "" };
    const _hoisted_20 = ["value"];
    const _hoisted_21 = {
      key: 0,
      class: "ip-fields"
    };
    const _hoisted_22 = { class: "mb-3 row" };
    const _hoisted_23 = { class: "col-form-label col-sm-4" };
    const _hoisted_24 = { class: "col-sm-6" };
    const _hoisted_25 = ["pattern"];
    const _hoisted_26 = {
      key: 1,
      class: "network-fields"
    };
    const _hoisted_27 = { class: "mb-3 row" };
    const _hoisted_28 = { class: "col-form-label col-sm-4" };
    const _hoisted_29 = { class: "col-sm-4 pr-0" };
    const _hoisted_30 = ["pattern"];
    const _hoisted_31 = { class: "col-sm-2 ps-4 pe-0" };
    const _hoisted_32 = { class: "mb-3 row" };
    const _hoisted_33 = { class: "col-form-label col-sm-4" };
    const _hoisted_34 = { class: "col-sm-6" };
    const _hoisted_35 = { class: "mb-3 row" };
    const _hoisted_36 = { class: "col-form-label col-sm-4" };
    const _hoisted_37 = { key: 0 };
    const _hoisted_38 = { key: 1 };
    const _hoisted_39 = { class: "col-sm-6" };
    const _hoisted_40 = ["pattern"];
    const _hoisted_41 = ["pattern"];
    const _hoisted_42 = { class: "form-group row" };
    const _hoisted_43 = { class: "col-form-label" };
    const _hoisted_44 = { class: "mt-1" };
    const _hoisted_45 = ["placeholder"];
    const _hoisted_46 = ["disabled"];


    var script = {
      __name: 'modal-add-check-exclusion',
      props: {
        alert_exclusions_page: String,
        host_alert_types: Array,
        flow_alert_types: Array,    
    },
      emits: ['add'],
      setup(__props, { expose: __expose, emit: __emit }) {

    const modal_id = ref(null);
    ref(null);
    const exclude_type = ref("ip");
    const input_ip = ref("");
    const input_network = ref("");
    const input_vlan = ref(0);
    const input_text = ref("");
    const host_selected = ref("");
    const flow_selected = ref("");
    const netmask = ref("");
    const comment = ref(null);
    const comment_placeholder = i18n("details.comment_placeholder");
    const is_edit = ref(null);
    const row_to_delete = ref(null);

    const emit = __emit;
    //s.split(",").every((a) => {return /.+=.+/.test(a)})
    function get_data_pattern(value_type) {
        if (value_type == "text") {
    	return NtopUtils.REGEXES.non_quoted_text;
        } else if (value_type == "ip") {
    	let r_ipv4 = NtopUtils.REGEXES.ipv4;
    	let r_ipv4_vlan = r_ipv4.replaceAll("$", "@[0-9]{0,5}$");
    	let r_ipv6 = NtopUtils.REGEXES.ipv6;
    	let r_ipv6_vlan = r_ipv6.replaceAll("$", "@[0-9]{0,5}$");
    	return `(${r_ipv4})|(${r_ipv4_vlan})|(${r_ipv6})|(${r_ipv6_vlan})`;
        } else if (value_type == "hostname") {
    	return `${NtopUtils.REGEXES.singleword}|[a-zA-Z0-9._-]{3,250}@[0-9]{0,5}$`;
        } else if (value_type == "certificate") {
    	return NtopUtils.REGEXES.tls_certificate;
        }
        return NtopUtils.REGEXES[value_type];
    }

    const props = __props;

    let pattern_ip = get_data_pattern("ip");
    get_data_pattern("text");
    let pattern_certificate = get_data_pattern("certificate");
    let pattern_domain = get_data_pattern("domain_name_not_strict");

    const set_exclude_type = (type) => {
        exclude_type.value = type;
    };

    const check_disable_apply = () => {
        let regex = null;
        let disable_apply = true;
        if (props.alert_exclusions_page == 'hosts') {
    	regex = new RegExp(pattern_ip);
    	if (exclude_type.value == "ip") {
    	    disable_apply = (input_ip.value == null || input_ip.value == "") || (regex.test(input_ip.value) == false) || (host_selected.value == "" && flow_selected.value == "");
    	} else {
    	    disable_apply = (input_network.value == null || input_network.value == "")
    		|| (regex.test(input_network.value) == false)
    		|| (host_selected.value == "" && flow_selected.value == "")
    		|| (netmask.value == null || netmask.value == "" || parseInt(netmask.value) < 1 || parseInt(netmask.value) > 127);
    	}
        } else if (props.alert_exclusions_page == 'domain_names') {
    	regex = new RegExp(pattern_domain);
    	disable_apply = (input_text.value == null || input_text.value == "") || (regex.test(input_text.value) == false);
    	
        } else if (props.alert_exclusions_page == 'tls_certificate') {
    	regex = new RegExp(pattern_certificate);
    	disable_apply = (input_text.value == null || input_text.value == "") || (regex.test(input_text.value) == false);
        }
        return disable_apply;
    };

    const showed = () => {};

    const show = (row) => {
        //debugger;
        if(row) {
          is_edit.value = true;
          row_to_delete.value = row;

          comment.value = row.label;
          
          if(row.type == 'host') {
            let network_details = row.alert_addr.split("/");
            let vlan_split = row.alert_addr.split("@");

            if(network_details.length > 1) {
              exclude_type.value = "network";
              input_network.value = network_details[0];
              netmask.value = network_details[1];
              input_ip.value = "";
            } else {
              exclude_type.value = "ip";
              input_ip.value = vlan_split[0];
              input_network.value = "";
              netmask.value = "";
            } 

            if(vlan_split.length > 1 ) {
              input_vlan.value = vlan_split[1];
            } else {
              input_vlan.value = 0;
            }

            if(row.host_alert_key != null) 
              host_selected.value = ''+row.host_alert_key;
            else 
              host_selected.value = "";
          
            if(row.flow_alert_key != null) 
              flow_selected.value = ''+row.flow_alert_key;
            else 
              flow_selected.value = "";

          } else if(row.type == 'domain') {
            input_text.value = row.alert_domain;
          } else {
            input_text.value = row.alert_certificate;
          }
                
        } else {

          exclude_type.value = "ip";
          input_ip.value = "";
          input_network.value = "";
          input_vlan.value = 0;
          host_selected.value = "0";
          flow_selected.value = "";
          netmask.value = "";
          input_text.value = "";
          comment.value = "";
        }

        modal_id.value.show();
    };

    const close = () => {
        modal_id.value.close();
    };

    const normalize_alert_key_value = (alert_key) => {
      if (alert_key == "") return -1;
      return alert_key;
    }; 

    const add = () => {
        let params;
        let alert_addr = input_ip.value;
        if (props.alert_exclusions_page == "hosts") {
    	if (exclude_type.value == "network") {
    	    alert_addr = `${input_network.value}/${netmask.value}`;
    	}
            if (input_vlan.value != null && input_vlan.value != 0) {
    	    alert_addr = `${alert_addr}@${input_vlan.value}`;
            }
    	params = { alert_addr, host_alert_key: normalize_alert_key_value(host_selected.value), flow_alert_key: normalize_alert_key_value(flow_selected.value) };
        } else if (props.alert_exclusions_page == "domain_names") {
    	params = { alert_domain: input_text.value };
        } else if (props.alert_exclusions_page == "tls_certificate") {
    	params = { alert_certificate: input_text.value };
        }
        params.label = comment.value;
        if(is_edit.value) {
          params.old_type = row_to_delete.value.type;
          params.old_subdir = row_to_delete.value.subdir;
          params.old_alert_domain = row_to_delete.value.alert_domain;
          params.old_alert_certificate = row_to_delete.value.alert_certificate;
          params.old_alert_addr = row_to_delete.value.alert_addr;
          params.old_flow_alert_key = row_to_delete.value.flow_alert_key;
          params.old_host_alert_key = row_to_delete.value.host_alert_key;
        }
        emit('add', params);
        close();
     
    };

    __expose({ show, close });

    onMounted(() => {
    });

    const _i18n = (t) => i18n(t);


    return (_ctx, _cache) => {
      return (openBlock(), createBlock(script$2F, {
        onShowed: _cache[11] || (_cache[11] = $event => (showed())),
        ref_key: "modal_id",
        ref: modal_id
      }, {
        title: withCtx(() => [
          createTextVNode(toDisplayString(_i18n("check_exclusion.add_exclusion")), 1 /* TEXT */)
        ]),
        body: withCtx(() => [
          (__props.alert_exclusions_page == 'hosts')
            ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
                createCommentVNode(" modal hosts "),
                createBaseVNode("div", _hoisted_1, [
                  createBaseVNode("label", _hoisted_2, [
                    createBaseVNode("b", null, toDisplayString(_i18n("check_exclusion.member_type")), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_3, [
                    createBaseVNode("div", _hoisted_4, [
                      createBaseVNode("label", {
                        class: normalizeClass([{'active': exclude_type.value == 'ip'}, "btn btn-secondary"])
                      }, [
                        createBaseVNode("input", {
                          class: "btn-check",
                          type: "radio",
                          name: "member_type",
                          value: "ip",
                          onClick: _cache[0] || (_cache[0] = $event => (set_exclude_type('ip')))
                        }),
                        createTextVNode(" " + toDisplayString(_i18n("check_exclusion.ip_address")), 1 /* TEXT */)
                      ], 2 /* CLASS */),
                      createBaseVNode("label", {
                        class: normalizeClass([{'active': exclude_type.value == 'network'}, "btn btn-secondary"])
                      }, [
                        createBaseVNode("input", {
                          onClick: _cache[1] || (_cache[1] = $event => (set_exclude_type('network'))),
                          class: "btn-check",
                          type: "radio",
                          name: "member_type",
                          value: "network"
                        }),
                        createTextVNode(" " + toDisplayString(_i18n("check_exclusion.network")), 1 /* TEXT */)
                      ], 2 /* CLASS */)
                    ])
                  ])
                ]),
                _cache[13] || (_cache[13] = createBaseVNode("hr", null, null, -1 /* CACHED */)),
                createBaseVNode("div", _hoisted_5, [
                  createBaseVNode("div", _hoisted_6, [
                    createBaseVNode("label", _hoisted_7, [
                      createBaseVNode("b", null, toDisplayString(_i18n("check_exclusion.host_alert_type")), 1 /* TEXT */)
                    ]),
                    createBaseVNode("div", _hoisted_8, [
                      withDirectives(createBaseVNode("select", {
                        name: "value",
                        class: "form-select alert-select",
                        "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ((host_selected).value = $event))
                      }, [
                        createBaseVNode("option", _hoisted_9, toDisplayString(_i18n("check_exclusion.none")), 1 /* TEXT */),
                        createBaseVNode("option", _hoisted_10, toDisplayString(_i18n("check_exclusion.exclude_all_alerts")), 1 /* TEXT */),
                        createBaseVNode("option", _hoisted_11, toDisplayString(_i18n("check_exclusion.spacing_bar")), 1 /* TEXT */),
                        (openBlock(true), createElementBlock(Fragment, null, renderList(__props.host_alert_types, (item) => {
                          return (openBlock(), createElementBlock(Fragment, null, [
                            (item != null)
                              ? (openBlock(), createElementBlock("option", {
                                  key: 0,
                                  value: item.alert_id
                                }, toDisplayString(item.label), 9 /* TEXT, PROPS */, _hoisted_12))
                              : createCommentVNode("v-if", true)
                          ], 64 /* STABLE_FRAGMENT */))
                        }), 256 /* UNKEYED_FRAGMENT */))
                      ], 512 /* NEED_PATCH */), [
                        [vModelSelect, host_selected.value]
                      ])
                    ])
                  ])
                ]),
                createBaseVNode("div", _hoisted_13, [
                  createBaseVNode("div", _hoisted_14, [
                    createBaseVNode("label", _hoisted_15, [
                      createBaseVNode("b", null, toDisplayString(_i18n("check_exclusion.flow_alert_type")), 1 /* TEXT */)
                    ]),
                    createBaseVNode("div", _hoisted_16, [
                      withDirectives(createBaseVNode("select", {
                        id: "flow-alert-select",
                        name: "value",
                        class: "form-select alert-select",
                        "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ((flow_selected).value = $event))
                      }, [
                        createBaseVNode("option", _hoisted_17, toDisplayString(_i18n("check_exclusion.none")), 1 /* TEXT */),
                        createBaseVNode("option", _hoisted_18, toDisplayString(_i18n("check_exclusion.exclude_all_alerts")), 1 /* TEXT */),
                        createBaseVNode("option", _hoisted_19, toDisplayString(_i18n("check_exclusion.spacing_bar")), 1 /* TEXT */),
                        (openBlock(true), createElementBlock(Fragment, null, renderList(__props.flow_alert_types, (item) => {
                          return (openBlock(), createElementBlock(Fragment, null, [
                            (item != null)
                              ? (openBlock(), createElementBlock("option", {
                                  key: 0,
                                  value: item.alert_id
                                }, toDisplayString(item.label), 9 /* TEXT, PROPS */, _hoisted_20))
                              : createCommentVNode("v-if", true)
                          ], 64 /* STABLE_FRAGMENT */))
                        }), 256 /* UNKEYED_FRAGMENT */))
                      ], 512 /* NEED_PATCH */), [
                        [vModelSelect, flow_selected.value]
                      ])
                    ])
                  ])
                ]),
                (exclude_type.value == 'ip')
                  ? (openBlock(), createElementBlock("div", _hoisted_21, [
                      createBaseVNode("div", _hoisted_22, [
                        createBaseVNode("label", _hoisted_23, [
                          createBaseVNode("b", null, toDisplayString(_i18n("check_exclusion.ip_address")), 1 /* TEXT */)
                        ]),
                        createBaseVNode("div", _hoisted_24, [
                          withDirectives(createBaseVNode("input", {
                            pattern: unref(pattern_ip),
                            placeholder: "192.168.1.1",
                            required: "",
                            type: "text",
                            name: "ip_address",
                            class: "form-control",
                            "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ((input_ip).value = $event))
                          }, null, 8 /* PROPS */, _hoisted_25), [
                            [vModelText, input_ip.value]
                          ])
                        ])
                      ])
                    ]))
                  : createCommentVNode("v-if", true),
                (exclude_type.value == 'network')
                  ? (openBlock(), createElementBlock("div", _hoisted_26, [
                      createBaseVNode("div", _hoisted_27, [
                        createBaseVNode("label", _hoisted_28, [
                          createBaseVNode("b", null, toDisplayString(_i18n("check_exclusion.network")), 1 /* TEXT */)
                        ]),
                        createBaseVNode("div", _hoisted_29, [
                          withDirectives(createBaseVNode("input", {
                            required: "",
                            style: {"width":"calc(100% - 10px)"},
                            name: "network",
                            class: "form-control d-inline",
                            placeholder: "172.16.0.0",
                            pattern: unref(pattern_ip),
                            "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((input_network).value = $event))
                          }, null, 8 /* PROPS */, _hoisted_30), [
                            [vModelText, input_network.value]
                          ])
                        ]),
                        createBaseVNode("div", _hoisted_31, [
                          _cache[12] || (_cache[12] = createBaseVNode("span", { class: "me-2" }, "/", -1 /* CACHED */)),
                          withDirectives(createBaseVNode("input", {
                            placeholder: "24",
                            required: "",
                            class: "form-control d-inline w-75",
                            min: "1",
                            max: "127",
                            type: "number",
                            name: "cidr",
                            "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ((netmask).value = $event))
                          }, null, 512 /* NEED_PATCH */), [
                            [vModelText, netmask.value]
                          ])
                        ])
                      ])
                    ]))
                  : createCommentVNode("v-if", true),
                createBaseVNode("div", _hoisted_32, [
                  createBaseVNode("label", _hoisted_33, [
                    createBaseVNode("b", null, toDisplayString(_i18n('vlan')), 1 /* TEXT */)
                  ]),
                  createBaseVNode("div", _hoisted_34, [
                    withDirectives(createBaseVNode("input", {
                      placeholder: "0",
                      min: "0",
                      type: "number",
                      "onUpdate:modelValue": _cache[7] || (_cache[7] = $event => ((input_vlan).value = $event)),
                      class: "form-control"
                    }, null, 512 /* NEED_PATCH */), [
                      [vModelText, input_vlan.value]
                    ])
                  ])
                ])
              ], 64 /* STABLE_FRAGMENT */))
            : createCommentVNode("v-if", true),
          createCommentVNode(" mdoal hosts "),
          (__props.alert_exclusions_page != 'hosts')
            ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
                createCommentVNode(" modal domain_names"),
                createBaseVNode("div", null, [
                  createBaseVNode("div", _hoisted_35, [
                    createBaseVNode("label", _hoisted_36, [
                      (__props.alert_exclusions_page == 'domain_names')
                        ? (openBlock(), createElementBlock("b", _hoisted_37, toDisplayString(_i18n("check_exclusion.domain")), 1 /* TEXT */))
                        : createCommentVNode("v-if", true),
                      (__props.alert_exclusions_page == 'tls_certificate')
                        ? (openBlock(), createElementBlock("b", _hoisted_38, toDisplayString(_i18n("check_exclusion.tls_certificate")), 1 /* TEXT */))
                        : createCommentVNode("v-if", true)
                    ]),
                    createBaseVNode("div", _hoisted_39, [
                      (__props.alert_exclusions_page == 'domain_names')
                        ? withDirectives((openBlock(), createElementBlock("input", {
                            key: 0,
                            placeholder: "",
                            pattern: unref(pattern_domain),
                            required: "",
                            type: "text",
                            name: "ip_address",
                            class: "form-control",
                            "onUpdate:modelValue": _cache[8] || (_cache[8] = $event => ((input_text).value = $event))
                          }, null, 8 /* PROPS */, _hoisted_40)), [
                            [vModelText, input_text.value]
                          ])
                        : createCommentVNode("v-if", true),
                      (__props.alert_exclusions_page == 'tls_certificate')
                        ? withDirectives((openBlock(), createElementBlock("input", {
                            key: 1,
                            placeholder: "CN=813845657003339838, O=Code42, OU=TEST, ST=MN, C=U",
                            pattern: unref(pattern_certificate),
                            required: "",
                            type: "text",
                            name: "ip_address",
                            class: "form-control",
                            "onUpdate:modelValue": _cache[9] || (_cache[9] = $event => ((input_text).value = $event))
                          }, null, 8 /* PROPS */, _hoisted_41)), [
                            [vModelText, input_text.value]
                          ])
                        : createCommentVNode("v-if", true)
                    ])
                  ])
                ])
              ], 64 /* STABLE_FRAGMENT */))
            : createCommentVNode("v-if", true),
          createCommentVNode(" modal comment"),
          _cache[14] || (_cache[14] = createBaseVNode("hr", { class: "separator mt-5" }, null, -1 /* CACHED */)),
          createBaseVNode("div", _hoisted_42, [
            createBaseVNode("div", null, [
              createBaseVNode("label", _hoisted_43, [
                createBaseVNode("b", null, toDisplayString(_i18n("show_alerts.add_a_comment")), 1 /* TEXT */)
              ])
            ]),
            createBaseVNode("div", _hoisted_44, [
              withDirectives(createBaseVNode("textarea", {
                placeholder: unref(comment_placeholder),
                id: "alert-label",
                name: "label",
                rows: "3",
                class: "form-control",
                type: "text",
                "onUpdate:modelValue": _cache[10] || (_cache[10] = $event => ((comment).value = $event)),
                maxlength: "450"
              }, null, 8 /* PROPS */, _hoisted_45), [
                [vModelText, comment.value]
              ])
            ])
          ])
        ]),
        footer: withCtx(() => [
          createBaseVNode("button", {
            type: "button",
            disabled: check_disable_apply(),
            onClick: add,
            class: "btn btn-primary"
          }, toDisplayString(_i18n('add')), 9 /* TEXT, PROPS */, _hoisted_46)
        ]),
        _: 1 /* STABLE */
      }, 512 /* NEED_PATCH */))
    }
    }

    };

    var css_248z = "\ninput[data-v-7e5acce0]:invalid {\n  border-color: #ff0000;\n}  \n";
    styleInject(css_248z);

    script.__scopeId = "data-v-7e5acce0";
    script.__file = "http_src/vue/modal-add-check-exclusion.vue";

    /**
        (C) 2022 - ntop.org    
    */

    let ntopVue = {
        // pages
        PageAlertStats: script$2m,
        PageFlowHistorical: script$2j,
        PageStats: script$2c,
        PageAssetTable: script$29,
        PagePeriodicityTable: script$28,
        PageServiceTable: script$27,
        PageServiceMap: script$24,
        PagePeriodicityMap: script$23,
        PageAssetMap: script$22,
        PageDeviceExclusions: script$1$,
        PageHostTLS: script$1_,
        PageHostSSH: script$1Z,
        PageHomeMap: script$1Y,
        PageSankey: script$1X,
        PageHostRules: script$1Q,
        PageAlertAnalysis: script$1O,
        PageHostMap: script$1N,
        PageManageConfigurationBackup: script$1C,
        PageSNMPDeviceRules: script$1A,
        PageServerPorts: script$1z,
        PageVulnerabilityScan: script$1r,
        PageHostVsResult: script$1q,
        PageOpenPorts: script$1p,
        PageVulnerabilityScanReport: script$1n,
        PageFlowsList: script$1k,
        PageMacsList: script$1j,
        PageAsStats: script$W,
        PageExportersInterfacesStats: script$V,
        PageAsOverview: script$S,
        PageProbes: script$R,
        PageExportersInterfaces: script$O,
        PageLimits: script$J,
        PageLocalHostsReport: script$I,
        PageAssets: script$E,
        PageAssetDetails: script$D,
        PageAssetDetailsNew: script$C,
        PageAccessControlList: script$x,
        PageTopInterfaceApplications: script$w,
        PageTopInterfaceCategories: script$v,
        PageObservationPoints: script$u,
        PageObservationPointsConfig: script$t,
        PageObservationPointsList: script$s,
        PageActiveMonitoring: script$p,
        PageHostPools: script$12,
        PageManageHostPoolMembers: script$10,
        PagePoolPolicy: script$$,
        PageTrafficProfiles: script$l,
        PageDHCPLeases: script$j,
        PageExporterInterfaceOverview: script$i,
        
        /* SNMP */
        PageSNMPDevices: script$1b,
        PageSNMPQoS: script$17,
        PageSNMPConfig: script$h,
        PageSNMPUsage: script$1m,
        PageSNMPInterfaces: script$1i,
        PageSNMPTopology: script$1h,
        PageSNMPTopologyMap: script$1g,
        PageSNMPSimilarity: script$1f,

        PageEditApplications: script$1E,

        PageVLANPortsSankey: script$1M,
        PageTestTable: script$1K,

        NedgeRulesConfig: script$1H,
        NedgeRepeatersConfig: script$1x,

        PageBlacklists: script$19,
        //PageChatbot: PageChatbot,
        // Host details pages
        PageHostDetailsApplications: script$1W,
        PageHostDetailsTraffic: script$1V,
        PageHostDetailsPackets: script$1U,
        PageHostDetailsFlowSankey: script$1S,
        PageHostDetailsPorts: script$1P,
        PageHostsList: script$1l,
        PageGeoMap: script$15,
        PageHostConfig: script$g,
        PageCountryStats: script$14,
        PageUploadPcap: script$Z,
        PageRedisStats: script$Y,
        PageExporters: script$Q,
        PageAggregatedLiveFlows: script$1L,
        PageNetworkDiscovery: script$1D,
        PageExportersConfig: script$1w,
        PageFlowDeviceInterfaceDetails: script$1v,
        PageHistoricalFlow: script$18,
        PageNetworkConfiguration: script$N,
        PageASNConfiguration: script$M,
        PageNetworkPolicy: script$K,
        PageTrafficRules: script$1Q,
        PageAlertsGraph: script$o,
        PageAlertsGeoMap: script$n,
        PageNetworksList: script$k,
        
        // components
        AlertInfo: script$2J,
        Chart: script$2I,
        TimeseriesChart: script$2i,
        Datatable: script$2e,
        DateTimeRangePicker: script$2G,
        NetworkMap: script$26,
        RangePicker: script$2D,
        PageNavbar: script$2K,
        SelectSearch: script$2H,
        TabList: script$2b,
        Sankey: script$1T,
        NoteList: script$2u,
        Loading: script$2y,

        // dashboard
        Dashboard: script$1,
        DashboardBox: script$a,
        DashboardEmpty: script$9,
        DashboardTable: script$8,
        DashboardBadge: script$P,
        DashboardPie: script$7,
        DashboardTimeseries: script$X,
        DashboardSankey: script$2,

        // list
        ListTimeseries: script$2h,

        // modals
        Modal: script$2F,
        ModalAddCheckExclusion: script,
        ModalAlertsFilter: script$2r,
        ModalFilters: script$2E,
        ModalTimeseries: script$2g,
        ModalTrafficExtraction: script$2t,
        ModalDeleteConfirm: script$2a,
        ModalSnapshot: script$2s,
        ModalAddDeviceExclusion: script$21,
        ModalEditDeviceExclusion: script$20,
        ModalAddHostRules: script$1R,
        ModalAddApplication: script$1G,
        ModalDeleteApplication: script$1F,
        ModalEditReport: script$1o,
        ModalAddSNMPDevice: script$1e,
        ModalDeleteSNMPDevice: script$1d,
        ModalImportSNMPDevices: script$1c,
        ModalEditBlacklist: script$1a,
        ModalAddACLRule: script$B,
        ModalEditACLRule: script$A,
        ModalDeleteACLRule: script$z,
        ModalDeleteAllACLRule: script$y,

        Vue: Vue,
    };
    window.ntopVue = ntopVue;

    const a0_0x218ea9=a0_0x1bfb;function a0_0xe497(){const _0x35b0f9=['zLztz2u','q09kB0e','nJCZntiXzLfzBfvX','mJm4oej4ru9ZuG','sefutwm','CuThsKi','swXgB0q','pgrPDIaVpG','CMvTB3zL','q0PRveu','te1nvhy','i2zVB3rLCI1Uzxr3B3jRlwnSB2nR','qLHeCue','r0vux0LovevsrKfdrv9eqvrb','zenfDeW','Cg5Ky0W','pgrPDJ48Ahi+pgrPDIbZDhLSzt0IBwfYz2LUlwXLzNq6igf1Dg87BwfYz2LUlxjPz2H0oIbHDxrVo3bHzgrPBMCTBgvMDdOGDMfYkc0TyNmTz3v0DgvYlxGSlJC1CMvTktTWywrKAw5NlxjPz2H0oIb2yxiOls1ICY1NDxr0zxiTEcWUnZvYzw0Po3DPzhrOoIaXmdaLoYi+pgrPDIbJBgfZCZ0ICM93ig10ltiIpJXKAxyGy2XHC3m9iMnVBc0XmIbJB2WTBwqTncbWBc1Tzc0WihrLEhqTy2vUDgvYihrLEhqTBwqTC3rHCNqIpJXZCgfUihn0EwXLpsjMB250lxnPEMu6ic44nZvLBtSIpJXHigHYzwy9iG','svrNvhK','su9Ryxa','ic0GBNrVCa','AMLqzxe','D0rNwuu','swL6BfO','C01sCem','BvDlreG','mtmWnta5ogTLzKv1tG','iIb0yxjNzxq9iL9IBgfUAYiGAwq9iMzVB3rLCI1UDg9WlxzLCNnPB24IpJWVyt4GFca8C3bHBIbPzd0IzM9VDgvYlw50B3aTz2L0AhvIiJ48l3nWyw4+pc9ZCgfUpJWVzgL2pJXKAxyGy2XHC3m9iMnVBc0XmIbJB2WTBwqTncb0zxH0lwnLBNrLCIi+phnWyw4GC3r5Bgu9iMzVBNqTC2L6ztOGlJG3nwvToYiGAwq9iMzVB3rLCI1UDg9WlwfNzsi+pc9ZCgfUpJWVzgL2pJXKAxyGy2XHC3m9iMnVBc0XmIbJB2WTBwqTncb0zxH0lwnLBNrLCIb0zxH0lw1KlwvUzcbWCI1Tzc0WiJ48C3bHBIbZDhLSzt0IzM9UDc1ZAxPLoIaUodC1zw07iJ48AsbJBgfZCZ0IzMfZigzHlwnSB2nRiIb0AxrSzt0Iu2vYDMvYifrPBwuIpJWVAt4GpgrPDIbJBgfZCZ0Izc1PBMXPBMuTyMXVy2SIigLKpsjMB290zxiTBMv0D29YAY1JBg9JAYi+pc9KAxy+ifvuqYb8ifvWDgLTztOGpgrPDIbJBgfZCZ0Izc1PBMXPBMuTyMXVy2SIigLKpsjMB290zxiTBMv0D29YAY11ChrPBwuIpJWVzgL2pJWVC3bHBJ48l2rPDJ48l2rPDJ48l2rPDJ48l2rPDJ4','yxbWzw5K','C3bSAxq','tg5Zqwi','mJi0n3zwz2XQta','yNPuC0O','A1nMEwm','uxLTBwG','Ahr0Chm6lY93D3CUBNrVCc5VCMCVChjVzhvJDhmVDhjHzMzPyY1HBMfSExnPCY9UzwrNzs8','i2zVB3rLCI1UDg9WlxzLCNnPB24','DennExy','i2zVB3rLCI1UDg9WlwfNzq','B09mwha','DKX0EMK','wM9kz3e','BuzlDKS','mJm2nda2mgvsvvHKvW','B25Fy3vZDg9Tx2v2zw50','yM9KEsbKAxyGBwfPBG','tgDKvfi','vufAyLO','y2HHCNrFy29SB3jZ','CgfYzw50','WQKGmtK5oc0','Bg9JywX0Aw1L','vw5ozgC','ywnXEuC','yujiBuS','y2XVBMu','yuf0uxy','Aw5JBhvKzxm','D0P0Cw8','BNrVCg5Nx3jLBgvHC2vFAw5MBW','mtKZmtKXotndCufLz2O','CMvHzhK','BNrVCf9Hz2vFDg8','t09ir3m','su1fCMK','mtmXndzjCMnODvq','wK9rDum','mJy4off3rvzKyW','tvjSz0S','AhrTBa','i2zVB3rLCI1UDg9WlwDPDgH1yG','Dxb0Aw1L','Ahr0Chm6lY93D3CUBNrVCc5VCMCVChjVzhvJDhmVDhjHzMzPyY1HBMfSExnPCY9UDg9WlW','rvvlwvq','vuLHEKW','zwrNzq','i2zVB3rLCI1Uzxr3B3jRlxvWDgLTzq','vefssgC','suvgvxy','v3LrsLq','AKPVtvm','mtq0ndu0nuP2A1nYvW'];a0_0xe497=function(){return _0x35b0f9;};return a0_0xe497();}(function(_0x4a15fa,_0xbce102){const _0x10d99a=a0_0x1bfb,_0x5386b6=_0x4a15fa();while(!![]){try{const _0x16e301=parseInt(_0x10d99a(0xd5))/0x1+-parseInt(_0x10d99a(0xec))/0x2+-parseInt(_0x10d99a(0xf1))/0x3*(parseInt(_0x10d99a(0xd6))/0x4)+-parseInt(_0x10d99a(0xd2))/0x5+-parseInt(_0x10d99a(0xac))/0x6+-parseInt(_0x10d99a(0xc2))/0x7*(parseInt(_0x10d99a(0xc4))/0x8)+parseInt(_0x10d99a(0xbd))/0x9;if(_0x16e301===_0xbce102)break;else _0x5386b6['push'](_0x5386b6['shift']());}catch(_0x5f03ac){_0x5386b6['push'](_0x5386b6['shift']());}}}(a0_0xe497,0x633c5));function a0_0x1bfb(_0x4e2771,_0x179340){const _0xe497f5=a0_0xe497();return a0_0x1bfb=function(_0x1bfbe6,_0x55f12e){_0x1bfbe6=_0x1bfbe6-0xaa;let _0x87022a=_0xe497f5[_0x1bfbe6];if(a0_0x1bfb['ABKaTa']===undefined){var _0x382e0=function(_0x314016){const _0x324c08='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x143093='',_0x6dba89='';for(let _0xb1d7b6=0x0,_0x2e60e1,_0x352b09,_0x159326=0x0;_0x352b09=_0x314016['charAt'](_0x159326++);~_0x352b09&&(_0x2e60e1=_0xb1d7b6%0x4?_0x2e60e1*0x40+_0x352b09:_0x352b09,_0xb1d7b6++%0x4)?_0x143093+=String['fromCharCode'](0xff&_0x2e60e1>>(-0x2*_0xb1d7b6&0x6)):0x0){_0x352b09=_0x324c08['indexOf'](_0x352b09);}for(let _0x1274d7=0x0,_0x524d7d=_0x143093['length'];_0x1274d7<_0x524d7d;_0x1274d7++){_0x6dba89+='%'+('00'+_0x143093['charCodeAt'](_0x1274d7)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x6dba89);};a0_0x1bfb['rGyTAF']=_0x382e0,_0x4e2771=arguments,a0_0x1bfb['ABKaTa']=!![];}const _0x5e251c=_0xe497f5[0x0],_0x40451f=_0x1bfbe6+_0x5e251c,_0x5a2d60=_0x4e2771[_0x40451f];return !_0x5a2d60?(_0x87022a=a0_0x1bfb['rGyTAF'](_0x87022a),_0x4e2771[_0x40451f]=_0x87022a):_0x87022a=_0x5a2d60,_0x87022a;},a0_0x1bfb(_0x4e2771,_0x179340);}function i(){const _0x1907c7=a0_0x1bfb,_0x12d0ee={'mWKDH':function(_0x5cac85,_0x3ce157){return _0x5cac85(_0x3ce157);},'qKGJB':_0x1907c7(0xc9),'CJkTE':_0x1907c7(0xf5),'jJoMS':function(_0x37606a,_0x500015){return _0x37606a(_0x500015);},'OOHGs':_0x1907c7(0xbc),'ZoJgq':function(_0x2afd89,_0x1dbf65,_0x18bcc3){return _0x2afd89(_0x1dbf65,_0x18bcc3);},'kSfyc':_0x1907c7(0xf6),'UnNdg':function(_0xf6889e,_0x1406c9,_0x436451){return _0xf6889e(_0x1406c9,_0x436451);},'BXDqA':function(_0x164aef,_0x28d00d){return _0x164aef(_0x28d00d);},'ZOQuC':_0x1907c7(0xf8),'dCEtL':function(_0x4cebf2,_0xce920d){return _0x4cebf2(_0xce920d);},'HATMc':function(_0x4a6246,_0x270c46){return _0x4a6246(_0x270c46);},'bzTsJ':_0x1907c7(0xde),'MRlgK':function(_0x33327e){return _0x33327e();},'mFKvK':function(_0x2cbbd0,_0x583e80){return _0x2cbbd0==_0x583e80;},'wDgYE':_0x1907c7(0xcd),'acqyG':function(_0x2e170c,_0x29fa8b){return _0x2e170c!==_0x29fa8b;},'UAZbZ':function(_0x232e8b,_0x239436){return _0x232e8b(_0x239436);},'sMRpC':_0x1907c7(0xfa),'aAtQv':_0x1907c7(0xd4),'WyQJT':function(_0x4ab37c,_0x693dcf){return _0x4ab37c!=_0x693dcf;},'IEFUv':function(_0x509a1f){return _0x509a1f();},'fVSge':function(_0x4e9727,_0x256ba6,_0x5bdf6a){return _0x4e9727(_0x256ba6,_0x5bdf6a);},'tCMyv':function(_0x502a2a,_0x4373f6,_0x39ad06){return _0x502a2a(_0x4373f6,_0x39ad06);},'IlFoD':function(_0x4fb1d3,_0xd1f4aa){return _0x4fb1d3!==_0xd1f4aa;},'LgdTR':function(_0x73209c,_0x1b3877){return _0x73209c===_0x1b3877;},'UIazL':_0x1907c7(0xc1),'LMMTv':function(_0x445a10){return _0x445a10();},'oOLXp':function(_0x2e95d8){return _0x2e95d8();}},_0x1ae993=_0x3656e4=>{const _0x500815=_0x1907c7;return _0x12d0ee[_0x500815(0xeb)]($,_0x500815(0xda))[_0x500815(0xee)]($(_0x3656e4)[_0x500815(0xb8)]())[_0x500815(0xc6)]();},_0x361878=()=>{const _0x3d4479=_0x1907c7;if(_0x3d4479(0xe7)!==_0x3d4479(0xe7))_0x4b5199=_0x3c59d6;else {const _0x3b8fe3=_0x12d0ee[_0x3d4479(0xd8)],_0x219e55=_0x12d0ee[_0x3d4479(0xdc)];let _0x1d3d29=_0x12d0ee[_0x3d4479(0xd1)](i18n,_0x12d0ee[_0x3d4479(0xc0)]),_0x2944fc=_0x1d3d29[_0x3d4479(0xef)]('|'),_0x2fce49='';!_0x2944fc[0x0][_0x3d4479(0xba)](_0x3d4479(0xcc))?_0x2fce49=_0x3b8fe3:_0x2fce49=_0x219e55;let _0xa1728f=_0x3d4479(0xe3)+_0x2fce49+_0x3d4479(0xed);const _0x5940f0=$(_0xa1728f);_0x12d0ee[_0x3d4479(0xaa)]($,_0x12d0ee[_0x3d4479(0xf3)],_0x5940f0)[_0x3d4479(0xc6)](_0x2944fc[0x0]),_0x12d0ee[_0x3d4479(0xb5)]($,_0x3d4479(0xc7),_0x5940f0)[_0x3d4479(0xc6)](_0x2944fc[0x1]);let _0x279870=_0x3d4479(0xb3)+_0x12d0ee[_0x3d4479(0xdf)](i18n,_0x3d4479(0xbf))+_0x3d4479(0xe6);return _0x12d0ee[_0x3d4479(0xaa)]($,_0x12d0ee[_0x3d4479(0xc3)],_0x5940f0)[_0x3d4479(0xc6)](_0x279870),_0x1ae993(_0x5940f0);}},_0x374974=_0x12d0ee[_0x1907c7(0xf9)](_0x361878),_0x741488=()=>{const _0x193a4b=_0x1907c7;_0x12d0ee[_0x193a4b(0xe1)]($,_0x193a4b(0xae))[_0x193a4b(0xee)](_0x374974);};_0x741488();const _0x6d96a0=()=>{const _0x162efb=_0x1907c7;return _0x12d0ee[_0x162efb(0xd7)]($,_0x12d0ee[_0x162efb(0xf2)])?.[_0x162efb(0xb2)]()?.[_0x162efb(0xb2)]()?.[_0x162efb(0xb2)]()?.[_0x162efb(0xb2)]()?.[_0x162efb(0xb2)]();},_0x1c7e11=()=>{const _0x14bc37=_0x1907c7;let _0x1e3300=_0x12d0ee[_0x14bc37(0xc5)](_0x6d96a0);if(_0x12d0ee[_0x14bc37(0xab)](_0x1e3300,null))return ![];let _0x1872d2=_0x1e3300[_0x14bc37(0xb8)]();_0x12d0ee[_0x14bc37(0xaa)]($,_0x12d0ee[_0x14bc37(0xf2)],_0x1872d2)[_0x14bc37(0xc6)](''),$(_0x12d0ee[_0x14bc37(0xe8)],_0x1872d2)[_0x14bc37(0xc6)]('');const _0x363cc1=_0x12d0ee[_0x14bc37(0xd7)](_0x1ae993,_0x1872d2);if(_0x12d0ee[_0x14bc37(0xb6)](_0x374974,_0x363cc1))return ![];return !![];},_0x5b8475=()=>{const _0x4940fe=_0x1907c7,_0x3eaa77={'TARHg':function(_0xf1ab9e,_0x220c7d){const _0x49e540=a0_0x1bfb;return _0x12d0ee[_0x49e540(0xb0)](_0xf1ab9e,_0x220c7d);},'IOkap':_0x12d0ee[_0x4940fe(0xf2)]};if(_0x12d0ee[_0x4940fe(0xb6)](_0x12d0ee[_0x4940fe(0xea)],_0x12d0ee[_0x4940fe(0xb9)])){let _0xb4137=_0x6d96a0();_0x12d0ee[_0x4940fe(0xd0)](_0xb4137,null)&&_0xb4137[_0x4940fe(0xdb)]();}else _0x3eaa77[_0x4940fe(0xce)](_0x2634a3,_0x3eaa77[_0x4940fe(0xe5)])[_0x4940fe(0xc6)](_0x3b4454[_0x4940fe(0xb4)]),_0x3eaa77[_0x4940fe(0xce)](_0x2649ac,_0x4940fe(0xcd))[_0x4940fe(0xc6)](_0x42f089[_0x4940fe(0xc8)]);},_0x3b19b2=_0x15b0c9=>{const _0x5b2874=_0x1907c7,_0x29e25d={'pndcL':function(_0x2090f0){const _0x5c2780=a0_0x1bfb;return _0x12d0ee[_0x5c2780(0xcf)](_0x2090f0);},'Qymmh':function(_0x4941e0,_0x51071c){const _0x58f1a1=a0_0x1bfb;return _0x12d0ee[_0x58f1a1(0xab)](_0x4941e0,_0x51071c);},'LnsAb':function(_0x2ec821,_0x2a7d59,_0x4730f1){const _0x133563=a0_0x1bfb;return _0x12d0ee[_0x133563(0xd3)](_0x2ec821,_0x2a7d59,_0x4730f1);},'EUKYT':_0x12d0ee[_0x5b2874(0xf2)],'IizlZ':function(_0x2695ac,_0x4abdf7,_0x206d4b){const _0x3e5eee=_0x5b2874;return _0x12d0ee[_0x3e5eee(0xf7)](_0x2695ac,_0x4abdf7,_0x206d4b);},'wJtqo':_0x12d0ee[_0x5b2874(0xe8)],'ITgTy':function(_0x255dcf,_0x25a5b7){return _0x255dcf(_0x25a5b7);},'aBHmK':function(_0x48f474,_0x4884c7){const _0xfd705a=_0x5b2874;return _0x12d0ee[_0xfd705a(0xd9)](_0x48f474,_0x4884c7);}};if(_0x12d0ee[_0x5b2874(0xaf)](_0x12d0ee[_0x5b2874(0xcb)],_0x12d0ee[_0x5b2874(0xcb)]))_0x12d0ee[_0x5b2874(0xdf)]($,_0x12d0ee[_0x5b2874(0xf2)])[_0x5b2874(0xc6)](_0x15b0c9[_0x5b2874(0xb4)]),_0x12d0ee[_0x5b2874(0xd1)]($,_0x5b2874(0xcd))[_0x5b2874(0xc6)](_0x15b0c9[_0x5b2874(0xc8)]);else {let _0x4c50b2=_0x29e25d[_0x5b2874(0xe2)](_0x109735);if(_0x29e25d[_0x5b2874(0xf4)](_0x4c50b2,null))return ![];let _0x4dff4b=_0x4c50b2[_0x5b2874(0xb8)]();_0x29e25d[_0x5b2874(0xf0)](_0xfcde6d,_0x29e25d[_0x5b2874(0xca)],_0x4dff4b)[_0x5b2874(0xc6)](''),_0x29e25d[_0x5b2874(0xe9)](_0x1c6991,_0x29e25d[_0x5b2874(0xbb)],_0x4dff4b)[_0x5b2874(0xc6)]('');const _0xaea31e=_0x29e25d[_0x5b2874(0xe4)](_0x54f8aa,_0x4dff4b);if(_0x29e25d[_0x5b2874(0xb7)](_0x37d24a,_0xaea31e))return ![];return !![];}};ntopng_events_manager[_0x1907c7(0xad)](_0x1907c7(0xb1),ntopng_custom_events[_0x1907c7(0xe0)],_0x9d8684=>{const _0x21a5a9=_0x1907c7;_0x12d0ee[_0x21a5a9(0xdd)](_0x1c7e11)==![]&&(_0x12d0ee[_0x21a5a9(0xdd)](_0x5b8475),_0x12d0ee[_0x21a5a9(0xdd)](_0x741488)),_0x12d0ee[_0x21a5a9(0xe1)](_0x3b19b2,_0x9d8684);});}$(document)[a0_0x218ea9(0xbe)](()=>i());

    //import './ntopng.scss'
    // Make it globally available
    window.Tagify = Q;

})();
