<commit_before>publishplan = function(options) {

    var _self = {},
        _options = $.extend({
            target: {},
            container: {},
            callback: function() {},
            autoOpen: false,
            modal: true,
            width:600,
        }, options),
        // bunch o variables

        _nameRequired,
        _editType,
        _selectionType,
        _dialog;

    _self.init = function() {
        _options.container.load('/districtmapping/plan/' + PLAN_ID + '/publish/', function() { setUpTarget(); setUpEvents(); _options.callback(); })

        return _self;
    };
    var setUpTarget = function() {
        $('#PlanPublisher').dialog(_options);
        _options.target.click( function() {
            $('#PlanPublisher').dialog('open');
            loadButtons();
        });
    };

    var setUpEvents = function() {
        $('#btnPublishPlan').click( function() {
            notImplemented();
        });

        $('#btnSubmitPlan').click( function() {
            notImplemented();
        });

        $('#btnClose').click(_close);

        var notImplemented = function() {
            $('
This feature has not yet been implemented. Stay tuned.
)').dialog({
                autoOpen:true,
                modal:true,
            });
        };

    };

    var _close = function() {
        $('#PlanPublisher').dialog('close');
    };

    return _self;
};

<commit_msg>Removed extra commas, ie killers.<commit_after>publishplan = function(options) {

    var _self = {},
        _options = $.extend({
            target: {},
            container: {},
            callback: function() {},
            autoOpen: false,
            modal: true,
            width:600
        }, options),
        // bunch o variables

        _nameRequired,
        _editType,
        _selectionType,
        _dialog;

    _self.init = function() {
        _options.container.load('/districtmapping/plan/' + PLAN_ID + '/publish/', function() { setUpTarget(); setUpEvents(); _options.callback(); })

        return _self;
    };
    var setUpTarget = function() {
        $('#PlanPublisher').dialog(_options);
        _options.target.click( function() {
            $('#PlanPublisher').dialog('open');
            loadButtons();
        });
    };

    var setUpEvents = function() {
        $('#btnPublishPlan').click( function() {
            notImplemented();
        });

        $('#btnSubmitPlan').click( function() {
            notImplemented();
        });

        $('#btnClose').click(_close);

        var notImplemented = function() {
            $('
This feature has not yet been implemented. Stay tuned.
)').dialog({
                autoOpen:true,
                modal:true
            });
        };

    };

    var _close = function() {
        $('#PlanPublisher').dialog('close');
    };

    return _self;
};

<|endoftext|>


<commit_before>import React, { Component } from 'react'
import 'whatwg-fetch'

class SearchHistory extends Component {
  render () {
    const { history, selectTx } = this.props
    return

Search History

        {history.map((tx, key) => {
          return

<button className='btn btn-secondary btn-block truncate' onClick={() => { selectTx(tx.id) }}>{tx.id}</button>

        })}

  }
}

class Search extends Component {
  constructor (props) {
    super(props)
    this.state = { txId: '159774426470c983bc32d2cacd609bae652fb82f2373da6024c30fb36364c6ea' }
  }

  changeTx (e) {
    const txId = e.target.value
    this.setState({txId})
  }

  findTx (e) {
    const { txId } = this.state
    const { findTx } = this.props
    e.target.blur()
    findTx(txId)
  }

  render () {
    const { txId } = this.state
    const { isSearching } = this.props
    return

      <input type='text' className='form-control' placeholder='Enter your Tx' value={txId} onChange={this.changeTx.bind(this)} />

        {
          isSearching &&
            <button className='btn btn-primary' disabled type='button'>
               Searching
            </button>
        }
        {
          !isSearching &&
            <button className='btn btn-primary' type='button' onClick={this.findTx.bind(this)}>
               Tx
            </button>
        }

  }
}

class Input extends Component {
  render () {
    const { input, findTx } = this.props
    const { owners_before, fulfills } = input
    return

Owners Before

        {(owners_before).map((ownerBefore, key) => {
          return

{ownerBefore}

        })}

      {
        fulfills &&

Fulfillments

<button className='btn btn-secondary btn-block truncate' onClick={() => { findTx(fulfills.transaction_id) }}>{fulfills.transaction_id}</button>

      }

  }
}

class Output extends Component {
  render () {
    const { output } = this.props
    const { public_keys } = output
    return

Public Keys

        {(public_keys).map((publicKey, key) => {
          return

{publicKey}

        })}

Amount

{output.amount}

  }
}

class Tx extends Component {
  inputs () {
    const { tx, findTx } = this.props
    const { inputs } = tx
    return inputs.map((input, key) => {
      return
<Input input={input} findTx={findTx} />

    })
  }

  outputs () {
    const { tx } = this.props
    const { outputs } = tx
    return outputs.map((output, key) => {
      return
<Output output={output} />

    })
  }

  isTransfer () {
    const { tx } = this.props
    return tx.operation === 'TRANSFER'
  }

  isCreate () {
    return !this.isTransfer()
  }

  render () {
    const { tx, findTx } = this.props
    return

Inputs

        {this.inputs()}

Outputs

        {this.outputs()}

Operation

{tx.operation}

Asset

        {
          this.isTransfer() &&

<button className='btn btn-secondary btn-block truncate' onClick={() => { findTx(tx.asset.id) }}>{tx.asset.id}</button>

        }
        {
          this.isCreate() &&

{ JSON.stringify(tx.asset, null, 2)}

        }

Meta

        { !tx.meta && '-'}
        {
          tx.meta &&

{ JSON.stringify(tx.meta, null, 2)}

        }

  }
}

class App extends Component {
  constructor (props) {
    super(props)
    this.state = { tx: null, isSearching: false, errors: {}, history: [] }
  }

  hasErrors () {
    const { errors } = this.state
    return Object.keys(errors).length > 1
  }

  findTx (txId) {
    this.setState({isSearching: true})
    fetch(
      `https://test.ipdb.io/api/v1/transactions/${txId}`, {
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'X-Requested-With': 'XMLHttpRequest'
        },
        method: 'GET',
        credentials: 'same-origin'
      }
    )
      .then(response => {
        if (!response.ok) {
          response.json().then(json => {
            this.setState({isSearching: false, errors: json})
          })
        } else {
          response.json().then(json => {
            this.setState({
              isSearching: false,
              tx: json,
              history: this.state.history.concat(json),
              errors: {}
            })
          })
        }
      })
      .catch(response => { console.log(response) })
  }

  selectTx (txId) {
    const { history } = this.state
    const selectedTx = history.find(_tx => {
      return _tx.id === txId
    })
    this.setState({
      tx: selectedTx
    })
  }

  render () {
    const { tx, isSearching, history } = this.state
    let result = null
    if (tx) {
      result = <Tx tx={tx} findTx={this.findTx.bind(this)} />
    }
    return (

IPDB-Explorer

            {
              this.hasErrors() &&

                  Couldn't find anything - sorry!

            }
            <Search isSearching={isSearching} findTx={this.findTx.bind(this)} />
            {
              history.length > 0 &&
                <SearchHistory history={history} selectTx={this.selectTx.bind(this)} />
            }

          {result}

    )
  }
}

export default App
<commit_msg>Hmm - IPDB is a bit flaky today<commit_after>import React, { Component } from 'react'
import 'whatwg-fetch'

class SearchHistory extends Component {
  render () {
    const { history, selectTx } = this.props
    return

Search History

        {history.map((tx, key) => {
          return

<button className='btn btn-secondary btn-block truncate' onClick={() => { selectTx(tx.id) }}>{tx.id}</button>

        })}

  }
}

class Search extends Component {
  constructor (props) {
    super(props)
    this.state = { txId: '6cf011722b0a79d77a9ea1f85a2c717e650d9000afd285f1868676cab15f5632' }
  }

  changeTx (e) {
    const txId = e.target.value
    this.setState({txId})
  }

  findTx (e) {
    const { txId } = this.state
    const { findTx } = this.props
    e.target.blur()
    findTx(txId)
  }

  render () {
    const { txId } = this.state
    const { isSearching } = this.props
    return

      <input type='text' className='form-control' placeholder='Enter your Tx' value={txId} onChange={this.changeTx.bind(this)} />

        {
          isSearching &&
            <button className='btn btn-primary' disabled type='button'>
               Searching
            </button>
        }
        {
          !isSearching &&
            <button className='btn btn-primary' type='button' onClick={this.findTx.bind(this)}>
               Tx
            </button>
        }

  }
}

class Input extends Component {
  render () {
    const { input, findTx } = this.props
    const { owners_before, fulfills } = input
    return

Owners Before

        {(owners_before).map((ownerBefore, key) => {
          return

{ownerBefore}

        })}

      {
        fulfills &&

Fulfillments

<button className='btn btn-secondary btn-block truncate' onClick={() => { findTx(fulfills.transaction_id) }}>{fulfills.transaction_id}</button>

      }

  }
}

class Output extends Component {
  render () {
    const { output } = this.props
    const { public_keys } = output
    return

Public Keys

        {(public_keys).map((publicKey, key) => {
          return

{publicKey}

        })}

Amount

{output.amount}

  }
}

class Tx extends Component {
  inputs () {
    const { tx, findTx } = this.props
    const { inputs } = tx
    return inputs.map((input, key) => {
      return
<Input input={input} findTx={findTx} />

    })
  }

  outputs () {
    const { tx } = this.props
    const { outputs } = tx
    return outputs.map((output, key) => {
      return
<Output output={output} />

    })
  }

  isTransfer () {
    const { tx } = this.props
    return tx.operation === 'TRANSFER'
  }

  isCreate () {
    return !this.isTransfer()
  }

  render () {
    const { tx, findTx } = this.props
    return

Inputs

        {this.inputs()}

Outputs

        {this.outputs()}

Operation

{tx.operation}

Asset

        {
          this.isTransfer() &&

<button className='btn btn-secondary btn-block truncate' onClick={() => { findTx(tx.asset.id) }}>{tx.asset.id}</button>

        }
        {
          this.isCreate() &&

{ JSON.stringify(tx.asset, null, 2)}

        }

Meta

        { !tx.meta && '-'}
        {
          tx.meta &&

{ JSON.stringify(tx.meta, null, 2)}

        }

  }
}

class App extends Component {
  constructor (props) {
    super(props)
    this.state = { tx: null, isSearching: false, errors: {}, history: [] }
  }

  hasErrors () {
    const { errors } = this.state
    return Object.keys(errors).length > 1
  }

  findTx (txId) {
    this.setState({isSearching: true})
    fetch(
      `http://localhost:9985/api/v1/transactions/${txId}`, {
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'X-Requested-With': 'XMLHttpRequest'
        },
        method: 'GET',
        credentials: 'same-origin'
      }
    )
      .then(response => {
        if (!response.ok) {
          response.json().then(json => {
            this.setState({isSearching: false, errors: json})
          })
        } else {
          response.json().then(json => {
            this.setState({
              isSearching: false,
              tx: json,
              history: this.state.history.concat(json),
              errors: {}
            })
          })
        }
      })
      .catch(response => { console.log(response) })
  }

  selectTx (txId) {
    const { history } = this.state
    const selectedTx = history.find(_tx => {
      return _tx.id === txId
    })
    this.setState({
      tx: selectedTx
    })
  }

  render () {
    const { tx, isSearching, history } = this.state
    let result = null
    if (tx) {
      result = <Tx tx={tx} findTx={this.findTx.bind(this)} />
    }
    return (

IPDB-Explorer

            {
              this.hasErrors() &&

                  Couldn't find anything - sorry!

            }
            <Search isSearching={isSearching} findTx={this.findTx.bind(this)} />
            {
              history.length > 0 &&
                <SearchHistory history={history} selectTx={this.selectTx.bind(this)} />
            }

          {result}

    )
  }
}

export default App
<|endoftext|>


<commit_before>$.cssHooks.backgroundColor = {
    get: function(elem) {
        if (elem.currentStyle)
            var bg = elem.currentStyle["backgroundColor"];
        else if (window.getComputedStyle)
            var bg = document.defaultView.getComputedStyle(elem,
                null).getPropertyValue("background-color");
        if (bg.search("rgb") == -1)
            return bg;
        else {
            bg = bg.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
            function hex(x) {
                return ("0" + parseInt(x).toString(16)).slice(-2);
            }
            return "#" + hex(bg[1]) + hex(bg[2]) + hex(bg[3]);
        }
    }
};

$.fn.dataTableExt.oSort['numeric_ignore_nan-asc']  = function(x,y) {
    if (isNaN(x) && isNaN(y)) return ((x < y) ? 1 : ((x > y) ?  -1 : 0));

    if (isNaN(x)) return 1;
    if (isNaN(y)) return -1;

    x = parseFloat( x );
    y = parseFloat( y );
    return ((x < y) ? -1 : ((x > y) ?  1 : 0));
};

$.fn.dataTableExt.oSort['numeric_ignore_nan-desc'] = function(x,y) {
    if (isNaN(x) && isNaN(y)) return ((x < y) ? 1 : ((x > y) ?  -1 : 0));

    if (isNaN(x)) return 1;
    if (isNaN(y)) return -1;

    x = parseFloat( x );
    y = parseFloat( y );
    return ((x < y) ?  1 : ((x > y) ? -1 : 0));
};

$.fn.dataTableExt.afnFiltering.push(
    function(oSettings, aData, iDataIndex) {
        var selected_tags = $('.filter_labels h3 span.tag-checked').map(function(k, v) {
                return $(this).text();
            }).get(),
            row_tags = aData[9].split(',');

        if (!selected_tags.length) { // nothing to filter out
            return true;
        } else {
            if (!row_tags.length || (row_tags.length === 1 && row_tags[0] === "")) {
                return false;
            }
            for (var i = 0; i < selected_tags.length; i++) {
                for (var j = 0; j < row_tags.length; j++) {
                    if (selected_tags[i] === row_tags[j]) {
                        return true;
                    }
                }
            }
            return false;
        }
    }
);

$.fn.dataTableExt.afnFiltering.push(
    function(oSettings, aData, iDataIndex) {
        var selected_categories = $('.category_labels h2 span.tag-checked').map(function(k, v) {
                return $(this).css('background-color');
            }).get(),
            row_color = aData[10];
        if (!selected_categories.length) { // nothing to filter out
            return true;
        } else {
            for (var i = 0; i < selected_categories.length; i++) {
                if (selected_categories[i] === row_color) {
                    return true;
                }
            }
        }
        return false;
    }
);

var genomeData = $('#genomeData');
var reset_checkboxes = function(checkbox_settings) {
    var checkboxes = $(".checkbox-row input");
    checkbox_settings.forEach(function(element, index) {
        var checkbox = $(checkboxes[index]);
        checkbox.prop('checked', element).trigger('change');
    });

};
$(document).ready(function() {
    var genome_url = genomeData.data('genome_url');
    var genomeTable = genomeData.dataTable({
        "ajax": genome_url,
        "columns": [
            {"data": "rsid" },
            {"data": "chromosome_position" },
            {"data": "risk_allele" },
            {"data": "genotype" },
            {"data": "disease_trait" },
            {"data": "p_value" },
            {"data": "or_or_beta" },
            {"data": "risk" },
            {"data": "priority", "defaultContent": "0" },
            {"data": "tags", "defaultContent": " "},
            {"data": "color", "defaultContent": ""}
        ],
        "pageLength": 100,
        'bSort': false,
        "aoColumnDefs": [
            { "sType": "numeric_ignore_nan", "aTargets": [ 5, 6 ] },
            { "bSearchable": false, "bVisible": false, "aTargets": [ 8 ] }
        ],
        "columnDefs": [{"targets": [ 8 ], "visible": false, "searchable": false}],
        "order": [[ 8, "desc" ]],
        "processing": true,
        "rowCallback": function(row, data, index) {
            var $row = $(row);
            $row.data('url', encodeURI(data['link'] + '?allele=' + data['genotype']));
            $('td:not(.color-marked)', row).prepend(' ').addClass('color-marked');
        },
        "drawCallback": function( settings ) {
            var rows = $('#genomeData tbody tr');

            // Remove existing event handlers;
            rows.off('click');

            rows.click(function(e) {
                e.preventDefault();
                var row = $(this);
                $('#genomeDataDestination').text('Loading...');
                $.get(row.data('url'), {'ajax':'1'})
                .fail(function(data) {
                    $('#genomeDataDestination').text('error\n'+data.statusCode()+' '+data.statusText+'\n\n'+data.responseText);
                })
                .done(function(data) {
                    $('#genomeDataDestination').html(data);
                });
                return false;
                window.open(row.data('url'), 'Your genome', "height=" + window.screen.height +",width=" + window.screen.width);
            });
            // Fix datatable for fluid container
            genomeData.css('width', '');

            // Ugly way to make table columns show/hide on init as requested
            var checkbox_settings = [
                false, // RSID
                false, // CHROMOSOME POS
                false, // RISK ALLELE
                true, // GENOTYPE
                true, // DISEASE TRAIT
                false, // P VALUE
                false, // OR
                false, // RISK
                false, // PRIORITY
                false, // TAGS
                false  // CATEGORY COLOR
            ];
            $('.table-settings').data('default-values', checkbox_settings);
            reset_checkboxes($('.table-settings').data('default-values'));

        }
    }).columnFilter({ sPlaceHolder: "head:after",
        aoColumns: [
            { type: "number-range" },
            { type: "number-range" },
            { type: "select" },
            { type: "select" },
            { type: "select" },
            { type: "number-range" },
            { type: "number-range" },
            { type: "select" },
            { type: "number-range" },
            { type: "select" },
            { type: "select" },
            { type: "select" }
        ]
    });
});

var progressbar = $('.progress-bar');
var interval;

function updateBar(values) {
    var l = values.received;
    var tot = values.size;

    var perc = (l / tot) * (100.00);
    progressbar.css('width', perc + '%');
    progressbar.text(perc + '%');
    if (values.status === 'done') {
        window.clearInterval(interval);
    }
}

$("form#upload_form").submit(function(e){
    $('form#upload_form input[type=submit]').hide();
    progressbar.parent().show();
    var getProgress = function() {
        $.ajax({
            url: "/progress",
            headers: {"X-Progress-ID": $('#progress-bar').data('upload_id')},
            dataType: 'json',
            success: function(data) {
                updateBar(data);
            }
        });
    };
    interval = window.setInterval(getProgress, 1000);
});

$('.genomeData_advanced').click(function(e) {
    $('#genomeData thead tr.filter-visiblity, .dataTables_length, .dataTables_filter, .table-settings').toggle();
});

$(".checkbox-row input").change(function() {
    var checked = $(this).is(":checked");
    var index = $(this).parent().parent().index();
    $('#genomeData tbody tr').each(function() {
        var $cell = $(this).find("td").eq(index);
        if(checked) {
            $cell.show();
        } else {
            $cell.hide();
        }
        $('td.first', this).removeClass('first');
        $('td:visible:first', this).addClass('first');
    });
    $('#genomeData thead tr').each(function() {
        if(checked) {
            $(this).find("th").eq(index).show();
        } else {
            $(this).find("th").eq(index).hide();
        }
    });
    $('#genomeData tfoot tr').each(function() {
        if(checked) {
            $(this).find("th").eq(index).show();
        } else {
            $(this).find("th").eq(index).hide();
        }
    });
});
$(".table-settings .defaultbutton").click(function() {
    reset_checkboxes($('.table-settings').data('default-values'));
    return false;
});

$('.filter_labels h3 span.label').on('click', function(e) {
    var label = $(e.delegateTarget),
        labelColorOff = label.data('color-off'),
        labelColorOn = label.data('color-on');
    label.toggleClass('tag-checked');
    if (label.hasClass('tag-checked')) {
        label.css('background-color', labelColorOn);
    } else {
        label.css('background-color', labelColorOff);
    }
    genomeData.DataTable().draw();
});

$('.category_labels h2 span.label').on('click', function(e) {
    var category = $(e.delegateTarget);
    category.toggleClass('tag-checked');
    genomeData.DataTable().draw();
});

/**
 * Layout expand
 */
function setLayout() {
    if(window.localStorage['expand'] == 'true') {
        $('.layout').removeClass('container container-fluid').addClass('container-fluid')
        $('[data-toggle=layout-fluid] .glyphicon.glyphicon-resize-full').removeClass('glyphicon-resize-full').addClass('glyphicon-resize-small')
    } else {
        $('.layout').removeClass('container container-fluid').addClass('container')
        $('[data-toggle=layout-fluid] .glyphicon.glyphicon-resize-small').removeClass('glyphicon-resize-small').addClass('glyphicon-resize-full')
    }
}

//setLayout();

$('[data-toggle=layout-fluid]').click(function() {
    window.localStorage['expand'] = String(!(window.localStorage['expand'] == 'true'));
    setLayout();
    return false;
});

if (window.location.href === '/disease/browse/') {
    (function poll(){
        setTimeout(function(){
            if ($('.alert.alert-info').length) {
                location.reload();
                poll();
            }
        }, 1000);
    })();
}

$(function() {
    $(window).scroll(function() {
        var doc_top = $(document).scrollTop();
        var doc_height = $(window).height();
        $('.stay-visible').each(function() {
            var $this = $(this);
            if (!$this.data('original_top')) {
                var elem_offset = $this.offset()
                $this.data('original_top', elem_offset.top);
                $this.css('position', 'absolute').css('top', elem_offset.top).css('left', elem_offset.left);
            }
            var elem_top = $this.data('original_top');
            var elem_bottom = elem_top + $this.height();
            var computed_top;
            if ( (doc_top > elem_top) && (doc_top + doc_height < elem_bottom) ) {
                console.log('1 doc_top: %o', doc_top);
                computed_top = doc_top;
            } else if (doc_top > elem_bottom) {
                computed_top = doc_top + doc_height - $this.height();
                console.log('2 doc_top + doc_height - this.height %o', computed_top);
            } else {
                computed_top = elem_top;
                console.log('3 elem_top %o', computed_top);
            }
            $this.css('top', computed_top);
        });
    });
});
<commit_msg> Floating right pane genome description #1<commit_after>$.cssHooks.backgroundColor = {
    get: function(elem) {
        if (elem.currentStyle)
            var bg = elem.currentStyle["backgroundColor"];
        else if (window.getComputedStyle)
            var bg = document.defaultView.getComputedStyle(elem,
                null).getPropertyValue("background-color");
        if (bg.search("rgb") == -1)
            return bg;
        else {
            bg = bg.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
            function hex(x) {
                return ("0" + parseInt(x).toString(16)).slice(-2);
            }
            return "#" + hex(bg[1]) + hex(bg[2]) + hex(bg[3]);
        }
    }
};

$.fn.dataTableExt.oSort['numeric_ignore_nan-asc']  = function(x,y) {
    if (isNaN(x) && isNaN(y)) return ((x < y) ? 1 : ((x > y) ?  -1 : 0));

    if (isNaN(x)) return 1;
    if (isNaN(y)) return -1;

    x = parseFloat( x );
    y = parseFloat( y );
    return ((x < y) ? -1 : ((x > y) ?  1 : 0));
};

$.fn.dataTableExt.oSort['numeric_ignore_nan-desc'] = function(x,y) {
    if (isNaN(x) && isNaN(y)) return ((x < y) ? 1 : ((x > y) ?  -1 : 0));

    if (isNaN(x)) return 1;
    if (isNaN(y)) return -1;

    x = parseFloat( x );
    y = parseFloat( y );
    return ((x < y) ?  1 : ((x > y) ? -1 : 0));
};

$.fn.dataTableExt.afnFiltering.push(
    function(oSettings, aData, iDataIndex) {
        var selected_tags = $('.filter_labels h3 span.tag-checked').map(function(k, v) {
                return $(this).text();
            }).get(),
            row_tags = aData[9].split(',');

        if (!selected_tags.length) { // nothing to filter out
            return true;
        } else {
            if (!row_tags.length || (row_tags.length === 1 && row_tags[0] === "")) {
                return false;
            }
            for (var i = 0; i < selected_tags.length; i++) {
                for (var j = 0; j < row_tags.length; j++) {
                    if (selected_tags[i] === row_tags[j]) {
                        return true;
                    }
                }
            }
            return false;
        }
    }
);

$.fn.dataTableExt.afnFiltering.push(
    function(oSettings, aData, iDataIndex) {
        var selected_categories = $('.category_labels h2 span.tag-checked').map(function(k, v) {
                return $(this).css('background-color');
            }).get(),
            row_color = aData[10];
        if (!selected_categories.length) { // nothing to filter out
            return true;
        } else {
            for (var i = 0; i < selected_categories.length; i++) {
                if (selected_categories[i] === row_color) {
                    return true;
                }
            }
        }
        return false;
    }
);

var genomeData = $('#genomeData');
var reset_checkboxes = function(checkbox_settings) {
    var checkboxes = $(".checkbox-row input");
    checkbox_settings.forEach(function(element, index) {
        var checkbox = $(checkboxes[index]);
        checkbox.prop('checked', element).trigger('change');
    });

};
$(document).ready(function() {
    var genome_url = genomeData.data('genome_url');
    var genomeTable = genomeData.dataTable({
        "ajax": genome_url,
        "columns": [
            {"data": "rsid" },
            {"data": "chromosome_position" },
            {"data": "risk_allele" },
            {"data": "genotype" },
            {"data": "disease_trait" },
            {"data": "p_value" },
            {"data": "or_or_beta" },
            {"data": "risk" },
            {"data": "priority", "defaultContent": "0" },
            {"data": "tags", "defaultContent": " "},
            {"data": "color", "defaultContent": ""}
        ],
        "pageLength": 100,
        'bSort': false,
        "aoColumnDefs": [
            { "sType": "numeric_ignore_nan", "aTargets": [ 5, 6 ] },
            { "bSearchable": false, "bVisible": false, "aTargets": [ 8 ] }
        ],
        "columnDefs": [{"targets": [ 8 ], "visible": false, "searchable": false}],
        "order": [[ 8, "desc" ]],
        "processing": true,
        "rowCallback": function(row, data, index) {
            var $row = $(row);
            $row.data('url', encodeURI(data['link'] + '?allele=' + data['genotype']));
            $('td:not(.color-marked)', row).prepend(' ').addClass('color-marked');
        },
        "drawCallback": function( settings ) {
            var rows = $('#genomeData tbody tr');

            // Remove existing event handlers;
            rows.off('click');

            rows.click(function(e) {
                e.preventDefault();
                var row = $(this);
                $('#genomeDataDestination').text('Loading...');
                $.get(row.data('url'), {'ajax':'1'})
                .fail(function(data) {
                    $('#genomeDataDestination').text('error\n'+data.statusCode()+' '+data.statusText+'\n\n'+data.responseText);
                })
                .done(function(data) {
                    $('#genomeDataDestination').html(data);
                });
                return false;
                window.open(row.data('url'), 'Your genome', "height=" + window.screen.height +",width=" + window.screen.width);
            });
            // Fix datatable for fluid container
            genomeData.css('width', '');

            // Ugly way to make table columns show/hide on init as requested
            var checkbox_settings = [
                false, // RSID
                false, // CHROMOSOME POS
                false, // RISK ALLELE
                true, // GENOTYPE
                true, // DISEASE TRAIT
                false, // P VALUE
                false, // OR
                false, // RISK
                false, // PRIORITY
                false, // TAGS
                false  // CATEGORY COLOR
            ];
            $('.table-settings').data('default-values', checkbox_settings);
            reset_checkboxes($('.table-settings').data('default-values'));

        }
    }).columnFilter({ sPlaceHolder: "head:after",
        aoColumns: [
            { type: "number-range" },
            { type: "number-range" },
            { type: "select" },
            { type: "select" },
            { type: "select" },
            { type: "number-range" },
            { type: "number-range" },
            { type: "select" },
            { type: "number-range" },
            { type: "select" },
            { type: "select" },
            { type: "select" }
        ]
    });
});

var progressbar = $('.progress-bar');
var interval;

function updateBar(values) {
    var l = values.received;
    var tot = values.size;

    var perc = (l / tot) * (100.00);
    progressbar.css('width', perc + '%');
    progressbar.text(perc + '%');
    if (values.status === 'done') {
        window.clearInterval(interval);
    }
}

$("form#upload_form").submit(function(e){
    $('form#upload_form input[type=submit]').hide();
    progressbar.parent().show();
    var getProgress = function() {
        $.ajax({
            url: "/progress",
            headers: {"X-Progress-ID": $('#progress-bar').data('upload_id')},
            dataType: 'json',
            success: function(data) {
                updateBar(data);
            }
        });
    };
    interval = window.setInterval(getProgress, 1000);
});

$('.genomeData_advanced').click(function(e) {
    $('#genomeData thead tr.filter-visiblity, .dataTables_length, .dataTables_filter, .table-settings').toggle();
});

$(".checkbox-row input").change(function() {
    var checked = $(this).is(":checked");
    var index = $(this).parent().parent().index();
    $('#genomeData tbody tr').each(function() {
        var $cell = $(this).find("td").eq(index);
        if(checked) {
            $cell.show();
        } else {
            $cell.hide();
        }
        $('td.first', this).removeClass('first');
        $('td:visible:first', this).addClass('first');
    });
    $('#genomeData thead tr').each(function() {
        if(checked) {
            $(this).find("th").eq(index).show();
        } else {
            $(this).find("th").eq(index).hide();
        }
    });
    $('#genomeData tfoot tr').each(function() {
        if(checked) {
            $(this).find("th").eq(index).show();
        } else {
            $(this).find("th").eq(index).hide();
        }
    });
});
$(".table-settings .defaultbutton").click(function() {
    reset_checkboxes($('.table-settings').data('default-values'));
    return false;
});

$('.filter_labels h3 span.label').on('click', function(e) {
    var label = $(e.delegateTarget),
        labelColorOff = label.data('color-off'),
        labelColorOn = label.data('color-on');
    label.toggleClass('tag-checked');
    if (label.hasClass('tag-checked')) {
        label.css('background-color', labelColorOn);
    } else {
        label.css('background-color', labelColorOff);
    }
    genomeData.DataTable().draw();
});

$('.category_labels h2 span.label').on('click', function(e) {
    var category = $(e.delegateTarget);
    category.toggleClass('tag-checked');
    genomeData.DataTable().draw();
});

/**
 * Layout expand
 */
function setLayout() {
    if(window.localStorage['expand'] == 'true') {
        $('.layout').removeClass('container container-fluid').addClass('container-fluid')
        $('[data-toggle=layout-fluid] .glyphicon.glyphicon-resize-full').removeClass('glyphicon-resize-full').addClass('glyphicon-resize-small')
    } else {
        $('.layout').removeClass('container container-fluid').addClass('container')
        $('[data-toggle=layout-fluid] .glyphicon.glyphicon-resize-small').removeClass('glyphicon-resize-small').addClass('glyphicon-resize-full')
    }
}

//setLayout();

$('[data-toggle=layout-fluid]').click(function() {
    window.localStorage['expand'] = String(!(window.localStorage['expand'] == 'true'));
    setLayout();
    return false;
});

if (window.location.href === '/disease/browse/') {
    (function poll(){
        setTimeout(function(){
            if ($('.alert.alert-info').length) {
                location.reload();
                poll();
            }
        }, 1000);
    })();
}

$(function() {
    $(window).scroll(function() {
        var doc_top = $(document).scrollTop();
        var doc_height = $(window).height();
        $('.stay-visible').each(function() {
            var $this = $(this);
            if (!$this.data('original_top')) {
                var elem_offset = $this.offset()
                $this.data('original_top', elem_offset.top);
                $this.css('position', 'absolute').css('top', elem_offset.top).css('left', elem_offset.left);
            }
            var elem_top = $this.data('original_top');
            var elem_bottom = elem_top + $this.height();
            var computed_top;
            if (doc_top > elem_bottom) {
                computed_top = doc_top + doc_height - $this.height();
                console.log('2 doc_top + doc_height - this.height %o', computed_top);
            } else {
                computed_top = elem_top;
                console.log('3 elem_top %o', computed_top);
            }
            $this.css('top', computed_top);
        });
    });
});
<|endoftext|>


<commit_before>/**
 * @namespace ajax
 * @summary module to make ajax requests
 */
var ajax = (function() {
    "use strict";
    /**
     * @summary loader HTML element injected into loaderTarget when ajax is running
     * @private
     * @type {Element}
     */
    var loader = document.createElement("div");
    loader.className = "ajax-loader";

    /**
     * @memberof ajax
     * @summary injects the loader element into loaderTarget
     * @private
     * @param target {Element}
     */
    function startLoader(target) {
        target.appendChild(loader);
    }

    /**
     * @memberof ajax
     * @summary removes the loader from loaderTarget
     * @private
     * @param target {Element}
     */
    function stopLoader(target) {
        target.removeChild(loader);
    }

    /**
     * @memberof ajax
     * @summary executes the XMLHttpRequest
     * @private
     * @param c {object} request configuration hash
     */
    function sendRequest(c) {
        var req = new XMLHttpRequest();

        if (c.loaderTarget) {
            startLoader(c.loaderTarget);
        }

        // start XMLHttpRequest
        req.open(c.method, c.url, c.async);
        setRequestHeaders(req, c.setHeaders);

        // request now going through
        req.onreadystatechange = function() {
            // Headers & status are received
            if (req.readyState === 2) {
                if (c.onHeaders) {
                    c.onHeaders(req.getAllResponseHeaders());
                }
            }

            // request is complete and successful
            if (req.readyState === 4) {
                // loader needs to be removed BEFORE callbacks
                if (c.loaderTarget) {
                    stopLoader(c.loaderTarget);
                }

                // process success or failure callbacks
                if (req.status.toString().charAt(0) === "2" && c.onSuccess) {
                    c.onSuccess({
                        response: getResponse(req, c),
                        status: req.status,
                        getHeader: function(name) {
                            return req.getResponseHeader.call(req, name);
                        }
                    });
                } else if (c.onFailure) {
                    c.onFailure({
                        response: getResponse(req, c),
                        status: req.status,
                        getHeader: function(name) {
                            return req.getResponseHeader.call(req, name);
                        }
                    });
                }
            }
        };
        req.send(c.data);
    }

    /**
     * @memberof ajax
     * @summary sets basic headers for the request and sets the additional ones provided by the configuration
     * @private
     * @param req {object} XMLHttpRequest object
     * @param headers {object} additional headers to be injected
     */
    function setRequestHeaders(req, headers) {
        // set default values
        if (!headers["Content-Type"]) {
            headers["Content-Type"] = "application/json;charset=utf-8";
        }

        if (!headers.Accept) {
            headers.Accept = "application/json";
        }

        if (!headers["X-Requested-With"]) {
            headers["X-Requested-With"] = "XMLHttpRequest";
        }

        // get all object (t) keys from own properties
        // inject them as request headers
        Object.keys(headers).forEach(function(header) {
            req.setRequestHeader(header, headers[header]);
        });

        // xhr2 spec
        if (req.responseType) {
            req.responseType = c.responseType; // "arraybuffer", "blob", "document", "json", "text"
        }
    }

    /**
     * @memberof ajax
     * @summary parses the response data based on provided responseType
     * @private
     * @param req {object} XMLHttpRequest object
     * @param c {object} configuration hash
     * @returns the parsed data
     */
    function getResponse(req, c) {
        var r = req.response;

        if (c.responseType && c.responseType === "json" && r !== "") {
            r = JSON.parse(r);
        }

        return r;
    }

    /**
     * @memberof ajax
     * @private
     * @summary verifies content provided by configuration object
     * @param c {object} configuration hash
     * @returns {boolean} result of validation (true|false)
     */
    function validateConfig(c) {
        if (Object.prototype.toString.call(c.url) !== "[object String]") {
            throw new TypeError("ajax: requires a URL string");
        }

        if (c.loaderTarget && c.loaderTarget.nodeType !== 1) {
            throw new TypeError("ajax: 'loaderTarget' is not an HTML element");
        }

        if (Object.prototype.toString.call(c.method) === "[object String]") {
            c.method = c.method.toUpperCase();
            if (!["GET","POST","PUT","DELETE"].some(function(method) {
                return method === c.method;
            })) {
                throw new TypeError("ajax: 'method' must have one of the following values:\nGET\nPOST\nPUT\nDelete");
            }
        } else {
            throw Error("ajax: query 'method' is required");
        }

        if (c.setHeaders && Object.prototype.toString.call(c.setHeaders) !== "[object Object]") {
            throw new TypeError("ajax: 'setHeaders' is not an object");
        }

        if (c.onFailure && Object.prototype.toString.call(c.onFailure) !== "[object Function]") {
            throw new TypeError("ajax: onFailure must be a function");
        }
        if (c.onSuccess && Object.prototype.toString.call(c.onSuccess) !== "[object Function]") {
            throw new TypeError("ajax: onFailure must be a function");
        }
        if (c.onHeaders && Object.prototype.toString.call(c.onHEaders) !== "[object Function]") {
            throw new TypeError("ajax: onFailure must be a function");
        }

        if (c.responseType) {
            if (!["json","text","arraybuffer","document","blob"].some(function(value) {
                    return value === c.responseType;
                })) {
                throw new Error("ajax: responseType must have one of the following value:\njson\ntext\ndocument\nblob\narraybuffer");
            }
        }

        return true;
    }

    /**
     * @memberof ajax
     * @private
     * @summary builds the final request configuration object
     * @param c {object} configuration hash
     * @returns {
     *  {
     *   async: boolean,
     *   data: (object|null),
     *   setHeaders: (object|{}),
     *   method: string,
     *   onFailure: (function|null),
     *   onHeaders: (function|null),
     *   onSuccess: (function|null),
     *   responseType: (string),
     *   url: string,
     *   loaderTarget: (Element|null)
     *   }
     *  }
     */
    function finalizeConfig(c) {
        return {
            async: c.async !== false,
            data: c.data || null,
            setHeaders: c.setHeaders || {},
            method: c.method,
            onFailure: c.onFailure || null,
            onHeaders: c.onHeaders || null,
            onSuccess: c.onSuccess || null,
            responseType: c.responseType || "text",
            url: encodeURI(c.url).replace(/%5B/g, "[").replace(/%5D/g, "]"),
            loaderTarget: c.loaderTarget || null
        };
    }

    function query(c) {
        if (validateConfig(c)) {
            sendRequest(finalizeConfig(c));
        }
    }

    return query;
}());
<commit_msg>removed "loader" feature. it is now in a separate module https://github.com/SebastienDaniel/blockUI/<commit_after>/**
 * @namespace ajax
 * @summary module to make ajax requests
 */
var ajax = (function() {
    "use strict";

    /**
     * @memberof ajax
     * @summary executes the XMLHttpRequest
     * @private
     * @param c {object} request configuration hash
     */
    function sendRequest(c) {
        var req = new XMLHttpRequest();

        // start XMLHttpRequest
        req.open(c.method, c.url, c.async);
        setRequestHeaders(req, c.setHeaders);

        // request now going through
        req.onreadystatechange = function() {
            // Headers & status are received
            if (req.readyState === 2) {
                if (c.onHeaders) {
                    c.onHeaders(req.getAllResponseHeaders());
                }
            }

            // request is complete and successful
            if (req.readyState === 4) {
                // process success or failure callbacks
                if (req.status.toString().charAt(0) === "2" && c.onSuccess) {
                    c.onSuccess({
                        response: getResponse(req, c),
                        status: req.status,
                        getHeader: function(name) {
                            return req.getResponseHeader.call(req, name);
                        }
                    });
                } else if (c.onFailure) {
                    c.onFailure({
                        response: getResponse(req, c),
                        status: req.status,
                        getHeader: function(name) {
                            return req.getResponseHeader.call(req, name);
                        }
                    });
                }
            }
        };
        req.send(c.data);
    }

    /**
     * @memberof ajax
     * @summary sets basic headers for the request and sets the additional ones provided by the configuration
     * @private
     * @param req {object} XMLHttpRequest object
     * @param headers {object} additional headers to be injected
     */
    function setRequestHeaders(req, headers) {
        // set default values
        if (!headers["Content-Type"]) {
            headers["Content-Type"] = "application/json;charset=utf-8";
        }

        if (!headers.Accept) {
            headers.Accept = "application/json";
        }

        if (!headers["X-Requested-With"]) {
            headers["X-Requested-With"] = "XMLHttpRequest";
        }

        // get all object (t) keys from own properties
        // inject them as request headers
        Object.keys(headers).forEach(function(header) {
            req.setRequestHeader(header, headers[header]);
        });

        // xhr2 spec
        if (req.responseType) {
            req.responseType = c.responseType; // "arraybuffer", "blob", "document", "json", "text"
        }
    }

    /**
     * @memberof ajax
     * @summary parses the response data based on provided responseType
     * @private
     * @param req {object} XMLHttpRequest object
     * @param c {object} configuration hash
     * @returns the parsed data
     */
    function getResponse(req, c) {
        var r = req.response;

        if (c.responseType && c.responseType === "json" && r !== "") {
            r = JSON.parse(r);
        }

        return r;
    }

    /**
     * @memberof ajax
     * @private
     * @summary verifies content provided by configuration object
     * @param c {object} configuration hash
     * @returns {boolean} result of validation (true|false)
     */
    function validateConfig(c) {
        if (Object.prototype.toString.call(c.url) !== "[object String]") {
            throw new TypeError("ajax: requires a URL string");
        }

        if (Object.prototype.toString.call(c.method) === "[object String]") {
            c.method = c.method.toUpperCase();
            if (!["GET","POST","PUT","DELETE"].some(function(method) {
                return method === c.method;
            })) {
                throw new TypeError("ajax: 'method' must have one of the following values:\nGET\nPOST\nPUT\nDelete");
            }
        } else {
            throw Error("ajax: query 'method' is required");
        }

        if (c.setHeaders && Object.prototype.toString.call(c.setHeaders) !== "[object Object]") {
            throw new TypeError("ajax: 'setHeaders' is not an object");
        }

        if (c.onFailure && Object.prototype.toString.call(c.onFailure) !== "[object Function]") {
            throw new TypeError("ajax: onFailure must be a function");
        }
        if (c.onSuccess && Object.prototype.toString.call(c.onSuccess) !== "[object Function]") {
            throw new TypeError("ajax: onFailure must be a function");
        }
        if (c.onHeaders && Object.prototype.toString.call(c.onHeaders) !== "[object Function]") {
            throw new TypeError("ajax: onFailure must be a function");
        }

        if (c.responseType) {
            if (!["json","text","arraybuffer","document","blob"].some(function(value) {
                    return value === c.responseType;
                })) {
                throw new Error("ajax: responseType must have one of the following value:\njson\ntext\ndocument\nblob\narraybuffer");
            }
        }

        return true;
    }

    /**
     * @memberof ajax
     * @private
     * @summary builds the final request configuration object
     * @param c {object} configuration hash
     * @returns {
     *  {
     *   async: boolean,
     *   data: (object|null),
     *   setHeaders: (object|{}),
     *   method: string,
     *   onFailure: (function|null),
     *   onHeaders: (function|null),
     *   onSuccess: (function|null),
     *   responseType: (string),
     *   url: string,
     *   loaderTarget: (Element|null)
     *   }
     *  }
     */
    function finalizeConfig(c) {
        return {
            async: c.async !== false,
            data: c.data || null,
            setHeaders: c.setHeaders || {},
            method: c.method,
            onFailure: c.onFailure || null,
            onHeaders: c.onHeaders || null,
            onSuccess: c.onSuccess || null,
            responseType: c.responseType || "text",
            url: encodeURI(c.url).replace(/%5B/g, "[").replace(/%5D/g, "]")
        };
    }

    function query(c) {
        if (validateConfig(c)) {
            sendRequest(finalizeConfig(c));
        }
    }

    return query;
}());

// adding commonJS exports if module.exports is part of the env. otherwise expose as a global module.
if (typeof module !== undefined && typeof module.exports !== undefined) {
    module.exports = ajax;
} else {
    window.ajax = ajax;
}
<|endoftext|>


<commit_before>var dgram = require("dgram");

function FanMaster (numberOfExpectedFans) {
    this.allFans = {}; // Dictionary of fan name -> BigAssFan
    this.connectionOpen = false;
    this.fanPort = 31415;
    this.everyone = "255.255.255.255";
    this.server = dgram.createSocket("udp4");
    this.pollingIntervalForFans = 1000;
    this.dispatchForFans = {}
    this.numberOfExpectedFans = numberOfExpectedFans ? numberOfExpectedFans : 1;
    this.theAllFan = new BigAssFan("ALL", "ALL", this.everyone, this); // If you wanted to broadcast to everyone

    this.onFanConnection = function () {}; // Callback you can register for
    this.onFanFullyUpdated = function () {}; // Callback you can register for

    this.broadcastToFans = function(message) {
        this.sendRaw("<ALL;" + message + ">", this.everyone);
    }.bind(this);

    this.sendRaw = function(message, address) {
        myLogWrapper("Sending: " + message);
        var buffMessage = new Buffer(message);
        this.server.send(buffMessage, 0, buffMessage.length, this.fanPort, address);
    }.bind(this);

    this.rescanForFans = function(){
        this.broadcastToFans("DEVICE;ID;GET");
    }.bind(this)

    this.rescanUntilAllFans = function(){
        var pollForFans = function() {
            if (Object.keys(this.allFans).length < this.numberOfExpectedFans) {
                this.rescanForFans();
            } else {
                clearInterval(id);
            }
        }.bind(this);
        var id = setInterval(pollForFans, this.pollingIntervalForFans);
        pollForFans();
    }.bind(this)

    this.server.on('close', function(msg, rinfo) {
        this.connectionOpen = false;
    }.bind(this));

    handleNewFan = function(msg, address) {
        if (msg[0] == "ALL") {
            return; // Message not addressed to us
        }
        if (msg[1] == "DEVICE" && msg[2] == "ID") {
            var newFan = new BigAssFan(msg[0], msg[3], address, this);
            this.allFans[msg[0]] = newFan;
            this.onFanConnection(newFan);
            newFan.updateAll(function() {
                this.onFanFullyUpdated(newFan)
            }.bind(this));
        } else {
            myLogWrapper("Recieved message from unknown fan - rescanning");
            this.rescanForFans();
        }
    }.bind(this)

    this.server.on("message", (function (msg, rinfo) {
        myLogWrapper("server got: " + msg + " from " + rinfo.address + ":" + rinfo.port);
        var splitMessage = ("" + msg).replace(/<|>|\(|\)/g, "").split(";");
        var fanId = splitMessage.shift();
        if (this.dispatchForFans[fanId]) {
            this.dispatchForFans[fanId](splitMessage);
        } else {
            splitMessage.unshift(fanId)
            handleNewFan(splitMessage, rinfo.address);
        }
    }).bind(this));

    this.server.bind(this.fanPort, (function () {
        this.server.setBroadcast(true);
        this.connectionOpen = true;
        this.rescanUntilAllFans();
    }).bind(this));
}

function BigAssProperty (name, bigAssFan) {
    this.name = name;
    this.bigAssFan = bigAssFan;

    this.allFieldsUpdateQuery = {}
    this.updateCallbacks = {}

    this.createGetField = function(name, query, isSettable, additionalProp, trueOpt, falseOpt, optionalFilter) {
        var toSendOnUpdate = query.concat("GET");
        toSendOnUpdate = additionalProp ? toSendOnUpdate.concat(additionalProp) : toSendOnUpdate;
        this.allFieldsUpdateQuery[name] = toSendOnUpdate;
        this.updateCallbacks[name] = {};

        var privateVarName = '_' + name;
        this[privateVarName] = undefined;

        var setFunction = function(value) {
            // TODO ensure that value fits in "filter"
            if (typeof value == "boolean" && trueOpt && falseOpt) {
                value = value ? trueOpt : falseOpt;
            }
            var successfullyUpdated = false;
            var updateTableId = this.registerUpdateCallback(name, function() {
                successfullyUpdated = true;
                this.unregisterUpdateCallback(updateTableId);
            }.bind(this))

            var toSetProperty = function () {
                this.bigAssFan.send(query.concat("SET", value))
            }.bind(this)

            retryCall(this.bigAssFan.maxRetries, this.bigAssFan.waitTimeOnRetry, toSetProperty, function (){
                return successfullyUpdated;
            });

        }.bind(this)

        Object.defineProperty(this, name, {
            get: function() {
                    return this[privateVarName];
                },
            set: isSettable ? setFunction : undefined
        });

        var handleUpdatedValue = function(value) {
            if (trueOpt) {
                this[privateVarName] = (value == trueOpt) ? true : (value == falseOpt || falseOpt == undefined ? false : value);
            } else {
                this[privateVarName] = value;
            }
            if (this.bigAssFan.onPropertyUpdate) {
                this.bigAssFan.onPropertyUpdate([this.name, name], value);
            }
            for (var key in this.updateCallbacks[name]) {
                this.updateCallbacks[name][key](value);
            }
        }.bind(this)

        var expectedRecieve = additionalProp ? query.concat(additionalProp) : query;
        this.bigAssFan.propertyListeners[this.name + "." + name] = [expectedRecieve, handleUpdatedValue];

    }.bind(this)

    this.update = function(name, callback) {
        var updated = false;
        var id = this.registerUpdateCallback(name, function (){
            updated = true;
            this.unregisterUpdateCallback(id);
            if (callback) {
                callback();
            }
        }.bind(this));

        retryCall(this.bigAssFan.maxRetries, this.bigAssFan.waitTimeOnRetry, function() {
            this.bigAssFan.send(this.allFieldsUpdateQuery[name]);
        }.bind(this), function (){
            return updated;
        });
    }.bind(this)

    this.updateAll = function(callback) {
        var syncCallback = new syncingCallback(this.allFieldsUpdateQuery, callback);
        for (var fieldKey in this.allFieldsUpdateQuery) {
            this.update(fieldKey, syncCallback.callbackToUse);
        }
    }.bind(this)

    this.registerUpdateCallback = function(name, callback) {
        do {
            possibleKey = Math.random()
        } while (this.updateCallbacks[name][possibleKey] != undefined);
        this.updateCallbacks[name][possibleKey] = callback;
        return possibleKey;
    }.bind(this)

    this.unregisterUpdateCallback = function(name, identifier) {
        // return delete this.updateCallbacks[name][identifier];
    }.bind(this)

    this.bigAssFan.propertyTable[name] = this;
}

function BigAssFan (name, id, address, master) {
    this.name = name;
    this.id = id;
    this.address = address;
    this.master = master;
    this.onPropertyUpdate = undefined;

    this.propertyTable = {};
    this.propertyListeners = [];
    this.maxRetries = 10;        // For properties
    this.waitTimeOnRetry = 250;  // For properties - in ms

    this.fan = new BigAssProperty('fan', this);
    this.fan.createGetField('isOn', ['FAN', 'PWR'], true, undefined, "ON", "OFF");
    this.fan.createGetField('speed', ['FAN', 'SPD'], true, 'CURR');
    this.fan.createGetField('min', ['FAN', 'SPD'], true, 'MIN');
    this.fan.createGetField('max', ['FAN', 'SPD'], true, 'MAX');
    this.fan.createGetField('auto', ['FAN', 'AUTO'], true, undefined);
    this.fan.createGetField('whoosh', ['FAN', 'WHOOSH'], true, "STATUS");
    this.fan.createGetField('isSpinningForwards', ['FAN', 'DIR'], true, undefined, "FWD", "BKW"); // TODO: Check backwards string...

    this.light = new BigAssProperty('light', this);
    this.light.createGetField('brightness', ['LIGHT', 'LEVEL'], true, 'CURR');
    this.light.createGetField('min', ['LIGHT', 'LEVEL'], true, 'MIN');
    this.light.createGetField('max', ['LIGHT', 'LEVEL'], true, 'MAX');
    this.light.createGetField('auto', ['LIGHT', 'AUTO'], true, undefined);
    this.light.createGetField('exists', ['DEVICE', 'LIGHT'], false, undefined, "PRESENT"); // Unknown false string.. WAY too lazy to unplug from fan

    this.sensor = new BigAssProperty('room', this);
    this.sensor.createGetField('isOccupied', ['SNSROCC', 'STATUS'], false, undefined, 'OCCUPIED', 'UNOCCUPIED');
    this.sensor.createGetField('minTimeout', ['SNSROCC', 'TIMEOUT'], true, 'MIN');
    this.sensor.createGetField('maxTimeout', ['SNSROCC', 'TIMEOUT'], true, 'MAX');
    this.sensor.createGetField('timeout', ['SNSROCC', 'TIMEOUT'], true, 'CURR');

    this.learn = new BigAssProperty('learn', this);
    this.learn.createGetField('isOn', ['LEARN', 'STATE'], true, undefined, 'ON', 'OFF');
    this.learn.createGetField('minSpeed', ['LEARN', 'MINSPEED'], true);
    this.learn.createGetField('maxSpeed', ['LEARN', 'MAXSPEED'], true);
    this.learn.createGetField('zeroTemp', ['LEARN', 'ZEROTEMP'], true); // ??? Wat.

    this.sleep = new BigAssProperty('sleep', this);
    this.sleep.createGetField('isOn', ['SLEEP', 'STATE'], true, undefined, 'ON', 'OFF');
    this.sleep.createGetField('smartIdealTemp', ['SMARTSLEEP', 'IDEALTEMP'], true);
    this.sleep.createGetField('minSpeed', ['SMARTSLEEP', 'MINSPEED'], true);
    this.sleep.createGetField('maxSpeed', ['SMARTSLEEP', 'MAXSPEED'], true);

    this.device = new BigAssProperty('device', this);
    this.device.createGetField('beeper', ['DEVICE', 'BEEPER'], true, undefined, 'ON', 'OFF');
    this.device.createGetField('indicators', ['DEVICE', 'INDICATORS'], true, undefined, 'ON', 'OFF');
    this.device.createGetField('winterMode', ['WINTERMODE', 'STATE'], true, undefined, 'ON', 'OFF');
    this.device.createGetField('height', ['WINTERMODE', 'HEIGHT'], true);
    this.device.createGetField('token', ['NW', 'TOKEN'], false); // ??? token for what? reference to api.bigassfans.com in packets
    this.device.createGetField('dhcp', ['NW', 'DHCP'], true, undefined, 'ON', 'OFF');
    this.device.createGetField('fw', ['FW', 'FW000003'], false); // What is the FW000003 for?
    this.device.createGetField('broadcastSSID', ['NW', 'SSID'], true);
    this.device.createGetField('isAccessPoint', ['NW', 'AP'], true, 'STATUS', 'ON', 'OFF');

    // Handles incoming messages from the fanMaster
    // Property listners are an array of two values
    //   - (1) : Array of property names to match on response
    //   - (2) : Callback to run
    this.handleMessage = function(message) {
        for (var key in this.propertyListeners) {
            var propertyListener = this.propertyListeners[key]
            if (!message || message.length < propertyListener[0].length) {
                continue;
            }
            var isSubset = true;
            for (var i = 0; i < propertyListener[0].length; i++) {
                if (propertyListener[0][i] != message[i]) {
                    isSubset = false;
                    break;
                }
            };
            if (isSubset) {
                propertyListener[1](message[i]);
            }
        }
    }.bind(this)

    this.master.dispatchForFans[name] = this.handleMessage;
    this.master.dispatchForFans[id] = this.handleMessage;

    this.updateAll = function(callback) {
        var syncCallback = new syncingCallback(this.propertyTable, callback);
        for (var propertyKey in this.propertyTable) {
            this.propertyTable[propertyKey].updateAll(syncCallback.callbackToUse);
        }
    }.bind(this)

    this.update = function(property, callback) {
        this[property].updateAll(callback)
    }.bind(this)

    this.send = function(msg) {
        var toSend = [this.id].concat(msg).join(";");
        this.master.sendRaw("<" + toSend + ">", address);
    }.bind(this)
}

function syncingCallback (tableBeingUpdated, callback) {
    this.callCount = 0;
    this.lengthOfTable = Object.keys(tableBeingUpdated).length;

    var callbackForUser = function() {
        if (++this.callCount == this.lengthOfTable) {
            callback();
        }
    }.bind(this)
    this.callbackToUse = callback ? callbackForUser : undefined;
}

var retryCall = function(maxRetries, waitTimeOnRetry, toCall, isSuccess) {
    var tried = 0;
    var retry = function() {
        if (!isSuccess()) {
            if (++tried >= maxRetries) {
                myLogWrapper("Failed - no more retries left");
                clearInterval(id);
            }
            myLogWrapper("Failed - retrying : " + tried);
            toCall();
        } else {
            clearInterval(id);
        }
    }.bind(this.bigAssFan);
    var id = setInterval(retry, waitTimeOnRetry);
    toCall();
}

var myLogWrapper = function(msg) {
    if (exports.logging) {
        console.log(msg)
    }
};

exports.FanMaster = FanMaster;
exports.logging = false;<commit_msg>Fixing API Calls<commit_after>var dgram = require("dgram");

function FanMaster (numberOfExpectedFans) {
    this.allFans = {}; // Dictionary of fan name -> BigAssFan
    this.connectionOpen = false;
    this.fanPort = 31415;
    this.everyone = "255.255.255.255";
    this.server = dgram.createSocket("udp4");
    this.pollingIntervalForFans = 1000;
    this.dispatchForFans = {}
    this.numberOfExpectedFans = numberOfExpectedFans ? numberOfExpectedFans : 1;
    this.theAllFan = new BigAssFan("ALL", "ALL", this.everyone, this); // If you wanted to broadcast to everyone

    this.onFanConnection = function () {}; // Callback you can register for
    this.onFanFullyUpdated = function () {}; // Callback you can register for

    this.broadcastToFans = function(message) {
        this.sendRaw("<ALL;" + message + ">", this.everyone);
    }.bind(this);

    this.sendRaw = function(message, address) {
        myLogWrapper("Sending: " + message);
        var buffMessage = new Buffer(message);
        this.server.send(buffMessage, 0, buffMessage.length, this.fanPort, address);
    }.bind(this);

    this.rescanForFans = function(){
        this.broadcastToFans("DEVICE;ID;GET");
    }.bind(this)

    this.rescanUntilAllFans = function(){
        var pollForFans = function() {
            if (Object.keys(this.allFans).length < this.numberOfExpectedFans) {
                this.rescanForFans();
            } else {
                clearInterval(id);
            }
        }.bind(this);
        var id = setInterval(pollForFans, this.pollingIntervalForFans);
        pollForFans();
    }.bind(this)

    this.server.on('close', function(msg, rinfo) {
        this.connectionOpen = false;
    }.bind(this));

    handleNewFan = function(msg, address) {
        if (msg[0] == "ALL") {
            return; // Message not addressed to us
        }
        if (msg[1] == "DEVICE" && msg[2] == "ID") {
            var newFan = new BigAssFan(msg[0], msg[3], address, this);
            this.allFans[msg[0]] = newFan;
            this.onFanConnection(newFan);
            newFan.updateAll(function() {
                this.onFanFullyUpdated(newFan)
            }.bind(this));
        } else {
            myLogWrapper("Recieved message from unknown fan - rescanning");
            this.rescanForFans();
        }
    }.bind(this)

    this.server.on("message", (function (msg, rinfo) {
        myLogWrapper("server got: " + msg + " from " + rinfo.address + ":" + rinfo.port);
        var splitMessage = ("" + msg).replace(/<|>|\(|\)/g, "").split(";");
        var fanId = splitMessage.shift();
        if (this.dispatchForFans[fanId]) {
            this.dispatchForFans[fanId](splitMessage);
        } else {
            splitMessage.unshift(fanId)
            handleNewFan(splitMessage, rinfo.address);
        }
    }).bind(this));

    this.server.bind(this.fanPort, (function () {
        this.server.setBroadcast(true);
        this.connectionOpen = true;
        this.rescanUntilAllFans();
    }).bind(this));
}

function BigAssProperty (name, bigAssFan) {
    this.name = name;
    this.bigAssFan = bigAssFan;

    this.allFieldsUpdateQuery = {}
    this.updateCallbacks = {}

    this.createGetField = function(name, query, isSettable, additionalProp, trueOpt, falseOpt, optionalFilter) {
        var toSendOnUpdate = query.concat("GET");
        toSendOnUpdate = additionalProp ? toSendOnUpdate.concat(additionalProp) : toSendOnUpdate;
        this.allFieldsUpdateQuery[name] = toSendOnUpdate;
        this.updateCallbacks[name] = {};

        var privateVarName = '_' + name;
        this[privateVarName] = undefined;

        var setFunction = function(value) {
            // TODO ensure that value fits in "filter"
            if (typeof value == "boolean" && trueOpt && falseOpt) {
                value = value ? trueOpt : falseOpt;
            }
            var successfullyUpdated = false;
            var updateTableId = this.registerUpdateCallback(name, function() {
                successfullyUpdated = true;
                this.unregisterUpdateCallback(updateTableId);
            }.bind(this))

            var toSetProperty = function () {
                this.bigAssFan.send(query.concat("SET", value))
            }.bind(this)

            retryCall(this.bigAssFan.maxRetries, this.bigAssFan.waitTimeOnRetry, toSetProperty, function (){
                return successfullyUpdated;
            });

        }.bind(this)

        Object.defineProperty(this, name, {
            get: function() {
                    return this[privateVarName];
                },
            set: isSettable ? setFunction : undefined
        });

        var handleUpdatedValue = function(value) {
            if (trueOpt) {
                this[privateVarName] = (value == trueOpt) ? true : (value == falseOpt || falseOpt == undefined ? false : value);
            } else {
                this[privateVarName] = value;
            }
            if (this.bigAssFan.onPropertyUpdate) {
                this.bigAssFan.onPropertyUpdate([this.name, name], value);
            }
            for (var key in this.updateCallbacks[name]) {
                this.updateCallbacks[name][key](value);
            }
        }.bind(this)

        var expectedRecieve = additionalProp ? query.concat(additionalProp) : query;
        this.bigAssFan.propertyListeners[this.name + "." + name] = [expectedRecieve, handleUpdatedValue];

    }.bind(this)

    this.update = function(name, callback) {
        var updated = false;
        var id = this.registerUpdateCallback(name, function (){
            updated = true;
            this.unregisterUpdateCallback(id);
            if (callback) {
                callback();
            }
        }.bind(this));

        retryCall(this.bigAssFan.maxRetries, this.bigAssFan.waitTimeOnRetry, function() {
            this.bigAssFan.send(this.allFieldsUpdateQuery[name]);
        }.bind(this), function (){
            return updated;
        });
    }.bind(this)

    this.updateAll = function(callback) {
        var syncCallback = new syncingCallback(this.allFieldsUpdateQuery, callback);
        for (var fieldKey in this.allFieldsUpdateQuery) {
            this.update(fieldKey, syncCallback.callbackToUse);
        }
    }.bind(this)

    this.registerUpdateCallback = function(name, callback) {
        do {
            possibleKey = Math.random()
        } while (this.updateCallbacks[name][possibleKey] != undefined);
        this.updateCallbacks[name][possibleKey] = callback;
        return possibleKey;
    }.bind(this)

    this.unregisterUpdateCallback = function(name, identifier) {
        // return delete this.updateCallbacks[name][identifier];
    }.bind(this)

    this.bigAssFan.propertyTable[name] = this;
}

function BigAssFan (name, id, address, master) {
    this.name = name;
    this.id = id;
    this.address = address;
    this.master = master;
    this.onPropertyUpdate = undefined;

    this.propertyTable = {};
    this.propertyListeners = [];
    this.maxRetries = 10;        // For properties
    this.waitTimeOnRetry = 250;  // For properties - in ms

    this.fan = new BigAssProperty('fan', this);
    this.fan.createGetField('isOn', ['FAN', 'PWR'], true, undefined, "ON", "OFF");
    this.fan.createGetField('speed', ['FAN', 'SPD'], true, 'ACTUAL');
    this.fan.createGetField('min', ['FAN', 'SPD'], true, 'MIN');
    this.fan.createGetField('max', ['FAN', 'SPD'], true, 'MAX');
    this.fan.createGetField('auto', ['FAN', 'AUTO'], true, undefined);
    this.fan.createGetField('whoosh', ['FAN', 'WHOOSH'], true, "STATUS");
    this.fan.createGetField('isSpinningForwards', ['FAN', 'DIR'], true, undefined, "FWD", "REV");

    this.light = new BigAssProperty('light', this);
    this.light.createGetField('brightness', ['LIGHT', 'LEVEL'], true, 'ACTUAL');
    this.light.createGetField('min', ['LIGHT', 'LEVEL'], true, 'MIN');
    this.light.createGetField('max', ['LIGHT', 'LEVEL'], true, 'MAX');
    this.light.createGetField('auto', ['LIGHT', 'AUTO'], true, undefined);
    this.light.createGetField('exists', ['DEVICE', 'LIGHT'], false, undefined, "PRESENT"); // Unknown false string.. WAY too lazy to unplug from fan

    this.sensor = new BigAssProperty('room', this);
    this.sensor.createGetField('isOccupied', ['SNSROCC', 'STATUS'], false, undefined, 'OCCUPIED', 'UNOCCUPIED');
    this.sensor.createGetField('minTimeout', ['SNSROCC', 'TIMEOUT'], true, 'MIN');
    this.sensor.createGetField('maxTimeout', ['SNSROCC', 'TIMEOUT'], true, 'MAX');
    this.sensor.createGetField('timeout', ['SNSROCC', 'TIMEOUT'], true, 'CURR');

    this.learn = new BigAssProperty('learn', this);
    this.learn.createGetField('isOn', ['LEARN', 'STATE'], true, undefined, 'ON', 'OFF');
    this.learn.createGetField('minSpeed', ['LEARN', 'MINSPEED'], true);
    this.learn.createGetField('maxSpeed', ['LEARN', 'MAXSPEED'], true);
    this.learn.createGetField('zeroTemp', ['LEARN', 'ZEROTEMP'], true); // ??? Wat.

    this.sleep = new BigAssProperty('sleep', this);
    this.sleep.createGetField('isOn', ['SLEEP', 'STATE'], true, undefined, 'ON', 'OFF');
    this.sleep.createGetField('smartIdealTemp', ['SMARTSLEEP', 'IDEALTEMP'], true);
    this.sleep.createGetField('minSpeed', ['SMARTSLEEP', 'MINSPEED'], true);
    this.sleep.createGetField('maxSpeed', ['SMARTSLEEP', 'MAXSPEED'], true);

    this.device = new BigAssProperty('device', this);
    this.device.createGetField('beeper', ['DEVICE', 'BEEPER'], true, undefined, 'ON', 'OFF');
    this.device.createGetField('indicators', ['DEVICE', 'INDICATORS'], true, undefined, 'ON', 'OFF');
    this.device.createGetField('winterMode', ['WINTERMODE', 'STATE'], true, undefined, 'ON', 'OFF');
    this.device.createGetField('height', ['WINTERMODE', 'HEIGHT'], true);
    this.device.createGetField('token', ['NW', 'TOKEN'], false); // ??? token for what? reference to api.bigassfans.com in packets
    this.device.createGetField('dhcp', ['NW', 'DHCP'], true, undefined, 'ON', 'OFF');
    this.device.createGetField('fw', ['FW', 'FW000003'], false); // What is the FW000003 for?
    this.device.createGetField('broadcastSSID', ['NW', 'SSID'], true);
    this.device.createGetField('isAccessPoint', ['NW', 'AP'], true, 'STATUS', 'ON', 'OFF');

    // Handles incoming messages from the fanMaster
    // Property listners are an array of two values
    //   - (1) : Array of property names to match on response
    //   - (2) : Callback to run
    this.handleMessage = function(message) {
        for (var key in this.propertyListeners) {
            var propertyListener = this.propertyListeners[key]
            if (!message || message.length < propertyListener[0].length) {
                continue;
            }
            var isSubset = true;
            for (var i = 0; i < propertyListener[0].length; i++) {
                if (propertyListener[0][i] != message[i]) {
                    isSubset = false;
                    break;
                }
            };
            if (isSubset) {
                propertyListener[1](message[i]);
            }
        }
    }.bind(this)

    this.master.dispatchForFans[name] = this.handleMessage;
    this.master.dispatchForFans[id] = this.handleMessage;

    this.updateAll = function(callback) {
        var syncCallback = new syncingCallback(this.propertyTable, callback);
        for (var propertyKey in this.propertyTable) {
            this.propertyTable[propertyKey].updateAll(syncCallback.callbackToUse);
        }
    }.bind(this)

    this.update = function(property, callback) {
        this[property].updateAll(callback)
    }.bind(this)

    this.send = function(msg) {
        var toSend = [this.id].concat(msg).join(";");
        this.master.sendRaw("<" + toSend + ">", address);
    }.bind(this)
}

function syncingCallback (tableBeingUpdated, callback) {
    this.callCount = 0;
    this.lengthOfTable = Object.keys(tableBeingUpdated).length;

    var callbackForUser = function() {
        if (++this.callCount == this.lengthOfTable) {
            callback();
        }
    }.bind(this)
    this.callbackToUse = callback ? callbackForUser : undefined;
}

var retryCall = function(maxRetries, waitTimeOnRetry, toCall, isSuccess) {
    var tried = 0;
    var retry = function() {
        if (!isSuccess()) {
            if (++tried >= maxRetries) {
                myLogWrapper("Failed - no more retries left");
                clearInterval(id);
            }
            myLogWrapper("Failed - retrying : " + tried);
            toCall();
        } else {
            clearInterval(id);
        }
    }.bind(this.bigAssFan);
    var id = setInterval(retry, waitTimeOnRetry);
    toCall();
}

var myLogWrapper = function(msg) {
    if (exports.logging) {
        console.log(msg)
    }
};

exports.FanMaster = FanMaster;
exports.logging = false;
<|endoftext|>


<commit_before>"use strict"

/**
 * @module core-controllers
 */

/**
 * Provides route actions for all requests relative to back end
 * authentication.
 *
 * @class authenticationController
 */

// Module dependencies
var passport = require("passport");
var openVeoAPI = require("@openveo/api");
var pathUtil = process.require("app/server/path.js");
var errors = process.require("app/server/httpErrors.js");
var applicationStorage = openVeoAPI.applicationStorage;

/**
 * Establishes requests authentication using module passport.
 *
 * If authentication fails, an HTTP code 401 is returned to the client.
 * If authentication succeeds, an HTTP code 200 is returned to the
 * client with user information as a JSON Object.
 *
 * @example
 *     {
 *       "id" : 1,
 *       "name" : "OpenVeo",
 *       "email" : "info@veo-labs.com",
 *       "roles" : ["1435066086321"]
 *     }
 *
 * @method authenticateAction
 * @static
 */
module.exports.authenticateAction = function(request, response, next){

  // Use passport to authenticate the request
  passport.authenticate("local", function(error, user, info){

    // An error occurred while authenticating
    // Dispatch the error
    if(error)
      return next(errors.BACK_END_AUTHENTICATION_ERROR);

    // No user was found for the given login / password
    // Send back a 401 Not Authorized
    if(!user)
      return next(errors.BACK_END_AUTHENTICATION_FAILED);

    // Establish a session, authenticate the request
    request.login(user, function(error){
      if(error)
        return next(error);

      return response.status(200).send(user);
    });

  })(request, response, next);
};

/**
 * Removes requests authentication, requests are not authenticated
 * anymore.
 *
 * An HTTP code 200 is returned to the client with no content.
 *
 * @method logoutAction
 * @static
 */
module.exports.logoutAction = function(request, response, next){
  request.logout();
  response.status(200).send();
};

/**
 * Checks if current request is authenticated.
 *
 * If not send back an HTTP code 401 with appropriate page.
 * It just get to the next route action if permission is granted.
 *
 * @method restrictAction
 * @static
 */
module.exports.restrictAction = function(request, response, next){
  var error = errors.BACK_END_UNAUTHORIZED;

  // User is authenticated
  if(request.isAuthenticated()){
    error = errors.BACK_END_FORBIDDEN;

    // Get requested permission for this request
    var permissions = getPermissionByUrl(applicationStorage.getPermissions(), request.url, request.method);

    // No particular permission requested : access granted by default
    // Also always grant access to primary user 0
    if(!permissions || request.user.id == 0)
      return next();

    // Checks if user has permission on this url
    // Iterates through user roles to find if requested permission
    // is part of his privileges
    if(request.user.permissions){
        // Found permission : access granted
        for(var i = 0; i<permissions.length;i++)
          if(request.user.permissions.indexOf(permissions[i])>=0) return next();
    }
  }

  // Not authenticated
  return next(error);

};

/**
 * Gets the tree of groups / permissions and return it as a JSON object.
 *
 * @example
 *     [
 *       {
 *         "label" : "Permissions group",
 *         "permissions" : [
 *           {
 *             "id" : "perm-1",
 *             "name" : "Name of the permission",
 *             "description" : "Description of the permission"
 *           }
 *           ...
 *         ]
 *       }
 *       ...
 *     ]
 *
 * @method getPermissionsAction
 * @static
 */
module.exports.getPermissionsAction = function(request, response, next){
  var permissions = applicationStorage.getPermissions();
  response.send({ permissions : permissions });
};

/**
 * Retrieves, recursively, the permission corresponding to the
 * couple url / http method.
 *
 * @example
 *     var permissions = [
 *       {
 *         "label" : "Permissions group",
 *         "permissions" : [
 *           {
 *             "id" : "perm-1",
 *             "name" : "Name of the permission",
 *             "description" : "Description of the permission",
 *             "paths" : [ "get /publishVideo" ]
 *           }
 *         ]
 *       }
 *     ];
 *     getPermissionByUrl(permissions, "/publishVideo", "GET"); // "perm-1"
 *     getPermissionByUrl(permissions, "/video", "GET"); // null
 *
 * @method getPermissionByUrl
 * @private
 * @static
 * @param {Object} permissions The tree of permissions
 * @param {String} url An url
 * @param {String} httpMethod The http method (POST, GET, PUT, DELETE)
 * @return {String} The permission id if found, null otherwise
 */
function getPermissionByUrl(permissions, url, httpMethod){
  var permissionsList = [];
  for(var i = 0 ; i < permissions.length ; i++){

    // Single permission
    if(permissions[i].id){
      if(permissions[i].paths){
        for(var j = 0 ; j < permissions[i].paths.length ; j++){
          var path = permissions[i].paths[j];
          if(pathUtil.validate(httpMethod + " " + url, path))
            permissionsList.push(permissions[i].id);
        }
      }
    }

    // Group of permissions
    else if(permissions[i].label){
      var permissionId = getPermissionByUrl(permissions[i].permissions, url, httpMethod);

      if(permissionId)
        permissionsList.push(permissionId);
    }
  }
  if(permissionsList.length == 0) return null;
  else return permissionsList;
}<commit_msg>DEBUG: concat array of permissions in case of permission group<commit_after>"use strict"

/**
 * @module core-controllers
 */

/**
 * Provides route actions for all requests relative to back end
 * authentication.
 *
 * @class authenticationController
 */

// Module dependencies
var passport = require("passport");
var openVeoAPI = require("@openveo/api");
var pathUtil = process.require("app/server/path.js");
var errors = process.require("app/server/httpErrors.js");
var applicationStorage = openVeoAPI.applicationStorage;

/**
 * Establishes requests authentication using module passport.
 *
 * If authentication fails, an HTTP code 401 is returned to the client.
 * If authentication succeeds, an HTTP code 200 is returned to the
 * client with user information as a JSON Object.
 *
 * @example
 *     {
 *       "id" : 1,
 *       "name" : "OpenVeo",
 *       "email" : "info@veo-labs.com",
 *       "roles" : ["1435066086321"]
 *     }
 *
 * @method authenticateAction
 * @static
 */
module.exports.authenticateAction = function(request, response, next){

  // Use passport to authenticate the request
  passport.authenticate("local", function(error, user, info){

    // An error occurred while authenticating
    // Dispatch the error
    if(error)
      return next(errors.BACK_END_AUTHENTICATION_ERROR);

    // No user was found for the given login / password
    // Send back a 401 Not Authorized
    if(!user)
      return next(errors.BACK_END_AUTHENTICATION_FAILED);

    // Establish a session, authenticate the request
    request.login(user, function(error){
      if(error)
        return next(error);

      return response.status(200).send(user);
    });

  })(request, response, next);
};

/**
 * Removes requests authentication, requests are not authenticated
 * anymore.
 *
 * An HTTP code 200 is returned to the client with no content.
 *
 * @method logoutAction
 * @static
 */
module.exports.logoutAction = function(request, response, next){
  request.logout();
  response.status(200).send();
};

/**
 * Checks if current request is authenticated.
 *
 * If not send back an HTTP code 401 with appropriate page.
 * It just get to the next route action if permission is granted.
 *
 * @method restrictAction
 * @static
 */
module.exports.restrictAction = function(request, response, next){
  var error = errors.BACK_END_UNAUTHORIZED;

  // User is authenticated
  if(request.isAuthenticated()){
    error = errors.BACK_END_FORBIDDEN;

    // Get requested permission for this request
    var permissions = getPermissionByUrl(applicationStorage.getPermissions(), request.url, request.method);

    // No particular permission requested : access granted by default
    // Also always grant access to primary user 0
    if(!permissions || request.user.id == 0)
      return next();

    // Checks if user has permission on this url
    // Iterates through user roles to find if requested permission
    // is part of his privileges
    console.log(request.user.permissions);
    console.log(permissions);
    if(request.user.permissions){
        // Found permission : access granted
        for(var i = 0; i<permissions.length;i++)
          if(request.user.permissions.indexOf(permissions[i])>=0) return next();
    }
  }

  // Not authenticated
  return next(error);

};

/**
 * Gets the tree of groups / permissions and return it as a JSON object.
 *
 * @example
 *     [
 *       {
 *         "label" : "Permissions group",
 *         "permissions" : [
 *           {
 *             "id" : "perm-1",
 *             "name" : "Name of the permission",
 *             "description" : "Description of the permission"
 *           }
 *           ...
 *         ]
 *       }
 *       ...
 *     ]
 *
 * @method getPermissionsAction
 * @static
 */
module.exports.getPermissionsAction = function(request, response, next){
  var permissions = applicationStorage.getPermissions();
  response.send({ permissions : permissions });
};

/**
 * Retrieves, recursively, the permission corresponding to the
 * couple url / http method.
 *
 * @example
 *     var permissions = [
 *       {
 *         "label" : "Permissions group",
 *         "permissions" : [
 *           {
 *             "id" : "perm-1",
 *             "name" : "Name of the permission",
 *             "description" : "Description of the permission",
 *             "paths" : [ "get /publishVideo" ]
 *           }
 *         ]
 *       }
 *     ];
 *     getPermissionByUrl(permissions, "/publishVideo", "GET"); // "perm-1"
 *     getPermissionByUrl(permissions, "/video", "GET"); // null
 *
 * @method getPermissionByUrl
 * @private
 * @static
 * @param {Object} permissions The tree of permissions
 * @param {String} url An url
 * @param {String} httpMethod The http method (POST, GET, PUT, DELETE)
 * @return {String} The permission id if found, null otherwise
 */
function getPermissionByUrl(permissions, url, httpMethod){
  var permissionsList = [];
  for(var i = 0 ; i < permissions.length ; i++){

    // Single permission
    if(permissions[i].id){
      if(permissions[i].paths){
        for(var j = 0 ; j < permissions[i].paths.length ; j++){
          var path = permissions[i].paths[j];
          if(pathUtil.validate(httpMethod + " " + url, path))
            permissionsList.push(permissions[i].id);
        }
      }
    }

    // Group of permissions
    else if(permissions[i].label){
      var permissionId = getPermissionByUrl(permissions[i].permissions, url, httpMethod);

      if(permissionId)
        permissionsList.concat(permissionId);
    }
  }
  if(permissionsList.length == 0) return null;
  else return permissionsList;
}<|endoftext|>


<commit_before>/** @module api */
import Auth from './auth.js';

/**
 * Fetches a url using a stored access token, signing the user in
 * if no access token exists
 * @param {string} url The url to fetch
 * @return {Promise<Response>} Fetch response object
 */
const authenticatedFetch = async (url) => {
  const accessToken = Auth.getAccessToken();
  if (accessToken) {
    const response = await fetch(url, {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
      },
    });

    if (!response.ok) {
      if (response.status == 401) {
        Auth.signIn();
      } else {
        throw new Error(await response.text());
      }
    }

    return response;
  } else {
    Auth.signIn();
  }
};

// TODO: Add ability to filter by search query, to
//       later implement with navigation views
// https://github.com/GoogleCloudPlatform/healthcare-api-dicom-viewer/issues/6
/**
 * Fetches a list of the user's google cloud project ids recursively
 * @param {string=} pageToken Page token to use for the request
 * @param {Array=} projects Projects fetched from a previous iteration
 * @return {Promise<Array<string>>} List of projects available to the user
 */
const fetchProjects = async (pageToken) => {
  const response = await gapi.client.cloudresourcemanager.projects.list({
    pageToken,
  });
  const data = response.result;
  const projects = data.projects;

  // If next page token is present in the response, fetch again and
  // push result to the current project list
  if (data.nextPageToken) {
    projects.push(...await fetchProjects(data.nextPageToken));
  }

  // Ensure only the first iteration calls .map
  // by checking if pageToken was passed
  if (pageToken) {
    return projects;
  }
  return projects.map((project) => project.projectId);
};

/**
 * Fetches a list of the possible location ids for a given project
 * @param {string} projectId Project id to search locations for
 * @return {Promise<Array<string>>} List of locations available for project
 */
const fetchLocations = async (projectId) => {
  const data = await gapi.client.healthcare.projects.locations.list({
    name: `projects/${projectId}`,
  });

  // Return a list of location Id's
  return data.result.locations.map((location) => location.locationId);
};

/**
 * Fetches a list of the datasets in a project location
 * @param {string} projectId Project id
 * @param {string} location Location
 * @return {Promise<Array<string>>} List of datasets available
 */
const fetchDatasets = async (projectId, location) => {
  // TODO: Handle page tokens
  const data = await gapi.client.healthcare.projects.locations.datasets.list({
    parent: `projects/${projectId}/locations/${location}`,
  });

  // Return a list of datasets by only using content of string after last '/'
  return data.result.datasets
      .map((dataset) => dataset.name.split('/').slice(-1)[0]);
};

/**
 * Fetches a list of the dicomStores in a dataset
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @return {Promise<Array<string>>} List of dicomStores available
 */
const fetchDicomStores = async (projectId, location, dataset) => {
  // TODO: Handle page tokens
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.list({
        parent: `projects/${projectId}/locations/${location}/` +
          `datasets/${dataset}`,
      });

  // Return a list of dicomStores by only using content of string after last '/'
  return data.result.dicomStores.map((dicomStore) =>
    dicomStore.name.split('/').slice(-1)[0]);
};

/**
 * Fetches a list of studies in a dicom store
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @param {string} dicomStore Dicom Store
 * @return {Promise<Array<Object>>} List of studies in the dicom store
 */
const fetchStudies =
    async (projectId, location, dataset, dicomStore) => {
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.searchForStudies({
        parent: `projects/${projectId}/locations/${location}/` +
    `datasets/${dataset}/dicomStores/${dicomStore}`,
        dicomWebPath: 'studies',
      });

  return data.result;
};

/**
 * Fetches a list of series in a study
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @param {string} dicomStore Dicom Store
 * @param {string} studyId Study UID
 * @return {Promise<Array<Object>>} List of series in the study
 */
const fetchSeries =
    async (projectId, location, dataset, dicomStore, studyId) => {
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.studies.searchForSeries({
        parent: `projects/${projectId}/locations/${location}/` +
    `datasets/${dataset}/dicomStores/${dicomStore}`,
        dicomWebPath: `studies/${studyId}/series`,
      });

  return data.result;
};

export {authenticatedFetch, fetchProjects, fetchLocations, fetchDatasets,
  fetchDicomStores, fetchStudies, fetchSeries};
<commit_msg>Refactored fetchProjects to avoid confusion on return type<commit_after>/** @module api */
import Auth from './auth.js';

/**
 * Fetches a url using a stored access token, signing the user in
 * if no access token exists
 * @param {string} url The url to fetch
 * @return {Promise<Response>} Fetch response object
 */
const authenticatedFetch = async (url) => {
  const accessToken = Auth.getAccessToken();
  if (accessToken) {
    const response = await fetch(url, {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
      },
    });

    if (!response.ok) {
      if (response.status == 401) {
        Auth.signIn();
      } else {
        throw new Error(await response.text());
      }
    }

    return response;
  } else {
    Auth.signIn();
  }
};

// TODO: Add ability to filter by search query, to
//       later implement with navigation views
// https://github.com/GoogleCloudPlatform/healthcare-api-dicom-viewer/issues/6
/**
 * Fetches a list of the user's google cloud project ids
 * @return {Promise<Array<string>>} List of project ids available to the user
 */
const fetchProjects = async () => {
  /**
   * Fetches a list of the user's google cloud project recursively
   * @param {string=} pageToken Page token to use for the request
   * @return {Promise<Array<Object>>} List of projects available to the user
   */
  const fetchProjectsInternal = async (pageToken) => {
    const response = await gapi.client.cloudresourcemanager.projects.list({
      pageToken,
    });
    const data = response.result;
    const projects = data.projects;

    // If next page token is present in the response, fetch again and
    // push result to the current project list
    if (data.nextPageToken) {
      projects.push(...await fetchProjectsInternal(data.nextPageToken));
    }

    return projects;
  };

  const projects = await fetchProjectsInternal();
  return projects.map((project) => project.projectId);
};

/**
 * Fetches a list of the possible location ids for a given project
 * @param {string} projectId Project id to search locations for
 * @return {Promise<Array<string>>} List of locations available for project
 */
const fetchLocations = async (projectId) => {
  const data = await gapi.client.healthcare.projects.locations.list({
    name: `projects/${projectId}`,
  });

  // Return a list of location Id's
  return data.result.locations.map((location) => location.locationId);
};

/**
 * Fetches a list of the datasets in a project location
 * @param {string} projectId Project id
 * @param {string} location Location
 * @return {Promise<Array<string>>} List of datasets available
 */
const fetchDatasets = async (projectId, location) => {
  // TODO: Handle page tokens
  const data = await gapi.client.healthcare.projects.locations.datasets.list({
    parent: `projects/${projectId}/locations/${location}`,
  });

  // Return a list of datasets by only using content of string after last '/'
  return data.result.datasets
      .map((dataset) => dataset.name.split('/').slice(-1)[0]);
};

/**
 * Fetches a list of the dicomStores in a dataset
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @return {Promise<Array<string>>} List of dicomStores available
 */
const fetchDicomStores = async (projectId, location, dataset) => {
  // TODO: Handle page tokens
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.list({
        parent: `projects/${projectId}/locations/${location}/` +
          `datasets/${dataset}`,
      });

  // Return a list of dicomStores by only using content of string after last '/'
  return data.result.dicomStores.map((dicomStore) =>
    dicomStore.name.split('/').slice(-1)[0]);
};

/**
 * Fetches a list of studies in a dicom store
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @param {string} dicomStore Dicom Store
 * @return {Promise<Array<Object>>} List of studies in the dicom store
 */
const fetchStudies =
    async (projectId, location, dataset, dicomStore) => {
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.searchForStudies({
        parent: `projects/${projectId}/locations/${location}/` +
    `datasets/${dataset}/dicomStores/${dicomStore}`,
        dicomWebPath: 'studies',
      });

  return data.result;
};

/**
 * Fetches a list of series in a study
 * @param {string} projectId Project ID
 * @param {string} location Location
 * @param {string} dataset Dataset
 * @param {string} dicomStore Dicom Store
 * @param {string} studyId Study UID
 * @return {Promise<Array<Object>>} List of series in the study
 */
const fetchSeries =
    async (projectId, location, dataset, dicomStore, studyId) => {
  const data = await gapi.client.healthcare.projects.locations.datasets
      .dicomStores.studies.searchForSeries({
        parent: `projects/${projectId}/locations/${location}/` +
    `datasets/${dataset}/dicomStores/${dicomStore}`,
        dicomWebPath: `studies/${studyId}/series`,
      });

  return data.result;
};

export {authenticatedFetch, fetchProjects, fetchLocations, fetchDatasets,
  fetchDicomStores, fetchStudies, fetchSeries};
<|endoftext|>