{% extends 'base.html.j2' %}

{% block head %}
<title>{{ title }}</title>
{% endblock %}

{% block content %}

<div class="row flex-nowrap">
    <div class="col-md-4 col-12 menu">
        <div class="card">
            <div class="card-header">
                <a class="pull-right" role="button"><i class="fa" id="hide-show"></i></a>
                <h1><a href="index.html" class="link-unstyled">{{ title }}</a></h1>
                <p><a id="download" href="#">Download SVG</a></p>

                <nav>
                    <div class="nav nav-tabs card-header-tabs" role="tablist">
                        <a data-toggle="tab" role="tab" class="nav-item nav-link active" href="#geneset">Gene Sets</a>
                        <a data-toggle="tab" role="tab" class="nav-item nav-link" href="#genes">Genes</a>
                        <a data-toggle="tab" role="tab" class="nav-item nav-link" href="#structure">Structure</a>
                        <a data-toggle="tab" role="tab" class="nav-item nav-link" href="#style">Style</a>
                    </div>
                </nav>

            </div>
            <div class="tab-content">
                <div class="tab-pane card-body fade show active" id="geneset" role="tabpanel">
                    <textarea class="form-control" id="chooseGeneSets" placeholder="Choose Gene Sets..."></textarea>
                    <br>
                    <hr>
                    <br>
                    <input class="form-control" id="findGeneSet" placeholder="Search for Gene Sets by Gene..." />
                </div>


                <div class="tab-pane card-body fade" id="genes" role="tabpanel">
                    <div contenteditable="true" class="form-control" id="chooseGenes" placeholder="Choose Genes..."></div>
                </div>


                <div class="tab-pane card-body fade" id="structure" role="tabpanel">
                    <div><input type="checkbox" id="reordering" value="reordering" checked><label for="reordering"> Reset Gene Order on each change. </label></div>

                    <br>
                    <div id="values">
                        <b><label>Show Gene Values of</label></b>
                        <div class="form-check"><input class="form-check-input" type="radio" name="values" value="logcount">log₁₀ Original Counts</div>
                        <div class="form-check"><input class="form-check-input" type="radio" name="values" value="zscore_stddev" checked> Z-Scores (Standard Deviations)</div>
                        <div class="form-check"><input class="form-check-input" type="radio" name="values" value="zscore_mad"> Z-Scores (Mean Absolute Deviation)</div>
                    </div>

                    <br>
                    <div id="sorting">
                        <b><label>Sort Genes by</label></b>
                        <div class="form-check"><input class="form-check-input" type="radio" name="sorting" value="complete" checked> Complete-Linkage Hierarchical Clustering and Optimal Leaf Ordering</div>
                        <div id="distance" class="ml-3">
                            <label>Measure distance by</label>
                            <div class="form-check"><input class="form-check-input" type="radio" name="distance" value="euclidian" id="euclidian" checked><label>Euclidian</label></div>
                            <div class="form-check"><input class="form-check-input" type="radio" name="distance" value="pearson" disabled><label>Pearson</label></div>
                            <div class="form-check"><input class="form-check-input" type="radio" name="distance" value="spearman_rank" disabled><label>Spearman Rank</label></div>
                            <div class="form-check"><input class="form-check-input" type="radio" name="distance" value="kendall_tau" disabled><label>Kendall's tau</label></div>
                            <div class="form-check"><input class="form-check-input" type="radio" name="distance" value="manhattan" disabled><label>Manhattan</label></div>
                        </div>
                        <div class="form-check"><input class="form-check-input" type="radio" name="sorting" value="pc1"> Principal Component 1 of genes in sample space</div>
                    </div>

                    <br>
                    <label>Use the Backspace key to remove samples, groups, genes, and gene sets.</label>
                    <br>
                    <label>Press ⌘ and click a gene symbol to open GeneCards.</label>


                </div>

                <div class="tab-pane card-body fade" id="style" role="tabpanel">
                    <b><label>Legends</label></b>
                    <div><input type="checkbox" id="show_sample_metadata" value="show_sample_metadata" checked><label> Show Sample Metadata Text </label></div>
                    <div><input type="checkbox" id="show_legends" value="show_legends"><label> Show legends </label></div>
                    <div id="leg_pos">
                        <label>Legends Position</label><br>
                        <div class="form-check" style="display: inline; margin-right: 5px;"><input class="form-check-input" type="radio" name="legends_position" id="legend_left" value="left" disabled><label>Left</label></div>
                        <div class="form-check" style="display: inline; margin-right: 5px;"><input class="form-check-input" type="radio" name="legends_position" id="legend_right" value="right" checked disabled><label>Right</label></div>
                        <div class="form-check" style="display: inline; margin-right: 5px;"><input class="form-check-input" type="radio" name="legends_position" id="legend_above" value="above" disabled><label>Above</label></div>
                        <div class="form-check" style="display: inline; margin-right: 5px;"><input class="form-check-input" type="radio" name="legends_position" id="legend_below" value="below" disabled><label>Below</label></div>
                    </div>
                    <div><input type="checkbox" id="darker_legends" value="darker_legends" disabled><label> Boldface Legends </label></div>


                    <br>
                    <div id="colors">
                        <b><label>Cell Colors</label></b>
                        <div class="form-check"><input class="form-check-input" type="radio" name="colors" id="Triplet" value="interpolateTriplet" checked> along <input type="color" value="#0000cc" id="negative_color"> <input type="color" value="#c0c0c0" id="middle_color"> <input type="color" value="#cc0000" id="positive_color"></div>
                        <div class="form-check"><input class="form-check-input" type="radio" name="colors" id="Viridis" value="interpolateViridis"> Viridis</div>
                        <div class="form-check"><input class="form-check-input" type="radio" name="colors" id="Magma" value="interpolateMagma"> Magma</div>
                    </div>

                    <br>
                    <b><label>Spacing</label></b>
                    <div>
                        <label for="cell_spacing">Cell Spacing</label>
                        <input type="range" id="cell_spacing" name="cell_spacing" min="0" max="2" step="0.01" value="1" style="position: relative; top: 3px; display: inline">
                    </div>
                    <div>
                        <label for="gene_set_spacing">Gene Set Spacing</label>
                        <input type="range" id="gene_set_spacing" name="gene_set_spacing" min="0" max="40" step="1" value="10" style="position: relative; top: 3px; display: inline">
                    </div>
                    <div>
                        <label for="metadata_spacing">Metadata Spacing</label><br>
                        <select class="category" id="category_spacing" onchange="$('#metadata_spacing').val(metadata_spacing[this.value])"></select>
                        <input type="range" id="metadata_spacing" name="metadata_spacing" min="0" max="40" step="1" value="0" style="position: relative; top: 3px; display: inline">
                    </div>

                    <br>
                    <b><label>Positions</label></b>
                    <div id="x_labels">
                        <label>X-Axis Labels</label>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="x_labels" id="before" value="before"> before</div>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="x_labels" id="after" value="after" checked> after</div>
                    </div>
                    <div id="x_meta">
                        <label>X-Axis Metadata</label>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="x_meta" id="before" value="before" checked> before</div>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="x_meta" id="after" value="after"> after</div>
                    </div>
                    <div id="x_style">
                        <label>X-Axis Style</label>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="x_style" id="genes" value="genes"> Meta > Label</div>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="x_style" id="samples" value="samples" checked> Label > Meta</div>
                    </div>
                    <div>
                        <input type="checkbox" id="show_x_level_names" value="show_x_level_names" checked><label> Show X-Axis level names </label>
                    </div>
                    <div>
                        <label style="display: inline" for="rotation">X-Axis Label rotation  60</label>
                        <input type="range" id="rotation" name="rotation" min="60" max="90" step="1" value="60" style="position: relative; top: 3px; display: inline">
                        <label style="display: inline">90</label>
                    </div>


                    <br>
                    <div id="y_labels">
                        <label>Y-Axis Labels</label>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="y_labels" id="before" value="before" checked> before</div>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="y_labels" id="after" value="after"> after</div>
                    </div>
                    <div id="y_meta">
                        <label>Y-Axis Metadata</label>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="y_meta" id="before" value="before" checked> before</div>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="y_meta" id="after" value="after"> after</div>
                    </div>
                    <div id="y_style">
                        <label>Y-Axis Style</label>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="y_style" id="genes" value="genes" checked> Meta > Label</div>
                        <div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="y_style" id="samples" value="samples"> Label > Meta</div>
                    </div>
                    <div><input type="checkbox" id="show_y_level_names" value="show_y_level_names" checked><label> Show Y-Axis level names </label></div>

                    <br>
                    <button type="button" class="btn btn-outline-secondary btn-sm" id="transpose">Transpose</button>

                    <br>
                    <br>
                    <label>Drag the corners of the heatmap to resize.</label>
                    <br>
                    <label>Drag items to change their order.</label>
                    <br>
                    <label>Pinch or scroll with the control key to zoom.</label>

                </div>

            </div>

            <div class="card-footer">
                <small class="text-muted">This page generated by <a href="https://github.com/zfrenchee/Axial" target="_blank">Axial</a></small>
            </div>
        </div>
    </div>

    <div id="graph-container" class="col-md col-12"></div>

</div>

{% endblock %}

{% block scripts %}

{{ scripts_block }}

<script type="text/javascript">

/////////////////////////////////////////////////////////////////////////////
         ///////    Stuff involving list comparisons   ///////
/////////////////////////////////////////////////////////////////////////////

let distance_to_x_from_i_in_a = (a, x, i) => _(a).indexOf(x, i) - i;

Array.prototype.slice_from_to = function(from, to) { return _.object(_.range(from, to), this.slice(from, to)); };

function uniques(a1, i1, a2, i2) {

    if (i1 === a1.length || i2 === a2.length) {

        return [[a1.slice_from_to(i1, a1.length),
                 a2.slice_from_to(i2, a2.length)]]; }

    else if (a1[i1] === a2[i2]) { return uniques(a1, i1+1, a2, i2+1); }

    else {
        var distance_in_a1_to_a2_head = distance_to_x_from_i_in_a(a1, a2[i2], i1);
        var distance_in_a2_to_a1_head = distance_to_x_from_i_in_a(a2, a1[i1], i2);

        var a1_head_exists_in_a2 = distance_in_a2_to_a1_head > 0;
        var a2_head_exists_in_a1 = distance_in_a1_to_a2_head > 0;

        if (!a1_head_exists_in_a2 && !a2_head_exists_in_a1) { return [[{[''+i1]:a1[i1]}, {[''+i2]:a2[i2]}]].concat(uniques(a1, i1+1, a2, i2+1)); }

        else if (a1_head_exists_in_a2 && !a2_head_exists_in_a1) { return [[{}, a2.slice_from_to(i2, i2+distance_in_a2_to_a1_head)]].concat(uniques(a1, i1, a2, i2+distance_in_a2_to_a1_head)); }

        else if (!a1_head_exists_in_a2 && a2_head_exists_in_a1) { return [[a1.slice_from_to(i1, i1+distance_in_a1_to_a2_head), {}]].concat(uniques(a1, i1+distance_in_a1_to_a2_head, a2, i2)); }

        else if (distance_in_a2_to_a1_head < distance_in_a1_to_a2_head) { return [[{}, a2.slice_from_to(i2, i2+distance_in_a2_to_a1_head)]].concat(uniques(a1, i1, a2, i2+distance_in_a2_to_a1_head)); }

        else if (distance_in_a1_to_a2_head < distance_in_a2_to_a1_head) { return [[a1.slice_from_to(i1, i1+distance_in_a1_to_a2_head), {}]].concat(uniques(a1, i1+distance_in_a1_to_a2_head, a2, i2)); }

        else if (distance_in_a1_to_a2_head === distance_in_a2_to_a1_head) { return [[{}, a2.slice_from_to(i2, i2+distance_in_a2_to_a1_head)]].concat(uniques(a1, i1, a2, i2+distance_in_a2_to_a1_head)); }

        else { console.log('wtf'); }

    }
}

let uniqs = (a, b) => uniques(a, 0, b, 0).reduce((acc, [from_a1, from_a2]) => [Object.assign(acc[0], from_a1), Object.assign(acc[1], from_a2)], [{}, {}]);



/////////////////////////////////////////////////////////////////////////////
                          ///////    Set up   ///////
/////////////////////////////////////////////////////////////////////////////


var class_categories = _.uniq(_.flatten(Object.values(classes).map(obj => Object.keys(obj))));
if (class_categories.length === 0) { $(".form-check-input[value='class']").remove(); }
else {
    class_categories.forEach(function(key) {
        $(".category").append($("<option></option>").attr("value", key.toString().replace(/\W/g,'')).text(key.toString()));
    });
}

var metadata_spacing = _.object(class_categories.map(category => [category, 0]));
metadata_spacing['system'] = 10;
$('#metadata_spacing').val(metadata_spacing[$('#category_spacing').val()]);


var separate_zscore_by = {{ separate_zscore_by }};


var heatmap = Heatmap(matrix, gene_sets, classes, separate_zscore_by, refresh_genes);



/////////////////////////////////////////////////////////////////////////////
             ///////    Select Gene Set / Genes   ///////
/////////////////////////////////////////////////////////////////////////////

var $chooseGenes = $('#chooseGenes');
var $chooseGeneSets = $('#chooseGeneSets');
var selected_gene_sets = [];

var chooseGeneSets = document.getElementById("chooseGeneSets");
var chooseGeneSetsform = new Awesomplete(chooseGeneSets, {
    list: Object.entries(gene_sets),
    maxItems: 30,
    filter: function(text, input) { return Awesomplete.FILTER_CONTAINS(text, input.match(/[^,]*$/)[0]); },
    item: function(text, input) { return Awesomplete.ITEM(text, input.match(/[^,]*$/)[0]); },
    replace: function(text) {
        var before = this.input.value.match(/^.+,\s*|/)[0];
        this.input.value = before + text + ", ";
    }

});

var findGeneSet = document.getElementById("findGeneSet");
var findGeneSetform = new Awesomplete(findGeneSet, {
    list: Object.entries(gene_sets),
    maxItems: 1000,
    replace: function (text) { this.input.value = text.label; },
    filter: function (text, input) { return text.value.includes(input) || text.value.includes(input.toUpperCase()); }
});

function complete_selection(e) {
    var selected_gene_set_name = e.text.label;
    var selected_genes = e.text.value;
    var selected_gene_set = {'gene_set_name':selected_gene_set_name, 'genes':selected_genes};
    selected_gene_sets.push(selected_gene_set);
    heatmap.restart({'selected_gene_sets_':selected_gene_sets});

    chooseGeneSets.value = chooseGeneSets.value + selected_gene_set_name + ', '; findGeneSet.value = '';
    // window.location.hash = selected_gene_sets.map(gs => gs.gene_set_name).join(',')
};

chooseGeneSets.addEventListener('awesomplete-selectcomplete', complete_selection);
findGeneSet.addEventListener('awesomplete-selectcomplete', complete_selection);



function gene_set_list_change(e) {
    var keys = this.value.split(',').map(s => s.trim()).filter(s => s);
    var filtered_selected_gene_sets = selected_gene_sets.filter(gs => gs.gene_set_name === null || keys.includes(gs.gene_set_name));
    if (!_.isEqual(selected_gene_sets, filtered_selected_gene_sets)) {
        selected_gene_sets = filtered_selected_gene_sets;
        heatmap.restart({'selected_gene_sets_':selected_gene_sets});
    }
}
$chooseGeneSets.on('input', _.debounce(gene_set_list_change, 100));
$chooseGeneSets.on('blur', refresh_genes);

function refresh_genes() {
    selected_gene_sets = heatmap.rendered_gene_sets();
    $chooseGenes.text((str = flatten(selected_gene_sets.map(gene => gene.genes)).filter(n => n).join(", ")) ? (str + ", ") : str);
    $chooseGeneSets.val((str = selected_gene_sets.map(gene => gene.gene_set_name).filter(n => n).join(", ")) ? (str + ", ") : str);
}

function manual_gene_list_edit(e) {

    let flatten = (array) => [].concat.apply([], array);

    selected_gene_sets = heatmap.rendered_gene_sets();
    var old_list = flatten(selected_gene_sets.map(gs => gs.genes));
    var new_list = $(this).text().split(/[^A-Za-z0-9]/).filter(n => n);

    [removed, added] = uniqs(old_list, new_list);

    if (Object.keys(removed).length > 0 || Object.keys(added).length > 0) {

        // walk backwards through selected_gene_sets
        var removed_indices = Object.keys(removed).map(s => parseInt(s));

        var i = old_list.length-1;
        for (var gs_i = selected_gene_sets.length-1; gs_i >= 0; gs_i--) {
            var gene_set_genes_modified = false;
            for (var g_i = selected_gene_sets[gs_i].genes.length-1; g_i >= 0; g_i--) {
                if (removed_indices.includes(i)) { selected_gene_sets[gs_i].genes.splice(g_i,1); gene_set_genes_modified = true;}
                i--;
            }
            if (gene_set_genes_modified) {
                selected_gene_sets.splice(gs_i, 1, ...selected_gene_sets[gs_i].genes.map(gene => {return {'gene_set_name':null, 'genes':[gene]}}));
            }
        }

        var added_indices = Object.keys(added).map(s => parseInt(s));

        var gs_i = 0;
        var g_i = 0;
        var gene_sets_to_explode = [];
        new_list.forEach((gene, i) => {
            if (added_indices.includes(i)) {
                if (g_i === 0 || g_i === selected_gene_sets[gs_i].genes.length) {
                    selected_gene_sets.splice(gs_i,0,{'gene_set_name':null, 'genes':[gene]});
                    gs_i += 1; g_i = 0;
                } else  {
                    selected_gene_sets[gs_i].genes.splice(g_i,0,{'gene_set_name':null, 'genes':[gene]});
                    gene_sets_to_explode.push(gs_i);
                }
            }
        });
        _(gene_sets_to_explode).sortBy(x => -x).forEach(gs_i => {
            selected_gene_sets.splice(gs_i, 1, ...selected_gene_sets[gs_i].genes.map(gene => {return {'gene_set_name':null, 'genes':[gene]}}));
        });

        heatmap.restart({'selected_gene_sets_':selected_gene_sets});

    }

}
$chooseGenes.on('input', _.debounce(manual_gene_list_edit, 100));
$chooseGenes.on('blur', refresh_genes);

/////////////////////////////////////////////////////////////////////////////
                 ///////    Style Figure    ///////
/////////////////////////////////////////////////////////////////////////////

$("#reordering").change(           function () { heatmap.set_reordering(this.checked) });
$("#values input:radio").change(   function () { heatmap.order({'values_': this.value}); heatmap.render(); refresh_genes(); });
$("#sorting input:radio").change(  function () { heatmap.order({'sorting_': this.value}); heatmap.render(); refresh_genes(); $('#euclidian').prop('disabled', (this.value !== 'complete')); });

$("#show_sample_metadata").change( function () { heatmap.style({'show_sample_metadata_': this.checked}); });
$("#show_legends").change(         function () { heatmap.style({'show_legends_': this.checked}); $('#leg_pos input:radio, #darker_legends').prop('disabled', !this.checked); });
$("#leg_pos input:radio").change(  function () { heatmap.style({'legends_position_': this.value}) });
$("#darker_legends").change(       function () { heatmap.style({'darker_legends_': this.checked}) });
$("#colors input:radio").change(   function () { heatmap.style({'color_style_': this.value}); });
$("#negative_color").change(       function () { heatmap.style({'negative_color_': this.value}) });
$("#middle_color").change(         function () { heatmap.style({'middle_color_': this.value}) });
$("#positive_color").change(       function () { heatmap.style({'positive_color_': this.value}) });
$("#cell_spacing").change(         function () { heatmap.render({'spacing_': parseFloat(this.value)}); });
$("#gene_set_spacing").change(     function () { heatmap.spacing({'gene_set': this.value}) });
$("#metadata_spacing").change(     function () { metadata_spacing[$("#category_spacing").val()] = this.value;
                                                 heatmap.spacing({[$("#category_spacing").val()]: this.value}); });

$("#x_labels input:radio").change( function () { heatmap.render({'x_axis_leaves_position_': this.value}); });
$("#x_meta input:radio").change(   function () { heatmap.render({'x_axis_nodes_position_': this.value}); });
$("#x_style input:radio").change(  function () { heatmap.render({'x_axis_style_': this.value}); });
$("#show_x_level_names").change(   function () { heatmap.style({'show_x_level_names_': this.checked}); });
$("#rotation").change(             function () { heatmap.style({'rotation_': parseFloat(this.value)}); });
$("#y_labels input:radio").change( function () { heatmap.render({'y_axis_leaves_position_': this.value}); });
$("#y_meta input:radio").change(   function () { heatmap.render({'y_axis_nodes_position_': this.value}); });
$("#y_style input:radio").change(  function () { heatmap.render({'y_axis_style_': this.value}); });
$("#show_y_level_names").change(   function () { heatmap.style({'show_y_level_names_': this.checked}); });
$("#transpose").click(             function () { heatmap.transpose(); $('input[type="radio"][name="x_style"]').not(':checked').prop("checked", true); $('input[type="radio"][name="y_style"]').not(':checked').prop("checked", true);});


/////////////////////////////////////////////////////////////////////////////
                 ///////    Download SVG    ///////
/////////////////////////////////////////////////////////////////////////////

d3.select("#download").on("click", function() {

    d3.select(this)
      .attr("href", 'data:application/octet-stream;base64,' + btoa(unescape(encodeURIComponent(d3.select("#graph-container").html()))))
      .attr("download", "plot.svg")
})

$('#hide-show').click(function(e) { $('.menu').toggleClass('hidden'); });


    </script>


{% endblock %}

