<commit_before>'use strict'

var _ = require('lodash')
var async = require('async')
var React = require('react')
var ReactDOM = require('react-dom')
var Router = require('react-router')
var Link = Router.Link
var DocumentTitle = require('react-document-title')
var color = require('../../js/color')

var NetworkPanel = require('./NetworkPanel')
var GroupPanel = require('./GroupPanel')
var GenePanel = require('./GenePanel')
var EdgePanel = require('./EdgePanel')
var AnalysisPanel = require('./AnalysisPanel')
var ProgressBar = require('./ProgressBar')
var NetworkControlPanel = require('./NetworkControlPanel')
var TissuesPanel = require('./TissuesPanel')
var EdgeLegend = require('./EdgeLegend')
var LegendPanel = require('./LegendPanel')
var DownloadPanel = require('./DownloadPanel')
var SVGCollection = require('./SVGCollection')

var Cookies = require('cookies-js')
var D3Network = require('../../js/D3Network.js')
var d3fd = require('../../js/d3fd.js')
var AffinityPropagation = require('affinity-propagation')
var color = require('../../js/color')
var htmlutil = require('../htmlutil')
var quicksort = require('../../js/sort/quicksort')

var ZOOM_SCALE = [0.05, 10]

var keyListener = function(e) {

    if (e.altKey && e.keyCode === 78) { // alt+n secret key combo easter egg idbeholdi
        this.state.network && this.state.network.toggleNegative()
        this.setState({
            hasNegatives: !this.state.hasNegatives
        })
    }
}

var network2js = function(network) {

    var time = Date.now()
    
    var js = {
        elements: {
            nodes: _.map(network.genes, function(gene) {
                return {
                    data: gene
                }
            }),
            edges: [],
            allEdges: [],
        },
        edgeValueScales: [[0, 12, 15], [0, -12, -15]],
        edgeColorScales: [['#ffffff', '#000000', '#ff3c00'], ['#ffffff', '#00a0d2', '#7a18ec']],
        buffer: network.buffer
    }

    js.elements.hashNodes = _.indexBy(js.elements.nodes, function(node) {
        return node.data.id
    })

    var numNodes = js.elements.nodes.length

    // convert buffer to an array of Z-scores
    var dataView = new DataView(network.buffer) // DataView is big-endian by default
    var data = new Array(numNodes * (numNodes - 1) / 2) // buffer contains a symmetric matrix
    for (var i = 0; i < data.length; i++) {
        data[i] = (dataView.getUint16(i * 2) - 32768) / 1000
    }

    // get a suitable threshold for showing edges
    var dataCopy = data.slice(0)
    quicksort(dataCopy)
    js.threshold = Math.min(_.last(js.edgeValueScales[0]) - 1, dataCopy[Math.max(0, dataCopy.length - numNodes * 2)])
    // js.threshold = Math.min(_.last(js.edgeValueScales[0]) - 1, dataCopy[Math.max(0, dataCopy.length - numNodes * 10)])

    // keep track of lone genes (not connected to another gene based on threshold)
    var isConnected = new Array(numNodes)
    for (var i = 0; i < isConnected.length; i++) {
        isConnected[i] = false
    }
    // add edges based on threshold
    var i = 0
    for (var i1 = 0; i1 < numNodes - 1; i1++) {
        for (var i2 = i1 + 1; i2 < numNodes; i2++) {
            js.elements.allEdges.push({
                data: {
                    source: js.elements.nodes[i1].data.id,
                    target: js.elements.nodes[i2].data.id,
                    weight: data[i]
                }
            })

            if (Math.abs(data[i]) >= js.threshold) {
                js.elements.edges.push({
                    data: {
                        source: js.elements.nodes[i1].data.id,
                        target: js.elements.nodes[i2].data.id,
                        weight: data[i]
                    }
                })
                isConnected[i1] = true
                isConnected[i2] = true
            }
            i++
        }
    }
    
    // add default groups
    js.elements.groups = [
        {
            name: 'All genes',
            nodes: _.map(js.elements.nodes, function(node) {
                return node.data.id
            }),
            type: 'auto'
        },
    ]

    // add custom groups
    _.forEach(network.groups, function(group) {
        js.elements.groups.push({
            name: group.name,
            index_: group.id,
            nodes: group.genes,
            type: 'custom'
        })
        // add custom group information per gene to allow partial coloring in the ui
        _.forEach(group.genes, function (id) {
            if (js.elements.hashNodes[id].customGroups == undefined) {
                js.elements.hashNodes[id].customGroups = []
            }
            js.elements.hashNodes[id].customGroups.push(group.id)
        })
    })

    console.debug('Network.network2js: %d ms', (Date.now() - time))
    time = Date.now()
    
    // affinity propagation clustering
    if (numNodes > 4) {
        var preference = -32.768 // minimum possible Z-score
        var clusters = AffinityPropagation.getClusters(data, {symmetric: true, preference: preference, damping: 0.8})
        if (clusters.exemplars.length === 1) { // only one cluster found, cluster again with a higher preference
            preference = 'min' // minimum Z-score in data
            clusters = AffinityPropagation.getClusters(data, {symmetric: true, preference: preference, damping: 0.8})        
        }

        // create cluster groups
        var clusterGroups = []
        var clusterHash = {}
        _.forEach(clusters.exemplars, function(exemplar, i) {
            var group = {
                nodes: [],
                type: 'cluster',
                exemplar: js.elements.nodes[exemplar].data.id
            }
            clusterGroups.push(group)
            clusterHash[exemplar] = group
        })
        
        // add genes to clusters
        _.forEach(clusters.clusters, function(exemplar, i) {
            clusterHash[exemplar].nodes.push(js.elements.nodes[i].data.id)
        })

        // add cluster groups to network
        clusterGroups = _.sortBy(clusterGroups, function(group) { return -group.nodes.length })
        _.forEach(clusterGroups, function(group, i) {
            group.name = 'Cluster ' + (i + 1)
            group.index_ = i
        })
        Array.prototype.push.apply(js.elements.groups, clusterGroups)
        
        console.debug('AffinityPropagation: %d ms', (Date.now() - time))
    }
    
    // 'My selection' group has to be last for D3Network to handle it correctly // TODO fix this  
    js.elements.groups.push({
        name: 'My selection',
        nodes: [],
        type: 'auto'
    })

    // console.log('EDGES: ' + JSON.stringify(js.elements.edges))

    return js
}

var AnnotatedGeneRow = React.createClass({

    propTypes: {
        
        data: React.PropTypes.object.isRequired,
        num: React.PropTypes.number
    },
    
    render: function() {

        var data = this.props.data
        var desc = (data.gene.description || 'no description').replace(/\[[^\]]+\]/g, '')
        
        return ( <tr>
                 <td className='text'>
                 <Link className='nodecoration black' target='_blank' title={desc} to={`/gene/${data.gene.id}`}>
                 <SVGCollection.Rectangle className='tablerectangle' title={data.gene.biotype.replace(/_/g, ' ')} fill={color.biotype2color[data.gene.biotype] || color.colors.gnblack} />
                 {data.gene.name}
                 </Link>
                 </td>
                 <td className='text'>
                 <Link className='nodecoration black' title={desc} to={`/gene/${data.gene.name}`}>
                 <span>{desc}</span>
                 </Link>
                 </td>
                 <td style={{textAlign: 'center'}}>TBA</td>
                 <td style={{textAlign: 'center'}}>TBA</td>
                 <td style={{textAlign: 'center'}}><SVGCollection.Annotated /></td>
                 <td style={{textAlign: 'center'}}>
                 <a title={'Open network highlighting ' + data.gene.name} href={GN.urls.networkPage + '0!' + data.gene.name + '|' + this.props.termId + ',0!' + data.gene.name} target='_blank'>
                 <SVGCollection.NetworkIcon />
                 </a>
                 </td>
                 </tr>
        )
    }
})

var GeneTable = React.createClass({

    render: function() {
        var that = this
        var rows = _.map(this.props.genes, function(data, i) {
                return (<AnnotatedGeneRow key={data.gene.id} data={data} num={i} />)
            })
        return (
            <div>
            <div className='gn-term-container-outer' style={{backgroundColor: color.colors.gnwhite, marginTop: '10px'}}>
                <div className='gn-term-container-inner maxwidth' style={{padding: '20px'}}>
                <table className='gn-term-table datatable'>
                <tbody>
                <tr>
                  <th className='tabletextheader' style={{width: '10%'}}>GENE</th>
                  <th className='tabletextheader' style={{width: '60%'}}>DESCRIPTION</th>
                  <th>P-VALUE</th><commit_msg>open gene in new window from network page<commit_after>'use strict'

var _ = require('lodash')
var async = require('async')
var React = require('react')
var ReactDOM = require('react-dom')
var Router = require('react-router')
var Link = Router.Link
var DocumentTitle = require('react-document-title')
var color = require('../../js/color')

var NetworkPanel = require('./NetworkPanel')
var GroupPanel = require('./GroupPanel')
var GenePanel = require('./GenePanel')
var EdgePanel = require('./EdgePanel')
var AnalysisPanel = require('./AnalysisPanel')
var ProgressBar = require('./ProgressBar')
var NetworkControlPanel = require('./NetworkControlPanel')
var TissuesPanel = require('./TissuesPanel')
var EdgeLegend = require('./EdgeLegend')
var LegendPanel = require('./LegendPanel')
var DownloadPanel = require('./DownloadPanel')
var SVGCollection = require('./SVGCollection')

var Cookies = require('cookies-js')
var D3Network = require('../../js/D3Network.js')
var d3fd = require('../../js/d3fd.js')
var AffinityPropagation = require('affinity-propagation')
var color = require('../../js/color')
var htmlutil = require('../htmlutil')
var quicksort = require('../../js/sort/quicksort')

var ZOOM_SCALE = [0.05, 10]

var keyListener = function(e) {

    if (e.altKey && e.keyCode === 78) { // alt+n secret key combo easter egg idbeholdi
        this.state.network && this.state.network.toggleNegative()
        this.setState({
            hasNegatives: !this.state.hasNegatives
        })
    }
}

var network2js = function(network) {

    var time = Date.now()
    
    var js = {
        elements: {
            nodes: _.map(network.genes, function(gene) {
                return {
                    data: gene
                }
            }),
            edges: [],
            allEdges: [],
        },
        edgeValueScales: [[0, 12, 15], [0, -12, -15]],
        edgeColorScales: [['#ffffff', '#000000', '#ff3c00'], ['#ffffff', '#00a0d2', '#7a18ec']],
        buffer: network.buffer
    }

    js.elements.hashNodes = _.indexBy(js.elements.nodes, function(node) {
        return node.data.id
    })

    var numNodes = js.elements.nodes.length

    // convert buffer to an array of Z-scores
    var dataView = new DataView(network.buffer) // DataView is big-endian by default
    var data = new Array(numNodes * (numNodes - 1) / 2) // buffer contains a symmetric matrix
    for (var i = 0; i < data.length; i++) {
        data[i] = (dataView.getUint16(i * 2) - 32768) / 1000
    }

    // get a suitable threshold for showing edges
    var dataCopy = data.slice(0)
    quicksort(dataCopy)
    js.threshold = Math.min(_.last(js.edgeValueScales[0]) - 1, dataCopy[Math.max(0, dataCopy.length - numNodes * 2)])
    // js.threshold = Math.min(_.last(js.edgeValueScales[0]) - 1, dataCopy[Math.max(0, dataCopy.length - numNodes * 10)])

    // keep track of lone genes (not connected to another gene based on threshold)
    var isConnected = new Array(numNodes)
    for (var i = 0; i < isConnected.length; i++) {
        isConnected[i] = false
    }
    // add edges based on threshold
    var i = 0
    for (var i1 = 0; i1 < numNodes - 1; i1++) {
        for (var i2 = i1 + 1; i2 < numNodes; i2++) {
            js.elements.allEdges.push({
                data: {
                    source: js.elements.nodes[i1].data.id,
                    target: js.elements.nodes[i2].data.id,
                    weight: data[i]
                }
            })

            if (Math.abs(data[i]) >= js.threshold) {
                js.elements.edges.push({
                    data: {
                        source: js.elements.nodes[i1].data.id,
                        target: js.elements.nodes[i2].data.id,
                        weight: data[i]
                    }
                })
                isConnected[i1] = true
                isConnected[i2] = true
            }
            i++
        }
    }
    
    // add default groups
    js.elements.groups = [
        {
            name: 'All genes',
            nodes: _.map(js.elements.nodes, function(node) {
                return node.data.id
            }),
            type: 'auto'
        },
    ]

    // add custom groups
    _.forEach(network.groups, function(group) {
        js.elements.groups.push({
            name: group.name,
            index_: group.id,
            nodes: group.genes,
            type: 'custom'
        })
        // add custom group information per gene to allow partial coloring in the ui
        _.forEach(group.genes, function (id) {
            if (js.elements.hashNodes[id].customGroups == undefined) {
                js.elements.hashNodes[id].customGroups = []
            }
            js.elements.hashNodes[id].customGroups.push(group.id)
        })
    })

    console.debug('Network.network2js: %d ms', (Date.now() - time))
    time = Date.now()
    
    // affinity propagation clustering
    if (numNodes > 4) {
        var preference = -32.768 // minimum possible Z-score
        var clusters = AffinityPropagation.getClusters(data, {symmetric: true, preference: preference, damping: 0.8})
        if (clusters.exemplars.length === 1) { // only one cluster found, cluster again with a higher preference
            preference = 'min' // minimum Z-score in data
            clusters = AffinityPropagation.getClusters(data, {symmetric: true, preference: preference, damping: 0.8})        
        }

        // create cluster groups
        var clusterGroups = []
        var clusterHash = {}
        _.forEach(clusters.exemplars, function(exemplar, i) {
            var group = {
                nodes: [],
                type: 'cluster',
                exemplar: js.elements.nodes[exemplar].data.id
            }
            clusterGroups.push(group)
            clusterHash[exemplar] = group
        })
        
        // add genes to clusters
        _.forEach(clusters.clusters, function(exemplar, i) {
            clusterHash[exemplar].nodes.push(js.elements.nodes[i].data.id)
        })

        // add cluster groups to network
        clusterGroups = _.sortBy(clusterGroups, function(group) { return -group.nodes.length })
        _.forEach(clusterGroups, function(group, i) {
            group.name = 'Cluster ' + (i + 1)
            group.index_ = i
        })
        Array.prototype.push.apply(js.elements.groups, clusterGroups)
        
        console.debug('AffinityPropagation: %d ms', (Date.now() - time))
    }
    
    // 'My selection' group has to be last for D3Network to handle it correctly // TODO fix this  
    js.elements.groups.push({
        name: 'My selection',
        nodes: [],
        type: 'auto'
    })

    // console.log('EDGES: ' + JSON.stringify(js.elements.edges))

    return js
}

var PredictedGenesRow = React.createClass({

    propTypes: {
        
        data: React.PropTypes.object.isRequired,
        num: React.PropTypes.number
    },
    
    render: function() {

        var data = this.props.data
        var desc = (data.gene.description || 'no description').replace(/\[[^\]]+\]/g, '')
        
        return ( <tr>
                 <td className='text'>
                 <Link className='nodecoration black' target='_blank' title={desc} to={`/gene/${data.gene.id}`}>
                 <SVGCollection.Rectangle className='tablerectangle' title={data.gene.biotype.replace(/_/g, ' ')} fill={color.biotype2color[data.gene.biotype] || color.colors.gnblack} />
                 {data.gene.name}
                 </Link>
                 </td>
                 <td className='text'>
                 <Link className='nodecoration black' target='_blank' title={desc} to={`/gene/${data.gene.name}`}>
                 <span>{desc}</span>
                 </Link>
                 </td>
                 <td style={{textAlign: 'center'}}>TBA</td>
                 <td style={{textAlign: 'center'}}>TBA</td>
                 <td style={{textAlign: 'center'}}><SVGCollection.Annotated /></td>
                 <td style={{textAlign: 'center'}}>
                 <a title={'Open network highlighting ' + data.gene.name} href={GN.urls.networkPage + '0!' + data.gene.name + '|' + this.props.termId + ',0!' + data.gene.name} target='_blank'>
                 <SVGCollection.NetworkIcon />
                 </a>
                 </td>
                 </tr>
        )
    }
})

var GeneTable = React.createClass({

    render: function() {
        var that = this
        var rows = _.map(this.props.genes, function(data, i) {
                return (<PredictedGenesRow key={data.gene.id} data={data} num={i} />)
            })
        return (
            <div>
            <div className='gn-term-container-outer' style={{backgroundColor: color.colors.gnwhite, marginTop: '10px'}}>
                <div className='gn-term-container-inner maxwidth' style={{padding: '20px'}}>
                <table className='gn-term-table datatable'>
                <tbody>
                <tr>
                  <th className='tabletextheader' style={{width: '10%'}}>GENE</th>
                  <th className='tabletextheader' style={{width: '60%'}}>DESCRIPTION</th>
                  <th>P-VALUE</th>
{'fraction_non_alphanumeric': 0.14363277393879564, 'fraction_numerical': 0.011626631567401558, 'mean_word_length': 1.919001120537858, 'pattern_counts': {'":': 0, '<': 87, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4723153', 'n_tokens_mistral': 5506, 'n_tokens_neox': 5265, 'n_words': 1187}