<commit_before>import path from 'path';
import chromedriver from 'chromedriver';
import webdriver from 'selenium-webdriver';
import {expect} from 'chai';
import electronPath from 'electron-prebuilt';

import {APP_STATUS_CONSTANTS, ENGINES, USER_INPUT_FIELDS} from '../app/constants/constants';
import {CREDENTIALS} from './credentials.js';

// import styles to use for tests
import logoStyles from '../app/components/Settings/EngineSelector/EngineSelector.css';
import btnStyles from '../app/components/Settings/ConnectButton/ConnectButton.css';

chromedriver.start(); // on port 9515
process.on('exit', chromedriver.stop);

const delay = time => new Promise(resolve => setTimeout(resolve, time));

describe('main window', function spec() {
    this.timeout(5000);

    before(async () => {
        await delay(1000); // wait chromedriver start time
        this.driver = new webdriver.Builder()
        .usingServer('http://localhost:9515')
        .withCapabilities({
            chromeOptions: {
                binary: electronPath,
                args: [`app=${path.resolve()}`]
            }
        })
        .forBrowser('electron')
        .build();
    });

    // TODO: simplify the code for these grabbing functions

    // grab group of elements
    const getLogos = () => this.driver.findElements(
        webdriver.By.className(logoStyles.logo)
    );
    const getInputs = () => this.driver.findElements(
        webdriver.By.xpath('//input')
    );

    // grab specific element
    const getLogo = (dialect) => this.driver.findElement(
        webdriver.By.id(`test-logo-${dialect}`)
    );
    const getInputField = (credential) => this.driver.findElement(
        webdriver.By.id(`test-input-${credential}`)
    );
    const getConnectBtn = () => this.driver.findElement(
        webdriver.By.id('test-connect-button')
    );
    const getDatabaseDropdown = () => this.driver.findElement(
        webdriver.By.id('test-database-dropdown')
    );
    const getDatabaseOptions = () => this.driver.findElement(
        webdriver.By.css('.Select-option')
    );
    const getTables = () => this.driver.findElement(
        webdriver.By.id('test-tables')
    );
    const getLogs = () => this.driver.findElement(
        webdriver.By.id('test-logs')
    );
    const getErrorMessage = () => this.driver.findElement(
        webdriver.By.id('test-error-message')
    );

    // grab property of element
    const getIdOf = (element) => element.getAttribute('id');
    const getClassOf = (element) => element.getAttribute('class');

    // user inputs
    async function fillInputs(testedDialect) {
        // enter credentials into the fields
        USER_INPUT_FIELDS[testedDialect].forEach(credential => {
            getInputField(credential)
            .then(input => input.sendKeys(CREDENTIALS['local'][credential]));
        });
    }
    async function wrongInputs(testedDialect) {
        // enter credentials into the fields
        USER_INPUT_FIELDS[testedDialect].forEach(credential => {
            getInputField(credential)
            .then(input => input.sendKeys('blah'));
        });
    }

    // TODO: replace delay times with a functions that waits for a change

    it('should open window',
    async () => {

        const title = await this.driver.getTitle();

        expect(title).to.equal('Plotly Desktop Connector');

    });

    it('should display five available dialect logos',
    async () => {

        const logos = await getLogos();

        expect(logos.length).to.equal(5);

    });

    it('should enter text into the text box',
    async () => {

        const inputs = await getInputs();
        const textinput = 'this is an input';

        inputs[0].sendKeys(textinput);
        expect(await inputs[0].getAttribute('value')).to.equal(textinput);

    });

    it('should clear input values if a new database dialect is selected',
    async () => {

        const inputs = await getInputs();
        const logos = await getLogos();

        logos[1].click();
        expect(await inputs[0].getAttribute('value')).to.equal('');

    });

    it('should have an initial state of disconnected',
    async () => {

        const expectedClass = `test-${APP_STATUS_CONSTANTS.DISCONNECTED}`;
        const btn = await getConnectBtn();

        const testClass = await getClassOf(btn);
        expect(testClass).to.contain(expectedClass);

    });

    it('should have updated the config with new dialect value',
    async () => {

        const expectedClass = 'test-consistent-state';
        const testedDialect = ENGINES.MYSQL;
        const logo = await getLogo(testedDialect);

        await logo.click()
        .then(await delay(500));
        const testClass = await getClassOf(logo);
        expect(testClass).to.contain(expectedClass);

    });

    it('should connect to the database using the inputs and selected dialect',
    async () => {

        const expectedClass = `test-${APP_STATUS_CONSTANTS.CONNECTED}`;
        const testedDialect = ENGINES.MYSQL;
        const btn = await getConnectBtn();

        // click on the evaluated dialect logo
        fillInputs(testedDialect)
        .then(await delay(500))
        // click to connect
        .then(await btn.click())
        .then(await delay(1000));
        const testClass = await getClassOf(btn);
        expect(testClass).to.contain(expectedClass);

    });

    it('should show the database selector after connection',
    async () => {

        const expectedClass = 'test-connected';
        const databaseDropdown = getDatabaseDropdown();

        const testClass = await getClassOf(databaseDropdown);
        expect(testClass).to.contain(expectedClass);

    });

    it('should show a log with one logged item in the log',
    async () => {

        const expectedClass = 'test-1-entries';
        const logs = await getLogs();

        const testClass = await getClassOf(logs);
        expect(testClass).to.contain(expectedClass);

    });

    it('should not show a table preview',
    async () => {

        expect(await getTables()).to.throw(/NoSuchElementError/);

    });

    it('should show table previews when database is selected from dropdown',
    async () => {

        // TODO: debug how to get options from react-select
        // TODO: debug how to set a value into the react-select item
        const databaseDropdown = await getDatabaseDropdown();
        await databaseDropdown.click()
        .then(await getDatabaseOptions());
        expect(await getDatabaseOptions().getAttribute('value')).to.equal('[]');

    });

    it('should disconnect when the disconnect button is pressed',
    async () => {

        const expectedClass = `test-${APP_STATUS_CONSTANTS.DISCONNECTED}`;
        const btn = await getConnectBtn();

        await btn.click()
        .then(await delay(1000));
        const testClass = await getClassOf(btn);
        expect(testClass).to.contain(expectedClass);

    });

    it('should display an error when wrong credentials are enetered and the ' +
    'button state should be disconnected and the log should not update',
    async () => {

        const expectedClass = `test-${APP_STATUS_CONSTANTS.ERROR}`;
        const testedDialect = ENGINES.MYSQL;
        const btn = await getConnectBtn();

        wrongInputs(testedDialect)
        .then(await delay(500))
        // click to connect
        .then(await btn.click())
        .then(await delay(1000));

        const errorMessage = await getErrorMessage();
        const testClass = await getClassOf(btn);
        expect(testClass.includes(expectedClass)).to.equal(true);
        expect(await errorMessage.getText()).to.have.length.above(0);

    });

    after(async () => {
        await this.driver.quit();
    });

});
<commit_msg>:cow: clean up element grabbing functions<commit_after>import path from 'path';
import chromedriver from 'chromedriver';
import webdriver from 'selenium-webdriver';
import {expect} from 'chai';
import electronPath from 'electron-prebuilt';

import {APP_STATUS_CONSTANTS, ENGINES, USER_INPUT_FIELDS} from '../app/constants/constants';
import {CREDENTIALS} from './credentials.js';

// import styles to use for tests
import logoStyles from '../app/components/Settings/EngineSelector/EngineSelector.css';
import btnStyles from '../app/components/Settings/ConnectButton/ConnectButton.css';

chromedriver.start(); // on port 9515
process.on('exit', chromedriver.stop);

const delay = time => new Promise(resolve => setTimeout(resolve, time));

describe('main window', function spec() {
    this.timeout(5000);

    before(async () => {
        await delay(1000); // wait chromedriver start time
        this.driver = new webdriver.Builder()
        .usingServer('http://localhost:9515')
        .withCapabilities({
            chromeOptions: {
                binary: electronPath,
                args: [`app=${path.resolve()}`]
            }
        })
        .forBrowser('electron')
        .build();

        const findels = (args) => this.driver.findElements(args);
        const findel = (args) => this.driver.findElement(args);

        const byClass = (args) => webdriver.By.className(args);
        const byId = (args) => webdriver.By.id(args);
        const byCss = (args) => webdriver.By.css(args);
        const byPath = (args) => webdriver.By.xpath(args);

        // grab group of elements
        this.getLogos = () => findels(byClass(logoStyles.logo));
        this.getInputs = () => findels(byPath('//input'));

        // grab specific element
        this.getLogo = (dialect) => findel(
            byId(`test-logo-${dialect}`)
        );

        this.getInputField = (credential) => findel(
            byId(`test-input-${credential}`)
        );

        this.getConnectBtn = () => findel(
            byId('test-connect-button')
        );

        this.getDatabaseDropdown = () => findel(
            byId('test-database-dropdown')
        );

        this.getDatabaseOptions = () => findel(
            byCss('.Select-option')
        );

        this.getTables = () => findel(
            byId('test-tables')
        );

        this.getLogs = () => findel(
            byId('test-logs')
        );

        this.getErrorMessage = () => findel(
            byId('test-error-message')
        );

        // user inputs
        this.fillInputs = async (testedDialect) => {
            USER_INPUT_FIELDS[testedDialect].forEach(credential => {
                this.getInputField(credential)
                .then(input => input.sendKeys(CREDENTIALS['local'][credential]));
            });
        };

        this.wrongInputs = async (testedDialect) => {
            USER_INPUT_FIELDS[testedDialect].forEach(credential => {
                this.getInputField(credential)
                .then(input => input.sendKeys('blah'));
            });
        };

    });

    // grab property of element
    const getClassOf = (element) => element.getAttribute('class');

    // TODO: replace delay times with a functions that waits for a change

    it('should open window',
    async () => {

        const title = await this.driver.getTitle();

        expect(title).to.equal('Plotly Desktop Connector');

    });

    it('should display five available dialect logos',
    async () => {

        const logos = await this.getLogos();

        expect(logos.length).to.equal(5);

    });

    it('should enter text into the text box',
    async () => {

        const inputs = await this.getInputs();
        const textinput = 'this is an input';

        inputs[0].sendKeys(textinput);
        expect(await inputs[0].getAttribute('value')).to.equal(textinput);

    });

    it('should clear input values if a new database dialect is selected',
    async () => {

        const inputs = await this.getInputs();
        const logos = await this.getLogos();

        logos[1].click();
        expect(await inputs[0].getAttribute('value')).to.equal('');

    });

    it('should have an initial state of disconnected',
    async () => {

        const expectedClass = `test-${APP_STATUS_CONSTANTS.DISCONNECTED}`;
        const btn = await this.getConnectBtn();

        const testClass = await getClassOf(btn);
        expect(testClass).to.contain(expectedClass);

    });

    it('should have updated the config with new dialect value',
    async () => {

        const expectedClass = 'test-consistent-state';
        const testedDialect = ENGINES.MYSQL;
        const logo = await this.getLogo(testedDialect);

        await logo.click()
        .then(await delay(500));
        const testClass = await getClassOf(logo);
        expect(testClass).to.contain(expectedClass);

    });

    it('should connect to the database using the inputs and selected dialect',
    async () => {

        const expectedClass = `test-${APP_STATUS_CONSTANTS.CONNECTED}`;
        const testedDialect = ENGINES.MYSQL;
        const btn = await this.getConnectBtn();

        // click on the evaluated dialect logo
        this.fillInputs(testedDialect)
        .then(await delay(500))
        // click to connect
        .then(await btn.click())
        .then(await delay(1000));
        const testClass = await getClassOf(btn);
        expect(testClass).to.contain(expectedClass);

    });

    it('should show the database selector after connection',
    async () => {

        const expectedClass = 'test-connected';
        const databaseDropdown = this.getDatabaseDropdown();

        const testClass = await getClassOf(databaseDropdown);
        expect(testClass).to.contain(expectedClass);

    });

    it('should show a log with one logged item in the log',
    async () => {

        const expectedClass = 'test-1-entries';
        const logs = await this.getLogs();

        const testClass = await getClassOf(logs);
        expect(testClass).to.contain(expectedClass);

    });

    it('should not show a table preview',
    async () => {

        expect(await this.getTables()).to.throw(/NoSuchElementError/);

    });

    it('should show table previews when database is selected from dropdown',
    async () => {

        // TODO: debug how to get options from react-select
        // TODO: debug how to set a value into the react-select item
        const databaseDropdown = await this.getDatabaseDropdown();
        await databaseDropdown.click();

        expect(await this.getDatabaseOptions().getAttribute('value')).to.equal('[]');

    });

    it('should disconnect when the disconnect button is pressed',
    async () => {

        const expectedClass = `test-${APP_STATUS_CONSTANTS.DISCONNECTED}`;
        const btn = await this.getConnectBtn();

        await btn.click()
        .then(await delay(1000));
        const testClass = await getClassOf(btn);
        expect(testClass).to.contain(expectedClass);

    });

    it('should display an error when wrong credentials are enetered and the ' +
    'button state should be disconnected and the log should not update',
    async () => {

        const expectedClass = `test-${APP_STATUS_CONSTANTS.ERROR}`;
        const testedDialect = ENGINES.MYSQL;
        const btn = await this.getConnectBtn();

        this.wrongInputs(testedDialect)
        .then(await delay(500))
        // click to connect
        .then(await btn.click())
        .then(await delay(1000));

        const errorMessage = await this.getErrorMessage();
        const testClass = await getClassOf(btn);
        expect(testClass.includes(expectedClass)).to.equal(true);
        expect(await errorMessage.getText()).to.have.length.above(0);

    });

    after(async () => {
        await this.driver.quit();
    });

});
<|endoftext|>


<commit_before>
// observer/listener pattern handling selection event on plots
function CaseSelectObserver() {
    this.funcs = {};
    this.caseId = null;
}
CaseSelectObserver.prototype = {
    subscribe: function(listener,func,fire) {
        this.funcs[listener] = func;
        if (fire)
            func.call(window,this.caseId);
    },
    fireSelection: function(caseId,source,targets,forceFire) {
        if (!forceFire&&compareAssociativeArrays(caseId,this.caseId)) return;
        //$('#submit-patient-btn').attr("disabled", caseId==null);

        this.caseId = caseId;
        for (var listener in this.funcs) {
            if (targets!=null&&(targets!=listener&&!targets[listener])) continue;
            if (listener==source) continue;
            var func = this.funcs[listener];
            func.call(window,caseId);
        }
    }
};
var csObs = new CaseSelectObserver();

function nrKeysAssociativeArrays(a) {
    if (a==null) return 0;
    var i = 0;
    for (var key in a) i++;
    return i;
}

function compareAssociativeArrays(a, b) {
    if (a == b) return true;
    if ((typeof a)!=(typeof {})||(typeof b)!=(typeof {})) return false;
    if (nrKeysAssociativeArrays(a) != nrKeysAssociativeArrays(b)) return false;
    for (var key in a) {
        if (a[key] != b[key]) {
            return false;
        }
    }
    return true;
}

// replot all
function resetAllPlots(dt) {
    var headerMap = getHeaderMap(dt);
    var caseMap = getCaseMap(dt);

    $('#clinical-data-loading-wait').hide();
    $('#summary-plot-table').show();

    var formatter = new google.visualization.PatternFormat(formatPatientLink('{0}'));
    formatter.format(dt, [0]);
    drawDataTable('clinical-data-table',dt,caseMap);

    var colCna = headerMap['copy_number_altered_fraction'];
    var colMut = headerMap['mutation_count'];
    plotMutVsCna('mut-cna-scatter-plot',dt,colCna,colMut,caseMap,false,false);

    $('#mut-cna-config').show();

    $(".mut-cna-axis-log").change(function() {
        mutCnaAxisScaleChanged(dt,colCna,colMut,caseMap);
    });

    resetSmallPlots(dt);
}

// replot small
function resetSmallPlots(dt) {
    var divNum = 1;
    for (var i=1; i<dt.getNumberOfColumns(); i++) {
        if (decideToPlot(dt,i))
            plotData(divNum++,dt,i,null);
    }
}

function decideToPlot(dt,col) {
    var c = dt.getColumnLabel(col);
    return c.toLowerCase().match(/(^age)|(survival)|(grade)|(stage)|(histology)/);
}

// draw datatable
function drawDataTable(divId,dt,caseMap) {
    var tableDataView = new google.visualization.DataView(dt);
    var table = new google.visualization.Table(document.getElementById(divId));
    var options = {
        allowHtml: true,
        showRowNumber: true
    };

    google.visualization.events.addListener(table, 'select', function(e){
        var s = table.getSelection();
        var caseId;
        if (s.length==0) caseId = null;
        else if (s.length==1) caseId = dt.getValue(s[0].row,0);
        else {
            caseId = {};
            for (var i=0; i<s.length; i++) {
                caseId[dt.getValue(s[i].row,0)] = true;
            }
        }

        csObs.fireSelection(caseId,divId);
    });
    google.visualization.events.addListener(table, 'ready', function(e){
        csObs.subscribe(divId,function(caseId) {
            if (caseId==null) {
                table.setSelection();
            } else if ((typeof caseId)==(typeof '')) {
                var ix = caseMap[caseId];
                table.setSelection(ix==null?null:[{'row': ix}]);
            } else if ((typeof caseId)==(typeof {})) {
                var rows = [];
                for (var id in caseId) {
                    var row = caseMap[id];
                    if (row!=null)
                        rows.push({'row':caseMap[id]});
                }
                table.setSelection(rows);
            }
        },false);
    });

    table.draw(tableDataView,options);
}

function plotMutVsCna(divId,dt,colCna,colMut,caseMap,hLog,vLog) {
        var scatterDataView = new google.visualization.DataView(dt);
        scatterDataView.setColumns(
            [colCna,
                colMut,
                {calc:function(dt,row){
                        return dt.getValue(row,0)+'\n('+(dt.getValue(row,colCna)*100).toFixed(1)+'%, '+dt.getValue(row,colMut)+')';
                    },type:'string',role:'tooltip'}]);
        var scatter = new google.visualization.ScatterChart(document.getElementById(divId));
        google.visualization.events.addListener(scatter, 'select', function(e){
            var s = scatter.getSelection();
            if (s.length>1) return;
            var caseId = s.length==0 ? null : dt.getValue(s[0].row,0);
            $('#case-id-div').html(formatPatientLink(caseId));
            csObs.fireSelection(caseId, 'scatter-plot');
            resetSmallPlots(dt);
        });

        google.visualization.events.addListener(scatter, 'ready', function(e){
            csObs.subscribe('scatter-plot',function(caseId) {
                if (caseId==null) {
                    scatter.setSelection(null);
                    $('#case-id-div').html("");
                }
                if ((typeof caseId)==(typeof "")) {
                    var ix = caseMap[caseId];
                    scatter.setSelection(ix==null?null:[{'row': ix}]);
                    $('#case-id-div').html(formatPatientLink(caseId));
                } else if ((typeof caseId)==(typeof {})) {
                    var rows = [];
                    for (var id in caseId) {
                        var row = caseMap[id];
                        if (row!=null)
                            rows.push({'row':caseMap[id]});
                    }
                    scatter.setSelection(rows);
                    $('#case-id-div').html(rows.length==1?formatPatientLink(id):"");
                }
            },true);
        });
        var options = {
            hAxis: {title: "Copy number alteration fraction", logScale:hLog, format:'#%'},
            vAxis: {title: "# of mutations", logScale:vLog, format:'#,###'},
            legend: {position:'none'}
        };
        scatter.draw(scatterDataView,options);
        return scatter;
}

function formatPatientLink(caseId) {
    return caseId==null?"":''+caseId+''
}

function getHeaderMap(dataTable) {
    var map = {};
    var cols = dataTable.getNumberOfColumns();
    for (var i=0; i<cols; i++) {
        map[dataTable.getColumnLabel(i)] = i;
    }
    return map;
}

function getCaseMap(dataTable) {
    var map = {};
    var rows = dataTable.getNumberOfRows();
    for (var i=0; i<rows; i++) {
        map[dataTable.getValue(i,0)] = i;
    }
    return map;
}

function makeContInxArray(start,end) {
    var ix = [];
    for (var i=start; i<=end; i++) {
        ix.push(i);
    }
    return ix;
}

function calcHistogram(dt,col,bins,caseIdsFilter) {
    var hist = [[dt.getColumnLabel(col),'# patients']];
    var caseIdHistMap = {};
    var histCaseIdMap = {};
    var rows = dt.getNumberOfRows();
    var t = dt.getColumnType(col);
    if (t=="number") {
        if (bins==null) bins = 10;
        if ((typeof bins)==(typeof 1)) {
            var r = dt.getColumnRange(col);
            var step = (r.max-r.min)/(bins-1);
            var n = bins;
            bins = [];
            for (var i=0; i<n-1; i++) {
                bins.push(r.min+i*step);
            }
        }

        var count = [];
        for (var i=0; i<=bins.length; i++) {
            count.push([]);
        }
        count.push([]); //unknown

        for (var r=0; r<rows; r++) {
            var caseId = dt.getValue(r,0);
            if (caseIdsFilter && !caseIdsFilter[caseId]) continue;
            var v = dt.getValue(r,col);
            var i;
            if (v==null) {
                i = bins.length+1;
            } else {
                for (i=0; i<bins.length; i++) {
                    if (bins[i]>=v) break;
                }
            }
            count[i].push(caseId);
            caseIdHistMap[caseId]=i;
        }

        hist.push(['<='+bins[0],count[0].length]);
        histCaseIdMap[0]=count[0];
        var i=1;
        for (; i<bins.length; i++) {
            hist.push([bins[i-1]+'~'+bins[i],count[i].length]);
            histCaseIdMap[i]=count[i];
        }
        hist.push(['>'+bins[bins.length-1],count[i].length]);
        histCaseIdMap[i]=count[i];
        if (count[++i].length>0) { // including unknow if positive
            hist.push(['Unknown',count[i].length]);
            histCaseIdMap[i]=count[i];
        }
    } else {
        var count = {};
        for (var r=0; r<rows; r++) {
            var caseId = dt.getValue(r,0);
            if (caseIdsFilter && !caseIdsFilter[caseId]) continue;
            var v = dt.getValue(r,col);
            if (v==null) v="Unknown";
            if(count[v]==null) count[v] = [];
            count[v].push(caseId);
        }
        var row=0;
        for (var key in count) {
            var c = count[key];
            hist.push([key,c.length]);
            histCaseIdMap[row]=c;
            for (var i=0; i<c.length; i++)
                caseIdHistMap[c[i]]=row;
            row++;
        }
    }
    return [hist,caseIdHistMap,histCaseIdMap];
}

function plotData(divNum,dt,col,caseIds) {
    var c = dt.getColumnLabel(col);
    var div = getSmallPlotDiv(divNum,c);
    var t = dt.getColumnType(col);
    var nCases = nrKeysAssociativeArrays(caseIds);
    var hist = calcHistogram(dt,col,getBins(dt,col),nCases>0?caseIds:null);
    var columnChart = t=='number';
    var chart = columnChart ?
        new google.visualization.ColumnChart(div) :
        new google.visualization.PieChart(div);
    google.visualization.events.addListener(chart, 'select', function(e){
        var s = chart.getSelection();
        var caseIds = {};
        var empty = true;
        for (var i=0; i<s.length; i++) {
            var ids = hist[2][s[i].row];
            for (var j=0; j<ids.length; j++) {
                caseIds[ids[j]] = true;
                empty = false;
            }
        }

        // if empty, update all plots including myself
        csObs.fireSelection(caseIds,empty?null:div.id);
    });
    google.visualization.events.addListener(chart, 'ready', function(e){
        csObs.subscribe(div.id,function(caseId) {
            if (caseId==null) {
                chart.setSelection();
            } else if ((typeof caseId)==(typeof '')) {
                var ix = hist[1][caseId];
                chart.setSelection(ix==null?null:[{'row': ix}]);
            } else if ((typeof caseId)==(typeof {})) {
                plotData(divNum,dt,col,caseId);
            }
        },caseIds==null);
    });
    var options = columnChart ?
        {
            hAxis: {title: dt.getColumnLabel(col)},
            vAxis: {title: '# of Patients'},
            legend: {position: 'none'}
        }
        :
        {'pieSliceText' : 'value'}
        ;
    chart.draw(google.visualization.arrayToDataTable(hist[0]),options);
    return chart;
}

function getBins(dt,col) {
    var c = dt.getColumnLabel(col);
    if (c=='age_at_diagnosis') {
        return [20,30,40,50,60,70,80];
    }
    if (c.match(/_months$/)) {
        return [12,24,36,48,60,72,84,96];
    }
    return null;
}

function getSmallPlotDiv(divNum,legend) {
    var div = document.getElementById('small-plot-div-'+divNum);
    if (div!=null) return div;

    var td = $('#small-plot-td-'+divNum);
    if (td.length==0) {
        var irow = Math.ceil(divNum/4);
        if (divNum%4==1) {
            $('#summary-plot-table').append('

');
            $('#small-plot-tr-'+irow).html('	');
        } else {
            $('#small-plot-tr-'+irow).append('	');
        }
        td = $('#small-plot-td-'+divNum);
    }

    td.html('<fieldset>'
            +'<legend style="color:blue;font-weight:bold;">'+legend+'</legend>'
            +'
'
            +'
'
            + '</fieldset>');
    return document.getElementById('small-plot-div-'+divNum);
}<commit_msg>cancer study view:  number precision in table<commit_after>
// observer/listener pattern handling selection event on plots
function CaseSelectObserver() {
    this.funcs = {};
    this.caseId = null;
}
CaseSelectObserver.prototype = {
    subscribe: function(listener,func,fire) {
        this.funcs[listener] = func;
        if (fire)
            func.call(window,this.caseId);
    },
    fireSelection: function(caseId,source,targets,forceFire) {
        if (!forceFire&&compareAssociativeArrays(caseId,this.caseId)) return;
        //$('#submit-patient-btn').attr("disabled", caseId==null);

        this.caseId = caseId;
        for (var listener in this.funcs) {
            if (targets!=null&&(targets!=listener&&!targets[listener])) continue;
            if (listener==source) continue;
            var func = this.funcs[listener];
            func.call(window,caseId);
        }
    }
};
var csObs = new CaseSelectObserver();

function nrKeysAssociativeArrays(a) {
    if (a==null) return 0;
    var i = 0;
    for (var key in a) i++;
    return i;
}

function compareAssociativeArrays(a, b) {
    if (a == b) return true;
    if ((typeof a)!=(typeof {})||(typeof b)!=(typeof {})) return false;
    if (nrKeysAssociativeArrays(a) != nrKeysAssociativeArrays(b)) return false;
    for (var key in a) {
        if (a[key] != b[key]) {
            return false;
        }
    }
    return true;
}

// replot all
function resetAllPlots(dt) {
    var headerMap = getHeaderMap(dt);
    var caseMap = getCaseMap(dt);

    $('#clinical-data-loading-wait').hide();
    $('#summary-plot-table').show();

    drawDataTable('clinical-data-table',dt,caseMap);

    var colCna = headerMap['copy_number_altered_fraction'];
    var colMut = headerMap['mutation_count'];
    plotMutVsCna('mut-cna-scatter-plot',dt,colCna,colMut,caseMap,false,false);

    $('#mut-cna-config').show();

    $(".mut-cna-axis-log").change(function() {
        mutCnaAxisScaleChanged(dt,colCna,colMut,caseMap);
    });

    resetSmallPlots(dt);
}

// replot small
function resetSmallPlots(dt) {
    var divNum = 1;
    for (var i=1; i<dt.getNumberOfColumns(); i++) {
        if (decideToPlot(dt,i))
            plotData(divNum++,dt,i,null);
    }
}

function decideToPlot(dt,col) {
    var c = dt.getColumnLabel(col);
    return c.toLowerCase().match(/(^age)|(survival)|(grade)|(stage)|(histology)/);
}

// draw datatable
function drawDataTable(divId,dt,caseMap) {
    var tableDataView = new google.visualization.DataView(dt);

    var idFormatter = new google.visualization.PatternFormat(formatPatientLink('{0}'));
    idFormatter.format(dt, [0]);

    var numFormatter = new google.visualization.NumberFormat({pattern:'#.##'});
    for (var i=0, len=dt.getNumberOfColumns(); i<len; i++) {
        numFormatter.format(dt, i);
    }

    var table = new google.visualization.Table(document.getElementById(divId));
    var options = {
        allowHtml: true,
        showRowNumber: true
    };

    google.visualization.events.addListener(table, 'select', function(e){
        var s = table.getSelection();
        var caseId;
        if (s.length==0) caseId = null;
        else if (s.length==1) caseId = dt.getValue(s[0].row,0);
        else {
            caseId = {};
            for (var i=0; i<s.length; i++) {
                caseId[dt.getValue(s[i].row,0)] = true;
            }
        }

        csObs.fireSelection(caseId,divId);
    });
    google.visualization.events.addListener(table, 'ready', function(e){
        csObs.subscribe(divId,function(caseId) {
            if (caseId==null) {
                table.setSelection();
            } else if ((typeof caseId)==(typeof '')) {
                var ix = caseMap[caseId];
                table.setSelection(ix==null?null:[{'row': ix}]);
            } else if ((typeof caseId)==(typeof {})) {
                var rows = [];
                for (var id in caseId) {
                    var row = caseMap[id];
                    if (row!=null)
                        rows.push({'row':caseMap[id]});
                }
                table.setSelection(rows);
            }
        },false);
    });

    table.draw(tableDataView,options);
}

function plotMutVsCna(divId,dt,colCna,colMut,caseMap,hLog,vLog) {
        var scatterDataView = new google.visualization.DataView(dt);
        scatterDataView.setColumns(
            [colCna,
                colMut,
                {calc:function(dt,row){
                        return dt.getValue(row,0)+'\n('+(dt.getValue(row,colCna)*100).toFixed(1)+'%, '+dt.getValue(row,colMut)+')';
                    },type:'string',role:'tooltip'}]);
        var scatter = new google.visualization.ScatterChart(document.getElementById(divId));
        google.visualization.events.addListener(scatter, 'select', function(e){
            var s = scatter.getSelection();
            if (s.length>1) return;
            var caseId = s.length==0 ? null : dt.getValue(s[0].row,0);
            $('#case-id-div').html(formatPatientLink(caseId));
            csObs.fireSelection(caseId, 'scatter-plot');
            resetSmallPlots(dt);
        });

        google.visualization.events.addListener(scatter, 'ready', function(e){
            csObs.subscribe('scatter-plot',function(caseId) {
                if (caseId==null) {
                    scatter.setSelection(null);
                    $('#case-id-div').html("");
                }
                if ((typeof caseId)==(typeof "")) {
                    var ix = caseMap[caseId];
                    scatter.setSelection(ix==null?null:[{'row': ix}]);
                    $('#case-id-div').html(formatPatientLink(caseId));
                } else if ((typeof caseId)==(typeof {})) {
                    var rows = [];
                    for (var id in caseId) {
                        var row = caseMap[id];
                        if (row!=null)
                            rows.push({'row':caseMap[id]});
                    }
                    scatter.setSelection(rows);
                    $('#case-id-div').html(rows.length==1?formatPatientLink(id):"");
                }
            },true);
        });
        var options = {
            hAxis: {title: "Copy number alteration fraction", logScale:hLog, format:'#%'},
            vAxis: {title: "# of mutations", logScale:vLog, format:'#,###'},
            legend: {position:'none'}
        };
        scatter.draw(scatterDataView,options);
        return scatter;
}

function formatPatientLink(caseId) {
    return caseId==null?"":''+caseId+''
}

function getHeaderMap(dataTable) {
    var map = {};
    var cols = dataTable.getNumberOfColumns();
    for (var i=0; i<cols; i++) {
        map[dataTable.getColumnLabel(i)] = i;
    }
    return map;
}

function getCaseMap(dataTable) {
    var map = {};
    var rows = dataTable.getNumberOfRows();
    for (var i=0; i<rows; i++) {
        map[dataTable.getValue(i,0)] = i;
    }
    return map;
}

function makeContInxArray(start,end) {
    var ix = [];
    for (var i=start; i<=end; i++) {
        ix.push(i);
    }
    return ix;
}

function calcHistogram(dt,col,bins,caseIdsFilter) {
    var hist = [[dt.getColumnLabel(col),'# patients']];
    var caseIdHistMap = {};
    var histCaseIdMap = {};
    var rows = dt.getNumberOfRows();
    var t = dt.getColumnType(col);
    if (t=="number") {
        if (bins==null) bins = 10;
        if ((typeof bins)==(typeof 1)) {
            var r = dt.getColumnRange(col);
            var step = (r.max-r.min)/(bins-1);
            var n = bins;
            bins = [];
            for (var i=0; i<n-1; i++) {
                bins.push(r.min+i*step);
            }
        }

        var count = [];
        for (var i=0; i<=bins.length; i++) {
            count.push([]);
        }
        count.push([]); //unknown

        for (var r=0; r<rows; r++) {
            var caseId = dt.getValue(r,0);
            if (caseIdsFilter && !caseIdsFilter[caseId]) continue;
            var v = dt.getValue(r,col);
            var i;
            if (v==null) {
                i = bins.length+1;
            } else {
                for (i=0; i<bins.length; i++) {
                    if (bins[i]>=v) break;
                }
            }
            count[i].push(caseId);
            caseIdHistMap[caseId]=i;
        }

        hist.push(['<='+bins[0],count[0].length]);
        histCaseIdMap[0]=count[0];
        var i=1;
        for (; i<bins.length; i++) {
            hist.push([bins[i-1]+'~'+bins[i],count[i].length]);
            histCaseIdMap[i]=count[i];
        }
        hist.push(['>'+bins[bins.length-1],count[i].length]);
        histCaseIdMap[i]=count[i];
        if (count[++i].length>0) { // including unknow if positive
            hist.push(['Unknown',count[i].length]);
            histCaseIdMap[i]=count[i];
        }
    } else {
        var count = {};
        for (var r=0; r<rows; r++) {
            var caseId = dt.getValue(r,0);
            if (caseIdsFilter && !caseIdsFilter[caseId]) continue;
            var v = dt.getValue(r,col);
            if (v==null) v="Unknown";
            if(count[v]==null) count[v] = [];
            count[v].push(caseId);
        }
        var row=0;
        for (var key in count) {
            var c = count[key];
            hist.push([key,c.length]);
            histCaseIdMap[row]=c;
            for (var i=0; i<c.length; i++)
                caseIdHistMap[c[i]]=row;
            row++;
        }
    }
    return [hist,caseIdHistMap,histCaseIdMap];
}

function plotData(divNum,dt,col,caseIds) {
    var c = dt.getColumnLabel(col);
    var div = getSmallPlotDiv(divNum,c);
    var t = dt.getColumnType(col);
    var nCases = nrKeysAssociativeArrays(caseIds);
    var hist = calcHistogram(dt,col,getBins(dt,col),nCases>0?caseIds:null);
    var columnChart = t=='number';
    var chart = columnChart ?
        new google.visualization.ColumnChart(div) :
        new google.visualization.PieChart(div);
    google.visualization.events.addListener(chart, 'select', function(e){
        var s = chart.getSelection();
        var caseIds = {};
        var empty = true;
        for (var i=0; i<s.length; i++) {
            var ids = hist[2][s[i].row];
            for (var j=0; j<ids.length; j++) {
                caseIds[ids[j]] = true;
                empty = false;
            }
        }

        // if empty, update all plots including myself
        csObs.fireSelection(caseIds,empty?null:div.id);
    });
    google.visualization.events.addListener(chart, 'ready', function(e){
        csObs.subscribe(div.id,function(caseId) {
            if (caseId==null) {
                chart.setSelection();
            } else if ((typeof caseId)==(typeof '')) {
                var ix = hist[1][caseId];
                chart.setSelection(ix==null?null:[{'row': ix}]);
            } else if ((typeof caseId)==(typeof {})) {
                plotData(divNum,dt,col,caseId);
            }
        },caseIds==null);
    });
    var options = columnChart ?
        {
            hAxis: {title: dt.getColumnLabel(col)},
            vAxis: {title: '# of Patients'},
            legend: {position: 'none'}
        }
        :
        {'pieSliceText' : 'value'}
        ;
    chart.draw(google.visualization.arrayToDataTable(hist[0]),options);
    return chart;
}

function getBins(dt,col) {
    var c = dt.getColumnLabel(col);
    if (c=='age_at_diagnosis') {
        return [20,30,40,50,60,70,80];
    }
    if (c.match(/_months$/)) {
        return [12,24,36,48,60,72,84,96];
    }
    return null;
}

function getSmallPlotDiv(divNum,legend) {
    var div = document.getElementById('small-plot-div-'+divNum);
    if (div!=null) return div;

    var td = $('#small-plot-td-'+divNum);
    if (td.length==0) {
        var irow = Math.ceil(divNum/4);
        if (divNum%4==1) {
            $('#summary-plot-table').append('

');
            $('#small-plot-tr-'+irow).html('	');
        } else {
            $('#small-plot-tr-'+irow).append('	');
        }
        td = $('#small-plot-td-'+divNum);
    }

    td.html('<fieldset>'
            +'<legend style="color:blue;font-weight:bold;">'+legend+'</legend>'
            +'
'
            +'
'
            + '</fieldset>');
    return document.getElementById('small-plot-div-'+divNum);
}<|endoftext|>


<commit_before>/**
 *
 * Copyright 2012 Adobe Systems Inc.;
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/*global module:false*/

module.exports = function(grunt) {

    // Project configuration.
    grunt.initConfig({

        clean: {
            release: ['css']
        },

        stylus: {
            compile: {
                options: {
                    paths: ['node_modules/topcoat-navigation-bar-base/src/mixins', 'node_modules/topcoat-utils/src/mixins', 'node_modules/topcoat-theme/src', 'node_modules/topcoat-theme/src/includes'],
                    import: ['navigation-bar-mixin', 'utils', 'theme-topcoat-mobile-light', 'global', 'layout', 'position', 'fonts'],
                    compress: false
                },
                files: [{
                    src: 'src/topcoat-navigation-bar.styl',
                    dest: 'css/topcoat-navigation-bar.css',
                }]
            }
        },

        cssmin: {
            minify: {
                expand: true,
                cwd: 'css',
                src: ['*.css', '!*.min.css'],
                dest: 'css',
                ext: '.min.css'
            }
        },

        copy: {
            release: {
                files: [{
                    expand: true,
                    flatten: true,
                    src: 'node_modules/topcoat-theme/font/**/*',
                    dest: 'font'
                }]
            }
        },

        jade: {
            compile: {
                expand: true,
                cwd: 'test/perf',
                src: ['*.jade'],
                dest: 'test/perf/',
                ext: '.test.html'
            }
        },

        simplemocha: {
            options: {
                ui: 'bdd',
                reporter: 'Nyan'
            },
            all: {
                src: ['test/*.test.js']
            }
        },

        watch: {
            files: 'src/**/*.styl',
            tasks: ['build', 'test']
        }

    });

    // These plugins provide necessary tasks.
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-stylus');
    grunt.loadNpmTasks('grunt-contrib-jade');
    grunt.loadNpmTasks('grunt-simple-mocha');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-copy');

    // Default task.
    grunt.registerTask('default', ['clean', 'build', 'test', 'release']);
    grunt.registerTask('build', ['stylus', 'jade']);
    grunt.registerTask('test', ['simplemocha']);
    grunt.registerTask('release', ['cssmin', 'copy']);

};
<commit_msg>removed nyan reporter<commit_after>/**
 *
 * Copyright 2012 Adobe Systems Inc.;
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/*global module:false*/

module.exports = function(grunt) {

    // Project configuration.
    grunt.initConfig({

        clean: {
            release: ['css']
        },

        stylus: {
            compile: {
                options: {
                    paths: ['node_modules/topcoat-navigation-bar-base/src/mixins', 'node_modules/topcoat-utils/src/mixins', 'node_modules/topcoat-theme/src', 'node_modules/topcoat-theme/src/includes'],
                    import: ['navigation-bar-mixin', 'utils', 'theme-topcoat-mobile-light', 'global', 'layout', 'position', 'fonts'],
                    compress: false
                },
                files: [{
                    src: 'src/topcoat-navigation-bar.styl',
                    dest: 'css/topcoat-navigation-bar.css',
                }]
            }
        },

        cssmin: {
            minify: {
                expand: true,
                cwd: 'css',
                src: ['*.css', '!*.min.css'],
                dest: 'css',
                ext: '.min.css'
            }
        },

        copy: {
            release: {
                files: [{
                    expand: true,
                    flatten: true,
                    src: 'node_modules/topcoat-theme/font/**/*',
                    dest: 'font'
                }]
            }
        },

        jade: {
            compile: {
                expand: true,
                cwd: 'test/perf',
                src: ['*.jade'],
                dest: 'test/perf/',
                ext: '.test.html'
            }
        },

        simplemocha: {
            options: {
                ui: 'bdd'
            },
            all: {
                src: ['test/*.test.js']
            }
        },

        watch: {
            files: 'src/**/*.styl',
            tasks: ['build', 'test']
        }

    });

    // These plugins provide necessary tasks.
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-stylus');
    grunt.loadNpmTasks('grunt-contrib-jade');
    grunt.loadNpmTasks('grunt-simple-mocha');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-copy');

    // Default task.
    grunt.registerTask('default', ['clean', 'build', 'test', 'release']);
    grunt.registerTask('build', ['stylus', 'jade']);
    grunt.registerTask('test', ['simplemocha']);
    grunt.registerTask('release', ['cssmin', 'copy']);

};
<|endoftext|>


<commit_before>
((window.gitter = {}).chat = {}).options = {
  room: 'thepracticaldev/1pr',
  useStyles: false
};

function toggleSidebar(){
	sidebar     = document.getElementById("sidebar");
	if (sidebar.style.display == 'none') {
		sidebar.style.display = 'block';
	} else if (sidebar.style.display == 'block') {
		sidebar.style.display = 'none';
	}
	else {
		sidebar.style.display = 'block';
	}
}

var mainContent = document.querySelector('#main-content');
mainContent.onresize = function(){

	if ( mainContent.getComputedStyle(element).width > 720 && sidebar.style.display == 'none') {
		sidebar.style.display = 'block';
	}

};
<commit_msg>Update main.js<commit_after>
((window.gitter = {}).chat = {}).options = {
  room: 'thepracticaldev/1pr',
  useStyles: false
};

function toggleSidebar(){
	sidebar     = document.getElementById("sidebar");
	if (sidebar.style.display == 'none') {
		sidebar.style.display = 'block';
	} else if (sidebar.style.display == 'block') {
		sidebar.style.display = 'none';
	}
	else {
		sidebar.style.display = 'block';
	}
}
<|endoftext|>


<commit_before>'use strict';
const request = require('request');
const cheerio = require('cheerio');

class SVPageParser{
    parseToday(url, callback) {
        const self = this;
        request(url, function(err, resp, body){
            let $ = cheerio.load(body);
            let message = self.formatMessage($);
            callback(message);
        });
    }

    formatMessage($) {
        let offers = $('#menu-plan-tab1').find('.menu-item');
        let text = '';
        $(offers).each(function (i, offer) {
            let menuDescription = $(offer).find('.menu-description');
            menuDescription.find('br').replaceWith(' ');

            text += '*' + removeSpecialCharacters($(offer).find('.menu-title').text().trim()) + '*\n';
            text += removeSpecialCharacters(menuDescription.text()) + '\n';
            text += '_' + removeSpecialCharacters($(offer).find('.prices-3 .val').text() + ' ' + $(offer).find('.prices-3 .desc').text()) + "_\n";
            let provenance = removeSpecialCharacters($(offer).find('.menu-provenance').text());
            if (provenance.length > 1) {
                text += '_' + provenance + "_\n\n";
            } else {
                text += "\n";
            }

        });

        function removeSpecialCharacters(text) {
            return (text.replace(/`|\*|_|#|\r/g, ''));
        }

        return text;
    }
}

module.exports = SVPageParser;
<commit_msg>feat(parser): add methods for week menu<commit_after>'use strict';
const request = require('request');
const cheerio = require('cheerio');

class SVPageParser{
    parseToday(url, callback) {
        const self = this;
        request(url, function(err, resp, body){
            let $ = cheerio.load(body);
            let message = self.formatMessage($);
            callback(message);
        });
    }

    parseWeek(url, callback) {
        const self = this;
        request(url, function(err, resp, body){
            let $ = cheerio.load(body);
            let message = self.formatWeek($);
            callback(message);
        });
    }

    formatWeek($) {
        let offers = $('#menu-plan-tab1').find('.menu-item');
        let text = '';
        $(offers).each(function (i, offer) {
            let menuDescription = $(offer).find('.menu-description');
            menuDescription.find('br').replaceWith(' ');

            text += '*' + removeSpecialCharacters($(offer).find('.menu-title').text().trim()) + '*\n';
            text += removeSpecialCharacters(menuDescription.text()) + '\n';
            text += '_' + removeSpecialCharacters($(offer).find('.prices-3 .val').text() + ' ' + $(offer).find('.prices-3 .desc').text()) + "_\n";
            let provenance = removeSpecialCharacters($(offer).find('.menu-provenance').text());
            if (provenance.length > 1) {
                text += '_' + provenance + "_\n\n";
            } else {
                text += "\n";
            }

        });

        function removeSpecialCharacters(text) {
            return (text.replace(/`|\*|_|#|\r/g, ''));
        }

        return text;
    }

    formatMessage($) {
        let offers = $('#menu-plan-tab1').find('.menu-item');
        let text = '';
        $(offers).each(function (i, offer) {
            let menuDescription = $(offer).find('.menu-description');
            menuDescription.find('br').replaceWith(' ');

            text += '*' + removeSpecialCharacters($(offer).find('.menu-title').text().trim()) + '*\n';
            text += removeSpecialCharacters(menuDescription.text()) + '\n';
            text += '_' + removeSpecialCharacters($(offer).find('.prices-3 .val').text() + ' ' + $(offer).find('.prices-3 .desc').text()) + "_\n";
            let provenance = removeSpecialCharacters($(offer).find('.menu-provenance').text());
            if (provenance.length > 1) {
                text += '_' + provenance + "_\n\n";
            } else {
                text += "\n";
            }

        });

        function removeSpecialCharacters(text) {
            return (text.replace(/`|\*|_|#|\r/g, ''));
        }

        return text;
    }
}

module.exports = SVPageParser;
<|endoftext|>


<commit_before>import React, { useState } from 'react';
import { useMutation } from '@apollo/react-hooks';
import gql from 'graphql-tag';
import { t } from 'ttag';
import url from 'url';
import Link from 'next/link';
import PropTypes from 'prop-types';

const UPVOTE = 'UPVOTE';
const DOWNVOTE = 'DOWNVOTE';

const ReplyRequestInfo = gql`
  fragment ReplyRequestInfo on ReplyRequest {
    id
    reason
    positiveFeedbackCount
    negativeFeedbackCount
  }
`;

const ReplyRequestInfoForUser = gql`
  fragment ReplyRequestInfoForUser on ReplyRequest {
    ownVote
  }
`;

const UPDATE_VOTE = gql`
  mutation UpdateVote($replyRequestId: String!, $vote: FeedbackVote!) {
    CreateOrUpdateReplyRequestFeedback(
      replyRequestId: $replyRequestId
      vote: $vote
    ) {
      ...ReplyRequestInfo
      ...ReplyRequestInfoForUser
    }
  }
  ${ReplyRequestInfo}
  ${ReplyRequestInfoForUser}
`;

const AuthorArticleLink = ({ articleId }) => (
  <Link
    href={url.format({
      pathname: '/articles',
      query: {
        searchUserByArticleId: articleId,
        filter: 'all',
        replyRequestCount: 1,
      },
    })}
  >

      {t`All messages reported by this user`}

  </Link>
);

function UserIcon() {
  return (
    <>
      <svg
        className="svg-user"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 512 512"
      >
        <path d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 48c110.457 0 200 89.543 200 200 0 36.982-10.049 71.611-27.548 101.328-7.072-25.444-25.663-54.208-63.93-65.374C377.207 271.782 384 248.414 384 224c0-70.689-57.189-128-128-128-70.689 0-128 57.19-128 128 0 24.414 6.793 47.783 19.478 67.954-38.299 11.175-56.876 39.913-63.938 65.362C66.046 327.601 56 292.976 56 256c0-110.457 89.543-200 200-200zm80 168c0 44.183-35.817 80-80 80s-80-35.817-80-80 35.817-80 80-80 80 35.817 80 80zM128 409.669v-27.758c0-20.41 13.53-38.348 33.156-43.955l24.476-6.993C206.342 344.648 230.605 352 256 352s49.658-7.352 70.369-21.038l24.476 6.993C370.47 343.563 384 361.5 384 381.911v27.758C349.315 438.592 304.693 456 256 456s-93.315-17.408-128-46.331z" />
      </svg>

    </>
  );
}

function ReplyRequestReason({ isArticleCreator, replyRequest, articleId }) {
  const {
    id: replyRequestId,
    reason: replyRequestReason,
    positiveFeedbackCount,
    negativeFeedbackCount,
    ownVote,
  } = replyRequest;

  const [voteReason, { loading }] = useMutation(UPDATE_VOTE);
  const handleVote = vote => {
    voteReason({ variables: { vote, replyRequestId } });
  };

  if (!(isArticleCreator || replyRequestReason)) return null;

  return (

      {replyRequestReason && (
        <>

            <button
              className="btn-vote btn-up-vote"
              onClick={() => handleVote(UPVOTE)}
              disabled={loading || ownVote === UPVOTE}
            >
              {positiveFeedbackCount}
              <svg
                className={`icon ${ownVote === UPVOTE && 'active'}`}
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 320 512"
              >
                <path d="M48.048 352h223.895c42.638 0 64.151-51.731 33.941-81.941l-111.943-112c-18.745-18.745-49.137-18.746-67.882 0l-111.952 112C-16.042 300.208 5.325 352 48.048 352zM160 192l112 112H48l112-112z" />
              </svg>
            </button>
            <button
              className="btn-vote btn-down-vote"
              onClick={() => {
                handleVote(DOWNVOTE);
              }}
              disabled={loading || ownVote === DOWNVOTE}
            >
              {negativeFeedbackCount}
              <svg
                className={`icon ${ownVote === DOWNVOTE && 'active'}`}
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 320 512"
              >
                <path d="M272 160H48.1c-42.6 0-64.2 51.7-33.9 81.9l111.9 112c18.7 18.7 49.1 18.7 67.9 0l112-112c30-30.1 8.7-81.9-34-81.9zM160 320L48 208h224L160 320z" />
              </svg>
            </button>

          <UserIcon />

{replyRequestReason}

        </>
      )}
      {isArticleCreator && <AuthorArticleLink articleId={articleId} />}

  );
}

ReplyRequestReason.propTypes = {
  articleId: PropTypes.string,
  replyRequest: PropTypes.object.isRequired,
  isArticleCreator: PropTypes.bool.isRequired, // should display link of searchUserByArticleId, no matter have reason or not
};

ReplyRequestReason.fragments = { ReplyRequestInfo, ReplyRequestInfoForUser };

export default ReplyRequestReason;
<commit_msg>[ReplyRequestReason] No need for useState, just use ownVote<commit_after>import React from 'react';
import { useMutation } from '@apollo/react-hooks';
import gql from 'graphql-tag';
import { t } from 'ttag';
import url from 'url';
import Link from 'next/link';
import PropTypes from 'prop-types';

const UPVOTE = 'UPVOTE';
const DOWNVOTE = 'DOWNVOTE';

const ReplyRequestInfo = gql`
  fragment ReplyRequestInfo on ReplyRequest {
    id
    reason
    positiveFeedbackCount
    negativeFeedbackCount
  }
`;

const ReplyRequestInfoForUser = gql`
  fragment ReplyRequestInfoForUser on ReplyRequest {
    ownVote
  }
`;

const UPDATE_VOTE = gql`
  mutation UpdateVote($replyRequestId: String!, $vote: FeedbackVote!) {
    CreateOrUpdateReplyRequestFeedback(
      replyRequestId: $replyRequestId
      vote: $vote
    ) {
      ...ReplyRequestInfo
      ...ReplyRequestInfoForUser
    }
  }
  ${ReplyRequestInfo}
  ${ReplyRequestInfoForUser}
`;

const AuthorArticleLink = ({ articleId }) => (
  <Link
    href={url.format({
      pathname: '/articles',
      query: {
        searchUserByArticleId: articleId,
        filter: 'all',
        replyRequestCount: 1,
      },
    })}
  >

      {t`All messages reported by this user`}

  </Link>
);

function UserIcon() {
  return (
    <>
      <svg
        className="svg-user"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 512 512"
      >
        <path d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 48c110.457 0 200 89.543 200 200 0 36.982-10.049 71.611-27.548 101.328-7.072-25.444-25.663-54.208-63.93-65.374C377.207 271.782 384 248.414 384 224c0-70.689-57.189-128-128-128-70.689 0-128 57.19-128 128 0 24.414 6.793 47.783 19.478 67.954-38.299 11.175-56.876 39.913-63.938 65.362C66.046 327.601 56 292.976 56 256c0-110.457 89.543-200 200-200zm80 168c0 44.183-35.817 80-80 80s-80-35.817-80-80 35.817-80 80-80 80 35.817 80 80zM128 409.669v-27.758c0-20.41 13.53-38.348 33.156-43.955l24.476-6.993C206.342 344.648 230.605 352 256 352s49.658-7.352 70.369-21.038l24.476 6.993C370.47 343.563 384 361.5 384 381.911v27.758C349.315 438.592 304.693 456 256 456s-93.315-17.408-128-46.331z" />
      </svg>

    </>
  );
}

function ReplyRequestReason({ isArticleCreator, replyRequest, articleId }) {
  const {
    id: replyRequestId,
    reason: replyRequestReason,
    positiveFeedbackCount,
    negativeFeedbackCount,
    ownVote,
  } = replyRequest;

  const [voteReason, { loading }] = useMutation(UPDATE_VOTE);
  const handleVote = vote => {
    voteReason({ variables: { vote, replyRequestId } });
  };

  if (!(isArticleCreator || replyRequestReason)) return null;

  return (

      {replyRequestReason && (
        <>

            <button
              className="btn-vote btn-up-vote"
              onClick={() => handleVote(UPVOTE)}
              disabled={loading || ownVote === UPVOTE}
            >
              {positiveFeedbackCount}
              <svg
                className={`icon ${ownVote === UPVOTE && 'active'}`}
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 320 512"
              >
                <path d="M48.048 352h223.895c42.638 0 64.151-51.731 33.941-81.941l-111.943-112c-18.745-18.745-49.137-18.746-67.882 0l-111.952 112C-16.042 300.208 5.325 352 48.048 352zM160 192l112 112H48l112-112z" />
              </svg>
            </button>
            <button
              className="btn-vote btn-down-vote"
              onClick={() => {
                handleVote(DOWNVOTE);
              }}
              disabled={loading || ownVote === DOWNVOTE}
            >
              {negativeFeedbackCount}
              <svg
                className={`icon ${ownVote === DOWNVOTE && 'active'}`}
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 320 512"
              >
                <path d="M272 160H48.1c-42.6 0-64.2 51.7-33.9 81.9l111.9 112c18.7 18.7 49.1 18.7 67.9 0l112-112c30-30.1 8.7-81.9-34-81.9zM160 320L48 208h224L160 320z" />
              </svg>
            </button>

          <UserIcon />

{replyRequestReason}

        </>
      )}
      {isArticleCreator && <AuthorArticleLink articleId={articleId} />}

  );
}

ReplyRequestReason.propTypes = {
  articleId: PropTypes.string,
  replyRequest: PropTypes.object.isRequired,
  isArticleCreator: PropTypes.bool.isRequired, // should display link of searchUserByArticleId, no matter have reason or not
};

ReplyRequestReason.fragments = { ReplyRequestInfo, ReplyRequestInfoForUser };

export default ReplyRequestReason;
<|endoftext|>


<commit_before>/*global $, app*/

module.exports = function ($html, cb) {
    cb = cb || function () {};

    $($html).find("a.source").oembed(null, {
        fallback : false,
        includeHandle: false,
        maxWidth: 750,
        maxHeight: 525,
        beforeEmbed: function(container, oembedData) {
            oembedData.code = oembedData.code.replace("http:", "https:");
        },
        afterEmbed: function(container, oembedData) {
            this.parent().parent().parent().show();
        },
        onProviderNotFound: function() {
            var link = $($html).find("a.source");
            var resourceURL = link.attr("href");
            if (resourceURL.match(/(https:\/\/)\S*\.(jpg|png|gif|gifv|webm)\b/i)) {
                link.parent().append("

");
                this.parent().parent().show();
            }
        }
    });
};
<commit_msg>Update embedIt.js<commit_after>/*global $, app*/

module.exports = function ($html, cb) {
    cb = cb || function () {};

    $($html).find("a.source").oembed(null, {
        fallback : false,
        includeHandle: false,
        maxWidth: 750,
        maxHeight: 525,
        beforeEmbed: function(oembedData) {
            console.log(oembedData);
            //oembedData.code = oembedData.code.replace("http:", "https:");
        },
        afterEmbed: function(container, oembedData) {
            this.parent().parent().parent().show();
        },
        onProviderNotFound: function() {
            var link = $($html).find("a.source");
            var resourceURL = link.attr("href");
            if (resourceURL.match(/(https:\/\/)\S*\.(jpg|png|gif|gifv|webm)\b/i)) {
                link.parent().append("

");
                this.parent().parent().show();
            }
        }
    });
};
<|endoftext|>


<commit_before>var gulp = require('gulp');                        // Gulp!
var sass = require('gulp-sass');                   // Sass
var prefix = require('gulp-autoprefixer');         // Autoprefixr
var minifycss = require('gulp-minify-css');        // Minify CSS
var concat = require('gulp-concat');               // Concat files
var uglify = require('gulp-uglify');               // Uglify javascript
var svgmin = require('gulp-svgmin');               // SVG minify
var imagemin = require('gulp-imagemin');           // Image minify
var rename = require('gulp-rename');               // Rename files
var notify = require("gulp-notify");               // Alerting stuff
var livereload = require('gulp-livereload');       // LiveReload
var jshint = require('gulp-jshint');               // jshint
var zip = require('gulp-zip');                     // Zip up dist

//
//    Compile all CSS for the site
//
//////////////////////////////////////////////////////////////////////
  gulp.task('sass', function (){
    return gulp.src([
      'bower_components/foundation/scss/normalize.scss',         // Gets normalize
      'assets/scss/app.scss'])                                   // Gets the apps scss
      .pipe(sass({
        includePaths: require('node-bourbon').includePaths,
        style: 'compressed', errLogToConsole: true
      }))                                                        // Compile sass
      .pipe(concat('main.css'))                                  // Concat all css
      .pipe(rename({suffix: '.min'}))                            // Rename it
      .pipe(minifycss())                                         // Minify the CSS
      .pipe(gulp.dest('assets/css/'))                            // Set the destination to assets/css
      .pipe(livereload())                                        // Reloads server
      .pipe(notify('Sass compiled & minified'));                 // Output to notification
  });

//
//    Get all the JS, concat and uglify
//
//////////////////////////////////////////////////////////////////////
  gulp.task('javascripts', function(){
    return gulp.src([
      'bower_components/jquery/dist/jquery.min.js',     // Gets Jquery
      'bower_components/fastclick/lib/fastclick.js',      // Gets fastclick
      // Gets Foundation JS change to only include the scripts you'll need
      'bower_components/foundation/js/foundation/foundation.js',
      'bower_components/foundation/js/foundation/foundation.abide.js',
      'bower_components/foundation/js/foundation/foundation.alert.js',
      'bower_components/foundation/js/foundation/foundation.clearing.js',
      'bower_components/foundation/js/foundation/foundation.equalizer.js',
      'bower_components/foundation/js/foundation/foundation.interchange.js',
      'bower_components/foundation/js/foundation/foundation.tooltip.js',
      'bower_components/foundation/js/foundation/foundation.topbar.js',
      // moving on...
      'assets/js/_*.js'])                                                 // Gets all the user JS _*.js from assets/js
      .pipe(concat('scripts.js'))                                         // Concat all the scripts
      .pipe(rename({suffix: '.min'}))                                     // Rename it
      .pipe(uglify())                                                     // Uglify(minify)
      .pipe(gulp.dest('assets/js/'))                                      // Set destination to assets/js
      .pipe(livereload())                                                 // Reloads server
      .pipe(notify('Javascripts compiled and minified'));                 // Output to notification
  });

//
//    Copy bower components to assets-folder
//
//////////////////////////////////////////////////////////////////////
  gulp.task('copy', function(){
    return gulp.src('bower_components/modernizr/modernizr.js')   // Gets Modernizr.js
    .pipe(uglify())                                       // Uglify(minify)
    .pipe(rename({suffix: '.min'}))                       // Rename it
    .pipe(gulp.dest('assets/js/'))                        // Set destination to assets/js
    .pipe(notify('Files copied'));                        // Output to notification
  });

//
//    JS hint
//
//////////////////////////////////////////////////////////////////////
  gulp.task('jshint', function() {
    return gulp.src('assets/js/_*.js')
      .pipe(jshint())
      .pipe(jshint.reporter('jshint-stylish'));
  });

//
//    Minify all SVGs and images
//
//////////////////////////////////////////////////////////////////////
  gulp.task('svgmin', function() {
    return gulp.src('assets/img/*.svg')                          // Gets all SVGs
    .pipe(svgmin())                                       // Minifies SVG
    .pipe(gulp.dest('assets/img_min/'))                   // Set destination to assets/img_min/
    .pipe(notify('SVGs minified'));                        // Output to notification
  });

  gulp.task('imagemin', function () {
    return gulp.src(['assets/img/*', '!assets/img/*.svg'])        // Gets all images except SVGs
    .pipe(imagemin())                                      // Minifies
    .pipe(gulp.dest('assets/img_min/'))                    // Set destination to assets/img_min/
    .pipe(notify('Images minified'));                        // Output to notification
  });

//
//    Builds the theme into a .zip
//
//////////////////////////////////////////////////////////////////////
  gulp.task('zip',['sass', 'jshint', 'javascripts', 'copy'], function() {
    var server = livereload.listen();

    return gulp.src([
      '!**/.*',
      '!node_modules/**',
      '!node_modules',
      '!bower_components/**',
      '!bower_components',
      '!assets/scss/**',
      '!assets/scss',
      '**/**',
    ]).pipe(zip('theme.zip'))
      .pipe(gulp.dest('.'))
      .pipe(notify('Theme zip built'));
  });

  gulp.task('build',['zip'], function() {
      process.exit(0);
  });

//
//    Default gulp task.
//
//////////////////////////////////////////////////////////////////////
gulp.task('watch', function(){
  var server = livereload();
  gulp.watch('**/*.php').on('change', function(file) {
        server.changed(file.path);
        util.log(util.colors.yellow('PHP file changed' + ' (' + file.path + ')'));
    });

  gulp.watch("bower_components/foundation/scss/**/*.scss", ['sass']); // Runs sass on foundation components change
  gulp.watch("assets/scss/**/*.scss", ['sass']);                      // Watch and run sass on changes
  gulp.watch("assets/js/_*.js", ['jshint', 'javascripts']);           // Watch and run javascripts on changes
  gulp.watch("assets/img/*", ['imagemin', 'svgmin']);                 // Watch and minify images on changes

});

gulp.task('default', ['sass', 'jshint', 'javascripts', 'copy', 'watch']);
<commit_msg>Don't bundle the markdown<commit_after>var gulp = require('gulp');                        // Gulp!
var sass = require('gulp-sass');                   // Sass
var prefix = require('gulp-autoprefixer');         // Autoprefixr
var minifycss = require('gulp-minify-css');        // Minify CSS
var concat = require('gulp-concat');               // Concat files
var uglify = require('gulp-uglify');               // Uglify javascript
var svgmin = require('gulp-svgmin');               // SVG minify
var imagemin = require('gulp-imagemin');           // Image minify
var rename = require('gulp-rename');               // Rename files
var notify = require("gulp-notify");               // Alerting stuff
var livereload = require('gulp-livereload');       // LiveReload
var jshint = require('gulp-jshint');               // jshint
var zip = require('gulp-zip');                     // Zip up dist

//
//    Compile all CSS for the site
//
//////////////////////////////////////////////////////////////////////
  gulp.task('sass', function (){
    return gulp.src([
      'bower_components/foundation/scss/normalize.scss',         // Gets normalize
      'assets/scss/app.scss'])                                   // Gets the apps scss
      .pipe(sass({
        includePaths: require('node-bourbon').includePaths,
        style: 'compressed', errLogToConsole: true
      }))                                                        // Compile sass
      .pipe(concat('main.css'))                                  // Concat all css
      .pipe(rename({suffix: '.min'}))                            // Rename it
      .pipe(minifycss())                                         // Minify the CSS
      .pipe(gulp.dest('assets/css/'))                            // Set the destination to assets/css
      .pipe(livereload())                                        // Reloads server
      .pipe(notify('Sass compiled & minified'));                 // Output to notification
  });

//
//    Get all the JS, concat and uglify
//
//////////////////////////////////////////////////////////////////////
  gulp.task('javascripts', function(){
    return gulp.src([
      'bower_components/jquery/dist/jquery.min.js',     // Gets Jquery
      'bower_components/fastclick/lib/fastclick.js',      // Gets fastclick
      // Gets Foundation JS change to only include the scripts you'll need
      'bower_components/foundation/js/foundation/foundation.js',
      'bower_components/foundation/js/foundation/foundation.abide.js',
      'bower_components/foundation/js/foundation/foundation.alert.js',
      'bower_components/foundation/js/foundation/foundation.clearing.js',
      'bower_components/foundation/js/foundation/foundation.equalizer.js',
      'bower_components/foundation/js/foundation/foundation.interchange.js',
      'bower_components/foundation/js/foundation/foundation.tooltip.js',
      'bower_components/foundation/js/foundation/foundation.topbar.js',
      // moving on...
      'assets/js/_*.js'])                                                 // Gets all the user JS _*.js from assets/js
      .pipe(concat('scripts.js'))                                         // Concat all the scripts
      .pipe(rename({suffix: '.min'}))                                     // Rename it
      .pipe(uglify())                                                     // Uglify(minify)
      .pipe(gulp.dest('assets/js/'))                                      // Set destination to assets/js
      .pipe(livereload())                                                 // Reloads server
      .pipe(notify('Javascripts compiled and minified'));                 // Output to notification
  });

//
//    Copy bower components to assets-folder
//
//////////////////////////////////////////////////////////////////////
  gulp.task('copy', function(){
    return gulp.src('bower_components/modernizr/modernizr.js')   // Gets Modernizr.js
    .pipe(uglify())                                       // Uglify(minify)
    .pipe(rename({suffix: '.min'}))                       // Rename it
    .pipe(gulp.dest('assets/js/'))                        // Set destination to assets/js
    .pipe(notify('Files copied'));                        // Output to notification
  });

//
//    JS hint
//
//////////////////////////////////////////////////////////////////////
  gulp.task('jshint', function() {
    return gulp.src('assets/js/_*.js')
      .pipe(jshint())
      .pipe(jshint.reporter('jshint-stylish'));
  });

//
//    Minify all SVGs and images
//
//////////////////////////////////////////////////////////////////////
  gulp.task('svgmin', function() {
    return gulp.src('assets/img/*.svg')                          // Gets all SVGs
    .pipe(svgmin())                                       // Minifies SVG
    .pipe(gulp.dest('assets/img_min/'))                   // Set destination to assets/img_min/
    .pipe(notify('SVGs minified'));                        // Output to notification
  });

  gulp.task('imagemin', function () {
    return gulp.src(['assets/img/*', '!assets/img/*.svg'])        // Gets all images except SVGs
    .pipe(imagemin())                                      // Minifies
    .pipe(gulp.dest('assets/img_min/'))                    // Set destination to assets/img_min/
    .pipe(notify('Images minified'));                        // Output to notification
  });

//
//    Builds the theme into a .zip
//
//////////////////////////////////////////////////////////////////////
  gulp.task('zip',['sass', 'jshint', 'javascripts', 'copy'], function() {
    var server = livereload.listen();

    return gulp.src([
      '!**/.*',
      '!**/*.md',
      '!node_modules/**',
      '!node_modules',
      '!bower_components/**',
      '!bower_components',
      '!assets/scss/**',
      '!assets/scss',
      '**/**',
    ]).pipe(zip('theme.zip'))
      .pipe(gulp.dest('.'))
      .pipe(notify('Theme zip built'));
  });

  gulp.task('build',['zip'], function() {
      process.exit(0);
  });

//
//    Default gulp task.
//
//////////////////////////////////////////////////////////////////////
gulp.task('watch', function(){
  var server = livereload();
  gulp.watch('**/*.php').on('change', function(file) {
        server.changed(file.path);
        util.log(util.colors.yellow('PHP file changed' + ' (' + file.path + ')'));
    });

  gulp.watch("bower_components/foundation/scss/**/*.scss", ['sass']); // Runs sass on foundation components change
  gulp.watch("assets/scss/**/*.scss", ['sass']);                      // Watch and run sass on changes
  gulp.watch("assets/js/_*.js", ['jshint', 'javascripts']);           // Watch and run javascripts on changes
  gulp.watch("assets/img/*", ['imagemin', 'svgmin']);                 // Watch and minify images on changes

});

gulp.task('default', ['sass', 'jshint', 'javascripts', 'copy', 'watch']);
<|endoftext|>


<commit_before>var e = module.exports = {};

e.init = () => {

    e.category = bu.CommandType.GENERAL;
};

e.requireCtx = require;

e.isCommand = true;

e.hidden = false;
e.usage = 'mods [online]';
e.info = `Gets a list of mods.`;
e.longinfo = `<p>Gets a list of mods on the guild.</p>`;

e.execute = (msg, words) => {
    try {
        bu.guildSettings.get(msg.channel.guild.id, 'staffperms').then(val => {
            var allow = val || bu.defaultStaff;
            var includeOffline = true;
            if (words[1] && words[1].toLowerCase() == 'online') {
                includeOffline = false;
            }
            var mods = msg.channel.guild.members.filter(m => {
                return !m.user.bot && bu.comparePerms(m, allow)
                    && (includeOffline || m.status == 'online');
            });
            var maxLength = 0;
            mods.forEach(m => {
                if (getName(m).length > maxLength) {
                    maxLength = getName(m).length;
                }
            });
            var message = '';
            mods.filter(m => m.status == 'online').forEach(m => {
                message += `<:vpOnline:212789758110334977> **${getName(m)}** (${m.user.id})\n`;
            });
            mods.filter(m => m.status == 'idle').forEach(m => {
                message += `<:vpAway:212789859071426561> **${getName(m)}** (${m.user.id})\n`;
            });
            mods.filter(m => m.status == 'dnd').forEach(m => {
                message += `<:vpDnD:236744731088912384> **${getName(m)}** (${m.user.id})\n`;
            });
            mods.filter(m => m.status == 'offline').forEach(m => {
                message += `<:vpOffline:212790005943369728> **${getName(m)}** (${m.user.id})\n`;
            });

            bu.send(msg, message);
        });

    } catch (err) {
        logger.error(err);
    }
};
function getName(member) {
    return member.user.username;
}

function pad(value, length) {
    return (value.toString().length < length) ? pad(value + ' ', length) : value;
}<commit_msg>owo embeds<commit_after>var e = module.exports = {};

e.init = () => {

    e.category = bu.CommandType.GENERAL;
};

e.requireCtx = require;

e.isCommand = true;

e.hidden = false;
e.usage = 'mods [online | o | away | a | dnd | d | offline]';
e.info = `Gets a list of mods.`;
e.longinfo = `<p>Gets a list of mods on the guild.</p>`;

e.execute = (msg, words) => {
    try {
        bu.guildSettings.get(msg.channel.guild.id, 'staffperms').then(val => {
            var allow = val || bu.defaultStaff;
            let status = 0;
            if (words[1])
                switch (words[1].toLowerCase()) {
                    case 'o':
                    case 'online':
                        status = 1;
                        break;
                    case 'a':
                    case 'away':
                        status = 2;
                        break;
                    case 'd':
                    case 'dnd':
                        status = 3;
                        break;
                    case 'offline':
                        status = 4;
                        break;
                }
            var includeOffline = true;
            if (words[1] && words[1].toLowerCase() == 'online') {
                includeOffline = false;
            }
            var mods = msg.channel.guild.members.filter(m => {
                return !m.user.bot && bu.comparePerms(m, allow) &&
                    (includeOffline || m.status == 'online');
            });
            var maxLength = 0;
            mods.forEach(m => {
                if (getName(m).length > maxLength) {
                    maxLength = getName(m).length;
                }
            });
            var message = '';
            let online = [];
            if (status == 0 || status == 1)
                mods.filter(m => m.status == 'online').forEach(m => {
                    online.push(`<:vpOnline:212789758110334977> **${getName(m)}** (${m.user.id})`);
                });
            let away = [];
            if (status == 0 || status == 2)
                mods.filter(m => m.status == 'idle').forEach(m => {
                    away.push(`<:vpAway:212789859071426561> **${getName(m)}** (${m.user.id})`);
                });
            let dnd = [];
            if (status == 0 || status == 3)
                mods.filter(m => m.status == 'dnd').forEach(m => {
                    dnd.push(`<:vpDnD:236744731088912384> **${getName(m)}** (${m.user.id})`);
                });
            let offline = [];
            if (status == 0 || status == 4)
                mods.filter(m => m.status == 'offline').forEach(m => {
                    offline.push(`<:vpOffline:212790005943369728> **${getName(m)}** (${m.user.id})`);
                });

            let embed = {
                title: `Mods on **${msg.guild.name}**`,
                fields: [],
                color: bu.avatarColours[bu.avatarId],
                description: `Here's a list of mods:`
            }
            if (online.length > 0) embed.fields.push({
                name: 'Online',
                value: online.join('\n')
            });
            if (away.length > 0) embed.fields.push({
                name: 'Away',
                value: away.join('\n')
            });
            if (dnd.length > 0) embed.fields.push({
                name: 'Do Not Disturb',
                value: dnd.join('\n')
            });
            if (offline.length > 0) embed.fields.push({
                name: 'Offline',
                value: offline.join('\n')
            });
            logger.debug(embed.fields);
            if (embed.fields.length == 0) {
                embed.description = 'Whoops! There are no mods with that status!';
            }
            bu.send(msg, {
                embed: embed
            });
        });

    } catch (err) {
        logger.error(err);
    }
};

function getName(member) {
    return member.user.username;
}

function pad(value, length) {
    return (value.toString().length < length) ? pad(value + ' ', length) : value;
}<|endoftext|>


<commit_before>"use strict";

let s = require('underscore.string');
let url = require('url');
let cheerio = require('cheerio');
let fetchHelper = require('helpers/fetch');
let dateHelper = require('helpers/date');

const theatreKey = 'novascena';
const baseUrl = 'http://novascena.org';
const sourceUrl = baseUrl + '/?page=playbills';

const defaultScene = 'main';

let fetcher = function(callback) {

    const today = new Date();
    const month = today.getMonth();
    const year = today.getFullYear();

    fetchHelper.getContent(sourceUrl, function(err, content) {
        if (err) return callback(err);
        callback(null, getSchedule(content));
    });

    function getSchedule(content) {
        const parsedShows = parseShows(content);
        const translatedShows = parsedShows.map(translateRawShow).filter(show => show !== null);
        return translatedShows.reduce(fetchHelper.splitShowByDates, []);
    }

    function parseShows(content) {
        let $ = cheerio.load(content);
        return $('.slider>ul>li').map(function (index, li) {
            let $li = $(li);
            let show = {};

            show.theatre = theatreKey;
            show.scene = defaultScene;
            show.date = $li.find('.date').text();
            const $image = $li.find('img');
            show.title = $image.attr('alt');
            show.image = $image.attr('src');
            const $links = $li.find('.links-list li a');
            show.url = $($links.get(0)).attr('href');
            show.playUrl = $($links.get(1)).attr('href');
            show.buyTicketUrl = $li.find('a.buy').attr('href');

            return show;
        }).get();
    }

    function translateRawShow(rawShow) {
        const dateRegExp = new RegExp('(\\d+:\\d+)\\s+\/\\s+(\\d+)\\s+(' +
            dateHelper.getMonthsNames('ru').join('|') + ')\\s+(\\d+)', 'i');
        const dateMatch = rawShow.date.match(dateRegExp);
        if (!dateMatch) {
            console.warn('Novascena: Unable to parse date: ' + rawShow.date);
            return null;
        }
        const mappedTime = dateMatch[1];
        const mappedDay = dateMatch[2];
        const mappedMonth = dateHelper.mapMonth(dateMatch[3].toLowerCase(), 'ru');
        if (mappedMonth < 0) {
            console.warn('Novascena: Unable to map month: ' + dateMatch[3].toLowerCase().toLowerCase());
            return null;
        }
        const mappedYear = dateMatch[4];
        const show = {
            theatre: rawShow.theatre,
            theatreRawData: {
                title: 'ЦИC «Новая Сцена»',
                url: baseUrl,
                hasFetcher: true,
            },
            title: rawShow.title,
            scene: rawShow.scene,
            date: new Date(mappedYear, mappedMonth, mappedDay, ...mappedTime.split(':')),
            image: url.resolve(sourceUrl, rawShow.image),
            url: url.resolve(sourceUrl, rawShow.url),
            playUrl: url.resolve(sourceUrl, rawShow.playUrl),
        };
        if (rawShow.buyTicketUrl) {
            show.buyTicketUrl = url.resolve(sourceUrl, rawShow.buyTicketUrl);
        }
        return show;
    }
};

module.exports.fetch = fetcher;<commit_msg>Use ru_short for month names for novascena.<commit_after>"use strict";

let s = require('underscore.string');
let url = require('url');
let cheerio = require('cheerio');
let fetchHelper = require('helpers/fetch');
let dateHelper = require('helpers/date');

const theatreKey = 'novascena';
const baseUrl = 'http://novascena.org';
const sourceUrl = baseUrl + '/?page=playbills';

const defaultScene = 'main';

let fetcher = function(callback) {

    const today = new Date();
    const month = today.getMonth();
    const year = today.getFullYear();

    fetchHelper.getContent(sourceUrl, function(err, content) {
        if (err) return callback(err);
        callback(null, getSchedule(content));
    });

    function getSchedule(content) {
        const parsedShows = parseShows(content);
        const translatedShows = parsedShows.map(translateRawShow).filter(show => show !== null);
        return translatedShows.reduce(fetchHelper.splitShowByDates, []);
    }

    function parseShows(content) {
        let $ = cheerio.load(content);
        return $('.slider>ul>li').map(function (index, li) {
            let $li = $(li);
            let show = {};

            show.theatre = theatreKey;
            show.scene = defaultScene;
            show.date = $li.find('.date').text();
            const $image = $li.find('img');
            show.title = $image.attr('alt');
            show.image = $image.attr('src');
            const $links = $li.find('.links-list li a');
            show.url = $($links.get(0)).attr('href');
            show.playUrl = $($links.get(1)).attr('href');
            show.buyTicketUrl = $li.find('a.buy').attr('href');

            return show;
        }).get();
    }

    function translateRawShow(rawShow) {
        const dateRegExp = new RegExp('(\\d+:\\d+)\\s+\/\\s+(\\d+)\\s+(' +
            dateHelper.getMonthsNames('ru_short').join('|') + ')\\s+(\\d+)', 'i');
        const dateMatch = rawShow.date.match(dateRegExp);
        if (!dateMatch) {
            console.warn('Novascena: Unable to parse date: ' + rawShow.date);
            return null;
        }
        const mappedTime = dateMatch[1];
        const mappedDay = dateMatch[2];
        const mappedMonth = dateHelper.mapMonth(dateMatch[3].toLowerCase(), 'ru');
        if (mappedMonth < 0) {
            console.warn('Novascena: Unable to map month: ' + dateMatch[3].toLowerCase().toLowerCase());
            return null;
        }
        const mappedYear = dateMatch[4];
        const show = {
            theatre: rawShow.theatre,
            theatreRawData: {
                title: 'ЦИC «Новая Сцена»',
                url: baseUrl,
                hasFetcher: true,
            },
            title: rawShow.title,
            scene: rawShow.scene,
            date: new Date(mappedYear, mappedMonth, mappedDay, ...mappedTime.split(':')),
            image: url.resolve(sourceUrl, rawShow.image),
            url: url.resolve(sourceUrl, rawShow.url),
            playUrl: url.resolve(sourceUrl, rawShow.playUrl),
        };
        if (rawShow.buyTicketUrl) {
            show.buyTicketUrl = url.resolve(sourceUrl, rawShow.buyTicketUrl);
        }
        return show;
    }
};

module.exports.fetch = fetcher;<|endoftext|>