<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="author" content="Joe Brown" />
    <title>covviz</title>

    <link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@700&display=swap" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css" rel="stylesheet">
    <link href="https://raw.githack.com/ttskch/select2-bootstrap4-theme/master/dist/select2-bootstrap4.css"
        rel="stylesheet">

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css">

    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>

    <!-- datatables, bootstrap 4 with select, responsive, scroller, searchpanes and html5 buttons -->
    <script type="text/javascript"
        src="https://cdn.datatables.net/v/bs4/dt-1.10.20/b-1.6.1/b-html5-1.6.1/r-2.2.3/sc-2.0.1/sp-1.0.1/sl-1.3.1/datatables.min.js"></script>
    <script type="text/javascript"
        src="https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/js/standalone/selectize.min.js"></script>


    <link rel="stylesheet" type="text/css"
        href="https://cdn.datatables.net/v/bs4/dt-1.10.20/b-1.6.1/b-html5-1.6.1/r-2.2.3/sc-2.0.1/sp-1.0.1/sl-1.3.1/datatables.min.css" />
    <link rel="stylesheet" type="text/css"
        href="https://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.6/css/selectize.bootstrap3.min.css">

    <style>
        body {
            height: 100%;
            margin: 0px;
            padding: 0px;
        }

        .brand {
            font-family: 'Rajdhani', sans-serif;
            font-size: 1.8rem;
        }

        .disabled_div {
            pointer-events: none;
            opacity: 0.4
        }

        div#ped_table_filter.dataTables_filter label {
            margin-bottom: 0;
        }

        div.dataTables_wrapper div.dataTables_info {
            padding-top: 0
        }

        .table-responsive {
            font-size: 0.85em;
        }

        .msg {
            background-color: white;
            font-size: 1em;
            border-right: none;
        }

        .tab-content>.tab-pane:not(.active),
        .pill-content>.pill-pane:not(.active) {
            display: block;
            height: 0;
            overflow-y: hidden;
        }

        .selectize-input {
            min-height: 38px !important;
            padding: 7px 12px !important;
        }

        .form-control.selectize-control {
            height: 38px !important;
            padding: 0px 0px 0px;
        }

        #gene_search_div {
            width: 180px;
        }

        table.dataTable thead th.sorting:after,
        table.dataTable thead th.sorting_asc:after,
        table.dataTable thead th.sorting_desc:after,
        table.dataTable thead th.sorting:before,
        table.dataTable thead th.sorting_asc:before,
        table.dataTable thead th.sorting_desc:before {
            font-family: FontAwesome !important;
        }

        table.dataTable.dtr-inline.collapsed>tbody>tr[role="row"]>td:first-child::before,
        table.dataTable.dtr-inline.collapsed>tbody>tr[role="row"]>th:first-child::before {
            top: 8px;
            background-color: #6c757d;
        }

        .btn-group-sm>.btn,
        .btn-sm {
            font-size: .775rem;
            line-height: 1.0;
        }
    </style>
</head>

<body>
    <nav class="navbar navbar-light bg-light border-bottom mx-0 p-1">
        <a class="navbar-brand brand text-decoration-none p-0 pl-2" href="https://github.com/brwnj/covviz">covviz</a>
        <ul class="nav nav-pills mr-auto">
            <li class="nav-item">
                <a class="nav-link active" data-toggle="tab" role="tab" id="chrom-cov-link"
                    href="#coverage">Chromosome Coverage</a>
            </li>
            <li class="nav-item" id="global_qc_nav" hidden>
                <a class="nav-link" data-toggle="tab" role="tab" id="global-qc-link" href="#qc">Global QC</a>
            </li>
        </ul>
        <form class="form-inline flex-nowrap my-2 my-lg-0" id="chromosome-ux-form">
            <div class="input-group flex-nowrap pr-2">
                <div class="input-group-prepend">
                    <span class="input-group-text d-none d-xl-block">Chromosome</span>
                    <span class="input-group-text d-xl-none">Chr</span>
                </div>
                <select class="form-control" id="region-select"></select>
                <div class="input-group-append">
                    <button class="btn btn-primary" type="button" id="btn-previous" title="Previous region (&#8592;)"
                        data-toggle="tooltip"><i class="fas fa-caret-left"></i></button>
                </div>
                <div class="input-group-append">
                    <button class="btn btn-primary" type="button" id="btn-next" title="Next region (&#8594;)"
                        data-toggle="tooltip"><i class="fas fa-caret-right"></i></button>
                </div>
            </div>
            <div class="pr-2" id="gene_search_div" hidden>
                <div class="input-group">
                    <input type="text" class="form-control" id="gene-search" aria-label="gene-search">
                </div>
            </div>
        </form>
    </nav>

    <main role="main" class="container-fluid w-90">
        <div class="tab-content pt-2">
            <div class="tab-pane show active" id="coverage" role="tabpanel">
                <ul class="nav nav-tabs" role="tablist">
                    <li class="nav-item">
                        <a class="nav-link active" id="scaled_tab" data-toggle="tab" href="#scaled" role="tab">Scaled
                            coverage</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" id="cov_tab" data-toggle="tab" href="#cov" role="tab">Proportions
                            covered</a>
                    </li>
                </ul>
                <div class="tab-content">
                    <div class="tab-pane show active" id="scaled" role="tabpanel">
                        <div style="height:500px" id="scaled_plot_placeholder">
                            <div
                                class="mt-2 d-flex justify-content-center align-items-center bg-light text-muted h-100">
                                <div class="d-flex flex-column">
                                    <div>
                                        Loading...
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="row pt-2" id="scaled_plot" hidden></div>
                    </div>
                    <div class="tab-pane" id="cov" role="tabpanel">
                        <div style="height:500px" id="cov_plot_placeholder">
                            <div
                                class="mt-2 d-flex justify-content-center align-items-center bg-light text-muted h-100">
                                <div class="d-flex flex-column">
                                    <div>
                                        Loading...
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="row pt-2" id="cov_plot" hidden></div>
                    </div>
                </div>
            </div>
            <div class="tab-pane" id="qc" role="tabpanel">
                <div id="global_qc_plots" class="container-fluid w-90">
                    <div class="row border-bottom">
                        <div class="col-6">
                            <h5>Inferred sex</h5>
                        </div>
                        <div class="col-6">
                            <h5>Problematic low and non-uniform coverage bins</h5>
                        </div>
                    </div>
                    <div class="row">
                        <div id="inferred_sex" class="col-6"></div>
                        <div id="bin_counts" class="col-6"></div>
                    </div>
                </div>
            </div>
        </div>
        <div class="container-fluid mb-5">
            <div class="row border bg-light py-3 d-none" id="table-wrapper">
                <div class="col-12">
                    <div class="table-responsive">
                        <table id="ped_table" class="table table-hover table-striped table-sm display nowrap"
                            width="100%"></table>
                    </div>
                </div>
            </div>
        </div>
    </main>
</body>

<script>
    const data = {{ data| tojson }}
    const colors = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf", "#7CB5EC", "#434348", "#90ED7D", "#F7A35C", "#8085E9", "#F15C80", "#E4D354", "#2B908F", "#F45B5B", "#91E8E1", "#4E79A7", "#F28E2C", "#E15759", "#76B7B2", "#59A14F", "#EDC949", "#AF7AA1", "#FF9DA7", "#9C755F", "#BAB0AB"]
    const dark2 = ["#1b9e77", "#d95f02", "#7570b3", "#e7298a", "#66a61e", "#e6ab02", "#a6761d", "#666666"]
    const cov_layout = {
        title: "",
        margin: { t: 10, b: 40 },
        height: 500,
        xaxis: { title: "Scaled Coverage", showgrid: false, range: [0, 1.5] },
        yaxis: { title: "Proportion of Regions Covered", range: [0, 1.] },
        hovermode: "closest",
        showlegend: false,
    }
    const scaled_layout = {
        title: "",
        margin: { t: 10, b: 40 },
        height: 500,
        xaxis: { title: "Position", rangeslider: {}, autorange: true, showgrid: false, showlines: false, zeroline: false },
        yaxis: {
            title: "Scaled Coverage",
            fixedrange: true,
            domain: [0, 3],
            tickvals: [0, 0.5, 1, 1.5, 2, 2.5, 3],
            showgrid: true,
            showticklabels: true,
            zeroline: false
        },
        hovermode: "closest",
        showlegend: false,
    }
    let ped = false
    let cov_traces = []
    let scaled_traces = []
    let gene_search_obj
    let ped_table = null

    const sample_colors = (arr) => {
        let cols = {}
        for (const [i, sample] of arr.entries()) {
            cols[sample] = colors[i % colors.length]
        }
        return cols
    }
    const color_map = sample_colors(data.sample_list)

    // https://stackoverflow.com/a/4198132
    const get_hash_params = (h = false) => {

        let hash_params = {}
        let e,
            a = /\+/g,  // Regex for replacing addition symbol with a space
            r = /([^&;=]+)=?([^&;]*)/g,
            d = (s) => { return decodeURIComponent(s.replace(a, " ")); },
            q = h ? h : window.location.hash.substring(1)

        while (e = r.exec(q))
            hash_params[d(e[1])] = d(e[2])

        return hash_params
    }

    const set_hash_params = (obj) => {
        let old = get_hash_params()
        for (let k in obj) {
            if (obj[k] == null) {
                delete old[k]
            } else {
                old[k] = obj[k]
            }
        }
        let str = "#"
        for (let k in old) {
            str += `${k}=${old[k]}&`
        }

        window.location.hash = str.slice(0, -1) // drop final '&'
        return old
    }

    // not currently listening for these events
    // window.onhashchange = (event) => {
    //     let old_hp = get_hash_params(new URL(event.oldURL).hash.substring(1))
    //     let new_hp = get_hash_params()
    // }

    const build_cov = (chr) => {
        // hide the placeholder
        $('#cov_plot_placeholder').prop('hidden', true)
        // show the plot
        $('#cov_plot').prop('hidden', false)
        cov_layout.xaxis.range = [0, 1.5]
        cov_layout.yaxis.range = [0, 1.]

        cov_traces = []
        for (const sample in data.roc[chr]) {
            cov_traces.push({
                x: data.roc.x_coords,
                y: data.roc[chr][sample],
                hoverinfo: "text",
                mode: "lines",
                text: sample,
                marker: { "color": color_map[sample] }
            })
        }

        let cov_plot = document.getElementById("cov_plot")
        Plotly.react(cov_plot, cov_traces, cov_layout)
        cov_plot.removeAllListeners("plotly_click")
        cov_plot.removeAllListeners("plotly_doubleclick")
        cov_plot.on("plotly_click", handle_plot_click)
        cov_plot.on("plotly_doubleclick", handle_plot_doubleclick)
    }

    const plot_intervals = (ranges, offset = -0.15) => {
        let x = []
        let y = []
        let text = []
        let prev = 0
        let max_offset = offset
        let y_offset = offset
        for (const range of ranges) {
            let [start, end, name] = range

            // overlaps previous gene
            if (start <= prev) {
                y_offset -= 0.05
                if (y_offset < max_offset) {
                    max_offset = y_offset
                }
            } else {
                y_offset = offset
            }

            x.push(start)
            x.push(end)
            y.push(y_offset)
            y.push(y_offset)
            text.push(name)
            text.push(name)

            // gap
            x.push("")
            y.push("")
            text.push("")
            prev = end
        }
        return {
            x: x,
            y: y,
            text: text,
            track_depth: max_offset,
        }
    }

    const build_scaled = (chr) => {
        // hide the placeholder
        $('#scaled_plot_placeholder').prop('hidden', true)
        // show the plot
        $('#scaled_plot').prop('hidden', false)

        scaled_layout.xaxis.range = []
        scaled_layout.xaxis.autorange = true

        scaled_traces = []
        // add the backgrounds
        for (const idx of [...Array(data[chr]["upper"].length).keys()]) {
            for (const bound of ["lower", "upper"]) {
                scaled_traces.push({
                    x: [...data.shared_coords, ...data[chr].coords],
                    y: data[chr][bound][idx],
                    fill: bound == "upper" ? "tonexty" : "none",
                    fillcolor: "rgba(108,117,125,0.3)",
                    type: "scatter",
                    mode: "lines",
                    hoverinfo: "none",
                    marker: { "color": "rgba(108,117,125,0.1)" },
                })
            }
        }

        // local sample traces
        for (const sample of data[chr].samples) {
            scaled_traces.push({
                x: sample.x,
                y: sample.y,
                text: sample.name,
                connectgaps: false,
                hoverinfo: "text",
                mode: "lines",
                name: "significant",
                marker: {
                    width: 1, color: color_map[sample.name]
                }
            })
        }

        // annotation tracks
        let y_offset = -0.10
        let track_idx = 0

        if ("annotations" in data[chr]) {
            for (const tracktype of Object.keys(data[chr].annotations)) {
                for (const track of data[chr].annotations[tracktype]) {
                    let trackname = track[0]
                    let track_color = dark2[track_idx % dark2.length]
                    let track_depth = 0
                    if (tracktype == "gff" || tracktype == "bed") {
                        let intervals = plot_intervals(track[1], y_offset)
                        track_depth = intervals.track_depth
                        scaled_traces.push({
                            x: intervals.x,
                            y: intervals.y,
                            text: intervals.text,
                            type: "scattergl",
                            name: trackname,
                            tracktype: tracktype,
                            connectgaps: false,
                            showlegend: false,
                            line: { width: 2, color: track_color },
                            mode: "lines+markers",
                            hoverinfo: "text+x+name",
                            hoverlabel: { namelength: -1 },
                            marker: {
                                size: 6,
                                symbol: "square",
                                color: track_color,
                                line: { width: 1, color: "white" },
                            },
                        })
                    } else {
                        scaled_traces.push({
                            x: track[1].x,
                            y: Array(track[1].x.length).fill(y_offset),
                            mode: "markers",
                            type: "scattergl",
                            name: trackname,
                            text: track[1].text.map((i) => {
                                return i.replaceAll(";", "<br>")
                            }),
                            hoverinfo: "text+x+name",
                            hoverlabel: { namelength: -1 },
                            marker: {
                                size: 6,
                                symbol: "square",
                                color: track_color,
                                line: { width: 1, color: "white" },
                            },
                            tracktype: tracktype,
                        })
                    }
                    y_offset = track_depth - 0.10
                    track_idx += 1
                }
            }
        }

        let scaled_plot = document.getElementById("scaled_plot")
        let p = Plotly.react(scaled_plot, scaled_traces, scaled_layout)
        scaled_plot.removeAllListeners("plotly_click")
        scaled_plot.removeAllListeners("plotly_doubleclick")
        scaled_plot.removeAllListeners("plotly_relayout")
        scaled_plot.on("plotly_click", handle_plot_click)
        scaled_plot.on("plotly_doubleclick", handle_plot_doubleclick)
        scaled_plot.on("plotly_relayout", handle_zoom_event)
        $("#scaled_plot").removeClass("disabled_div")
        return p
    }

    const csv_button_click = () => {
        ped_table.button('.buttons-csv').trigger()
    }

    const copy_button_click = () => {
        ped_table.button('.buttons-copy').trigger()
    }

    const reset_button_click = () => {
        build_scatter_plots()
        reset_datatable()
    }

    const build_table = () => {
        if ("ped" in data) {
            $("#table-wrapper").removeClass("d-none")
            ped_table = $("#ped_table").DataTable({
                data: data.ped.slice(1),
                columns: data.ped[0],
                responsive: true,
                // scrollX: true,
                scrollCollapse: true,
                paging: false,
                // pagingType: "simple",
                scrollY: "40vh",
                bSortClasses: false,
                buttons: [
                    'copyHtml5',
                    {
                        extend: 'csvHtml5',
                        title: 'covviz_export',
                    },
                ],
                dom: '<"top row"<"col-6"i><"col-6"f>>rt<"bottom"><"clear">',
                infoCallback: (oSettings, iStart, iEnd, iMax, iTotal, sPre) => {
                    let msg
                    // selection
                    if (iMax != iTotal) {
                        msg = `Selected ${iTotal} of ${iMax} records`
                    } else {
                        msg = `${iTotal} records`
                    }
                    return `
                        <span class="datatable-info">
                            <div class="input-group flex-nowrap input-group-sm">
                                <div class="input-group-prepend">
                                    <span class="input-group-text msg">${msg}</span>
                                </div>
                                <div class="input-group-append d-none" id="reset-button-wrapper">
                                    <button type="button" class="btn btn-outline-primary" onclick="reset_button_click()">Reset</button>
                                </div>
                                <div class="input-group-append">
                                    <button type="button" class="btn btn-outline-primary" id="csv-button" onclick="csv_button_click()">CSV</button>
                                </div>
                                <div class="input-group-append">
                                    <button type="button" class="btn btn-outline-primary" id="copy-button" onclick="copy_button_click()">Copy</button>
                                </div>
                            </div>
                        </span>
                    `
                },
                drawCallback: (settings) => {
                    if (settings.fnRecordsTotal() < data.sample_list.length) {
                        $("#reset-button-wrapper").removeClass("d-none")
                    } else {
                        $("#reset-button-wrapper").addClass("d-none")
                    }
                }
            })

            // track if we actually have a table rendered
            if ("sample_column" in data) {
                ped = true
                // register table clicks on sample_column
                ped_table.on('click', 'tr', function () {
                    if ($(this).hasClass('selected')) {
                        ped_table.$('tr.selected').removeClass('selected')
                        reset_line_plots()
                    }
                    else {
                        ped_table.$('tr.selected').removeClass('selected')
                        $(this).addClass('selected')
                        let sample_id = ped_table.rows('.selected').data()[0][data.sample_column]
                        highlight_plot_traces(sample_id)
                    }
                })
            }
        }
    }

    const get_scatter_data = (x, y, text, colors = 'rgba(31,120,180,0.5)', hover = false) => {
        return {
            x: x,
            y: y,
            mode: "markers",
            type: "scatter",
            text: text,
            hovertext: hover,
            hoverinfo: 'text',
            marker: { size: 8, color: colors, line: { color: 'rgb(40,40,40)', width: 1 } },
        }
    }

    const build_scatter_plot = (div, data, x_label, y_label) => {
        let el = document.getElementById(div)
        Plotly.react(el, [data], {
            margin: { t: 15, },
            height: 300,
            xaxis: { title: x_label },
            yaxis: { title: y_label },
            hovermode: "closest",
            legend: { orientation: "h", x: 0, y: 1.125, },
            dragmode: "lasso",
        })
        el.removeAllListeners("plotly_selected")
        el.removeAllListeners("plotly_deselect")
        el.on("plotly_selected", handle_scatter_selection)
        el.on("plotly_deselect", () => {
            build_scatter_plots()
            reset_datatable()
        })
    }

    const build_scatter_plots = () => {
        // inferred sex
        build_scatter_plot(
            "inferred_sex",
            get_scatter_data(data.depth.inferred.x, data.depth.inferred.y, data.depth.bins.samples, data.depth.inferred.color, data.depth.inferred.hover),
            "X copy number",
            "Y copy number"
        )

        // bin counts
        build_scatter_plot(
            "bin_counts",
            get_scatter_data(data.depth.bins.x, data.depth.bins.y, data.depth.bins.samples),
            "Proportion of bins with depth < 0.15",
            "Proportion of bins with depth<br>outside of (0.85, 1.15)"
        )

        // pca plots
        if ("pca_1" in data.depth.pca) {
            build_scatter_plot(
                "pca_1",
                get_scatter_data(data.depth.pca.pca_1, data.depth.pca.pca_2, data.depth.bins.samples),
                "PC1",
                "PC2"
            )
            build_scatter_plot(
                "pca_2",
                get_scatter_data(data.depth.pca.pca_1, data.depth.pca.pca_3, data.depth.bins.samples),
                "PC1",
                "PC3"
            )
        }
    }

    const build_global_qc = () => {
        // constructs required html and then calls building of plots
        if (!("depth" in data)) {
            return
        }

        $("#global_qc_nav").prop('hidden', false)

        // PCAs
        if ("pca_1" in data.depth.pca) {
            // insert HTML into plot area
            let pa = document.getElementById("global_qc_plots")
            pa.insertAdjacentHTML(
                'beforeend',
                `<div class="row border-bottom">
                <div class="col-6">
                    <h5>PCA 1 vs 2</h5>
                </div>
                <div class="col-6">
                    <h5>PCA 1 vs 3</h5>
                </div>
            </div>
            <div class="row">
                <div id="pca_1" class="col-6"></div>
                <div id="pca_2" class="col-6"></div>
            </div>`
            )
        }

        build_scatter_plots()
    }

    $('#region-select').on("change", () => {
        let chr = $('#region-select').find(':selected')[0].text
        set_hash_params({chr:chr, loc:null})
        let sample_id

        try {
            gene_search_obj.clear()
        }
        catch (err) {
        }
        $("#scaled_plot").addClass("disabled_div")
        if (ped) {
            if (ped_table.rows(".selected").data().length == 1) {
                sample_id = ped_table.rows(".selected").data()[0][data.sample_column]
            }
        }
        build_cov(chr)
        build_scaled(chr)
        if (sample_id) {
            highlight_plot_traces(sample_id)
        }
    })

    const search_datatable = (sample_id) => {
        // remove selection
        ped_table.$('tr.selected').removeClass('selected')
        // run the search
        ped_table.search(sample_id).draw()
        // highlight the selected sample within the search results
        ped_table.rows().every(function (row_index, table_loop, row_loop) {
            if (sample_id == this.data()[data.sample_column]) {
                ped_table.rows([row_index]).nodes().to$().addClass('selected')
            }
        })
    }

    const reset_datatable = () => {
        // remove selection
        ped_table.$('tr.selected').removeClass('selected')
        // remove search
        ped_table.search('').columns().search('') //.draw()
        // reset all table data
        ped_table.clear()
        ped_table.rows.add(data.ped.slice(1))
        ped_table.draw()
    }

    const handle_plot_click = (click_data) => {
        if (click_data.points[0].data.tracktype == 'gff') {
            let genes = click_data.points[0].text.split(";")
            for (var i = 0; i < genes.length; i++) {
                window.open("https://www.genecards.org/cgi-bin/carddisp.pl?gene=" + genes[i], "_blank")
            }
        } else if (click_data.points[0].data.tracktype == 'vcf') {
            // nothing yet
            return
        } else if (click_data.points[0].data.tracktype == 'bed') {
            // nothing yet
            return
        } else {
            let sample_id = click_data.points[0].data.text
            if (sample_id) {
                highlight_plot_traces(sample_id)
                if (ped) {
                    search_datatable(sample_id)
                }
            }
        }
    }

    const handle_scatter_selection = (event) => {
        // plot click event
        if (event === undefined) {
            return
        }
        let selected_sample_ids = event.points.map(point => point.text)
        let colors = []
        for (let i = 0; i < data.depth.bins.samples.length; i++) {
            colors.push('rgba(255,255,255,0.1)')
        }
        event.points.forEach((pt) => {
            colors[pt.pointNumber] = 'rgba(31,120,180,0.5)'
        })

        Plotly.restyle('inferred_sex', 'marker.color', [colors])
        Plotly.restyle('bin_counts', 'marker.color', [colors])
        if ("pca_1" in data.depth.pca) {
            Plotly.restyle('pca_1', 'marker.color', [colors])
            Plotly.restyle('pca_2', 'marker.color', [colors])
        }

        // subset table based on selection
        let table = []
        for (const row of data.ped.slice(1)) {
            if (selected_sample_ids.includes(row[data.sample_column])) {
                table.push(row)
            }
        }
        ped_table.clear()
        ped_table.rows.add(table)
        ped_table.draw()
    }

    const reset_line_plots = () => {
        scaled_layout.xaxis.autorange = true
        Plotly.react("scaled_plot", scaled_traces, scaled_layout).then(() => {set_hash_params({loc:null})})

        cov_layout.xaxis.range = [0, 1.5]
        cov_layout.yaxis.range = [0, 1.]
        Plotly.react("cov_plot", cov_traces, cov_layout)
    }

    const handle_plot_doubleclick = () => {
        reset_line_plots()
        // de-select in table
        if ("ped" in data) {
            reset_datatable()
        }
    }

    const get_selected_range = () => {
        let p = document.getElementById("scaled_plot")
        let start = p.layout.xaxis.range[0] < 0 ? 0 : Math.round(p.layout.xaxis.range[0])
        let end = Math.round(p.layout.xaxis.range[1])
        return [start, end]
    }

    const zoom_scaled_plot = (start, end) => {
        scaled_layout.xaxis.range = [start, end]
        scaled_layout.xaxis.autorange = false
        Plotly.relayout("scaled_plot", scaled_layout)
    }

    const handle_zoom_event = () => {
        let [start, end] = get_selected_range()
        set_hash_params({loc:`${start}-${end}`})
    }

    const highlight_plot_traces = (sample_id) => {
        let s_traces = []
        let c_traces = []
        let k_traces = []
        let highlight_color;
        for (var i = 0; i < scaled_traces.length; i++) {
            // let trace = scaled_traces[i]
            let trace = $.extend(true, {}, scaled_traces[i])
            // limit to significant sample traces
            if (trace.name == "significant") {
                // de-prioritize; gray
                if (trace.text != sample_id) {
                    trace.marker.color = 'rgba(108,117,125,0.2)'
                }
                else {
                    highlight_color = scaled_traces[i].marker.color
                }
            }
            s_traces.push(trace)
        }
        for (var i = 0; i < cov_traces.length; i++) {
            let trace = $.extend(true, {}, cov_traces[i])
            if (trace.text != sample_id) {
                trace.marker.color = 'rgba(108,117,125,0.2)'
            } else {
                trace.marker.color = highlight_color
            }
            c_traces.push(trace)
        }
        Plotly.react("cov_plot", c_traces, cov_layout)
        Plotly.react("scaled_plot", s_traces, scaled_layout)
    }

    const build_gene_search = () => {
        // grab the genes and their coords from gffs
        let genes = []
        let seen = new Set()

        for (const chr of data.chromosomes) {
            if ("annotations" in data[chr] && "gff" in data[chr].annotations) {
                for (const gff of data[chr].annotations.gff) {
                    let filename = gff[0]
                    let gene_data = gff[1]
                    for (const gene of gene_data) {
                        let [start, end, name] = gene
                        if (!(name in seen)) {
                            seen.add(name)
                            genes.push({
                                n: name,
                                v: [chr, start, end]
                            })
                        }
                    }
                }
            }
        }

        if (genes.length > 0) {

            // re-organize the buttons and show the search field
            // $('#chrom_selector_div').removeClass('col-12').addClass('col-10')
            $("#gene_search_div").prop('hidden', false)

            // n = name, v = [chrom, start, end]
            let gene_search = $('#gene-search').selectize({
                // plugins: ['remove_button'],
                valueField: 'v',
                labelField: 'n',
                searchField: 'n',
                options: genes,
                placeholder: 'Gene ID',
                mode: 'single',
                closeAfterSelect: true,
                maxItems: 1,
                maxOptions: 20,
            })
            gene_search_obj = gene_search[0].selectize
            // register handler
            $('#gene-search').on('change', function () {
                update_scaled_range(gene_search_obj.items[0])
            })
        }
    }

    const update_scaled_range = (coords) => {
        // change event can occur on clear
        if (!coords) {
            set_hash_params({loc: null})
            return
        }

        coords = coords.split(',')
        let chr = coords[0]
        let start = parseInt(coords[1])
        let end = parseInt(coords[2])
        let padding = (end - start) / 2
        let selected_chr = $('#region-select').find(':selected')[0].text
        if (selected_chr != chr) {
            $("#region-select").val(chr)
            // triggers redraw on new chromosome
            $("#region-select").trigger('change')
        }

        // zoom the scaled plot
        scaled_layout.xaxis.range = [start - padding, end + padding]
        scaled_layout.xaxis.autorange = false
        Plotly.relayout("scaled_plot", scaled_layout)
        gene_search_obj.clear()
    }

    $("#region-select").select2({
        data: data.chromosomes.map(i => {
            let p = get_hash_params()
            if ("chr" in p) {
                if (p.chr == i) {
                    return { id: i, text: i, selected: true}
                }
            }
            return { id: i, text: i }
        }),
        selectOnClose: true,
        width: 140,
        theme: 'bootstrap4',
    })

    $("#global-qc-link").on("click", () => {
        $("#chromosome-ux-form").addClass('d-none')
    })

    $("#chrom-cov-link").on("click", () => {
        $("#chromosome-ux-form").removeClass('d-none')
    })

    $("#btn-previous").click(function () {
        next_region(-1)
    })

    $("#btn-next").click(function () {
        next_region()
    })

    document.addEventListener("keydown", function (e) {
        // allow cursor nav inside input elements
        if (document.activeElement.tagName.toLowerCase() == "input") {
            return
        }
        if (e.which == 37) {
            next_region(-1)
        } else if (e.which == 39) {
            next_region()
        } else {
            return
        }
    })

    const next_region = (offset = 1) => {
        let chr = $('#region-select').find(':selected')[0].text
        let selected_index = data.chromosomes.indexOf(chr)
        let next_index
        // previous
        if (offset == -1) {
            if (selected_index == 0) {
                next_index = data.chromosomes.length - 1
            } else {
                next_index = selected_index - 1
            }
        } else {
            if (selected_index == data.chromosomes.length - 1) {
                next_index = 0
            } else {
                next_index = selected_index + 1
            }
        }
        chr = data.chromosomes[next_index]
        $("#region-select").val(chr)
        $("#region-select").trigger("change")
    }

    $(document).ready(function () {
        $(function () {
            $('[data-toggle="tooltip"]').tooltip()
        })

        let p = get_hash_params()
        let chr = $('#region-select').find(':selected')[0].text

        build_gene_search()
        build_cov(chr)
        build_scaled(chr).then(() => {
            if ("loc" in p) {
                try {
                    let coords = p.loc.split("-")
                    zoom_scaled_plot(parseInt(coords[0]), parseInt(coords[1]))
                }
                catch {
                    console.log("invalid loc:", p.loc)
                }
            }
        })
        build_table()
        build_global_qc()
    })

</script>

</html>
