<commit_before>/**
 * Created by josh on 5/14/15.
 * @return {number}
 */

var test = require('tape');
var Literals = require("../src/Literals");
var ometajs = require('ometa-js');
var Parser = require('../parser_compiled.js').Parser;
var DefaultFunctions = require('../ui/DefaultFunctions');
var Context = require('../src/Context');
var Symbols = require('../src/Symbols');
var moment  = require('moment');
var ctx = Context.global();

DefaultFunctions.makeDefaultFunctions(ctx);

/*

summary of table, runs summary on each column, prints max,min, mean,median
-max of list
-max of table
-min of list
-min of table
-mean of list
-mean of table
summary of list
summary of table
NDJSON filename, only loads first 100
parse March 5th as a partial date
FilterByDateRange(data, 'column name', from: March 5th to: March 9th)
-Unique(list)
Unique(table,'column name')
-SplitUnique(table, 'column name')
Histogram(list, bucketcount)
-Histogram(table, bucketcount,'column name')

*/

/*
function makeList(min,max) {
    var arr = [];
    for(var i=min; i<=max; i++) {
        arr.push(Literals.makeNumber(i));
    }
    return Literals.makeList(arr);
}
function makeTable(count, def) {
    var arr = [];
    var cvals = {};
    for(var i=0; i<=count; i++) {
        var row = [];
        def.forEach(function(col){
            var key = col[0];
            if(!cvals.hasOwnProperty(key)) {
                cvals[key] = col[1];
            } else {
                cvals[key] += col[2];
            }
            //console.log("col",key,col[1],col[2]);
            row.push(Literals.makeKeyValue(key,Literals.makeNumber(cvals[key])));
        });
        arr.push(Literals.makeList(row));
    }
    //console.log(cvals);
    return Literals.makeList(arr);
}
*/
function testExpressionToNumber(str,correct,msg) {
    test(msg, function(t){
        var expr = Parser.matchAll(str,'start');
        expr.value(ctx).then(function(v) {
            t.equal(v.getNumber(),correct,msg);
            t.end();
        }).done();
    });
}

testExpressionToNumber("MakeList(10) => Min()",1,'Min(1->10)');
testExpressionToNumber("MakeList(10) => Max()",10,'Max(1->10)');
testExpressionToNumber("MakeList(9) => Mean()",5,'Max(1->9)');

/*
test('table functions', function(t){
    var table = makeTable(10,[['a',0,-1], ['b',0,5]]);
    t.equal(Min(table).itemByKey('a').getNumber(),-10,'min');
    t.equal(Min(table).itemByKey('b').getNumber(),0,'min');
    t.equal(Max(table).itemByKey('a').getNumber(),0,'max');
    t.equal(Max(table).itemByKey('b').getNumber(),50,'max');
    t.equal(Mean(table).itemByKey('a').getNumber(),-5,'mean');
    t.equal(Mean(table).itemByKey('b').getNumber(),25,'mean');
    t.end();
});
*/

test('Unique(list)', function(t) {
    var expr = Parser.matchAll("Unique([1,2,3,2,1,3,2,3,2])",'start');
    expr.value(ctx).then(function(v) {
        t.equal(v.length(),3,'Unique(list)');
        t.end();
    });
});

/*

function objectTableToLiteral(js) {
    return Literals.makeList(js.map(function(row) {
        return Literals.makeList(Object.keys(row).map(function(col) {
            return Literals.makeKeyValue(col, Literals.makeString(row[col]));
        }));
    }));
}

function SplitUnique(table, tcol) {
    var outputs = {};
    for(var i=0; i<table.length(); i++) {
        var row = table.item(i);
        var col = row.itemByKey(tcol);
        var key = col.getString();
        if(!outputs.hasOwnProperty(key)) {
            outputs[key] = Literals.makeList([]);
        }
        outputs[key]._value.push(row);
    }
    return Literals.makeList(Object.keys(outputs).map(function(key) {
        var value = outputs[key];
        return Literals.makeKeyValue(key, value);
    }));
}

test('split unique', function(t) {
    var jstable = [
        {a:'foo',b:'boo'},
        {a:'far',b:'bar'},
        {a:'faz',b:'baz'},
        {a:'foo',b:'booz'},
        {a:'far',b:'barz'},
        {a:'faz',b:'bazz'},
        {a:'far',b:'bars'},
    ];
    var table = objectTableToLiteral(jstable);
    var tables = SplitUnique(table,'a');
    t.equal(tables.length(),3);
    var table0 = tables.itemByKey('foo');
    t.equal(table0.length(),2);
    var table1 = tables.itemByKey('far');
    t.equal(table1.length(),3);
    var table2 = tables.itemByKey('faz');
    t.equal(table2.length(),2);
    t.end();
});

function numberArrayToLiteral(arr) {
    return Literals.makeList(arr.map(function(v){ return Literals.makeNumber(v)}));
}

function stringChartList(list) {
    var it = list.getIterator();
    while(it.hasNext()) {
        var val = it.next().getNumber();
        var str = "";
        for(var i=0; i<val; i++) {
            str += '#';
        }
        console.log(str);
    }
}

*/

test('histogram list', function(t){
    var str = "Histogram([5,7,1,6,6,6,7,7,2,3,4,3,7,8,5,8,7,9,6,8,5,6,5,2,4,0,7,4,8,5,7,6,3,8,8,8,4,8],5)";
    Parser.matchAll(str,'start').value(ctx).then(function(v) {
        //console.log("the value is",v.toString());
        t.equal(v.item(1).getNumber(),5,"correct bucket value");
        t.end();
    }).done();
});
/*
test('histogram table', function(t){
    var str = "Histogram([ [x:1, y:2], [x:1, y:3], [x:2, y:4] ],5,'x')";
    Parser.matchAll(str,'start').value(ctx).then(function(v) {
        console.log("the value is",v.toString());
        t.equal(v.item(1).getNumber(),5,"correct bucket value");
        t.end();
    }).done();
});
*/

//var data = NDJSON(‘events.json’,100);
test('load ND JSON data', function(t) {
    var str = "NDJSON('events.json',20000)";
    Parser.matchAll(str, 'start').value(ctx).then(function(v) {
        var count = 0;
        var it = v.getIterator();
        var cinfos = v.getColumnInfos();
        var tsinfo = cinfos.filter(function(cinfo) { return cinfo.id() == 'Timestamp'})[0];
        while(it.hasNext()) {
            var val = it.next();
            //console.log('timestamp = ',tsinfo.getValue(val));
            count++;
        }
        console.log('final count = ', count);
        t.equal(count,20000);
        t.end();
    }).done();
});

//var startDate = Date(month:4, day:1) // may 1st
test('date create', function(t) {
    var str = "Date(month:3, day:1)";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        t.equal(v.type,'date','type is date');
        t.equal(v.getDate().month(),3,'month is april/3');
        t.end();
    }).done();
});

//var endDate = Date(month:’may’, day:10) // may 10th
test('date create 2', function(t) {
    var str = "Date(month:'june', day:19)";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        t.equal(v.type,'date','type is date');
        t.equal(v.getDate().month(),5,'month is june/3');
        t.end();
    }).done();
});

test('set column format for timestamps', function(t) {
    //var str = "[ Date(month:'june', day:19), Date(month:'june', day:20), Date(month:'june', day:21) ]";
    var str = "setColumnFormat([ [ts:'1429041449644', event:1], [ts:'1429041449645', event:2], [ts:'1429042668312', event:3], [ts:'1431898583000',event:4] ], 'ts', type:'date', parsePattern:'x')";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        //t.equal(v.length(),3);
        var it = v.getIterator();
        //var cis = v.getColumnInfos();
        while(it.hasNext()) {
            var row = it.next();
            //console.log("row = ", row.toString());
            //cis.forEach(function(ci) {
            //    console.log("column ", ci.title(), '=', ci.print(row));
            //});
        }
        t.end();
    }).done();
});

//
function makeTimestampData(count,month, date, hour, minute) {
    var rows = [];
    for(var i=0; i<count; i++) {
        var mom = moment();
        mom.date(i*date);
        mom.month(i*month);
        mom.hour(i*hour);
        mom.minute(i*minute);
        //console.log("moment = ", mom.toString());
        var row = Literals.makeList([
            Literals.makeKeyValue('timestamp',Literals.makeDate(mom)),
            Literals.makeKeyValue('event',Literals.makeNumber(i))
        ]);
        rows.push(row);
    }
    return rows;
}

//var d2 = FilterByDateRange(data,column:’timestamp’,start:startDate, end:endDate) // filter by timestamp column
test('filter by date range', function(t) {
    var sym = Symbols.make('data');
    sym.update(Literals.makeList(makeTimestampData(100,0,0,0,1)));
    ctx.register(sym);
    var str = "FilterByDateRange(data, 'timestamp', start:Date(month:'may', day:3), end:Date(month:'may', day:7) )";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        var it = v.getIterator();
        var cis = v.getColumnInfos();
        while(it.hasNext()) {
            var row = it.next();
            //console.log("row = ", row.toString());
            cis.forEach(function(ci) {
                //console.log("column ", ci.title(), '=', ci.print(row));
            });
        }
        t.end();
    }).done();
});

test('function in list literal', function(t) {
    var str = "[9, 4+5, Sum([4,5])]";
    Parser.matchAll(str, 'start').value(ctx).then(function(v) {
        //console.log("v = ", v.toString());
        t.equal(v.length(),3);
        t.equal(v.item(0).getNumber(),9);
        t.equal(v.item(1).getNumber(),9);
        t.equal(v.item(2).getNumber(),9);
        t.end();
    });
});
/*
// BucketByDateTime(data, column:'timestamp', by:'weekday')
test('BucketByDatetime', function(t) {
    var sym = Symbols.make('data');
    sym.update(Literals.makeList(makeTimestampData(100,0,0,1,0)));
    ctx.register(sym);
    var str = "BucketByDateTime(data, column:'timestamp', by:'weekday')";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        console.log("got output count",v.toString());
        var it = v.getIterator();
        while(it.hasNext()) {
            var val = it.next();
            console.log('val = ', val.toString());
        }
        t.end();
    }).done();
});
*/

//TChart(d2, xaxis:’timestamp’) // charts into 1 hour buckets

//var d3 = Unique(d2,column:’Publisher’)
//list of unique values
//var d4 = SplitUnique(d2,column:’Publisher’)
//TChart(d4, xid:’timestamp’) // makes three charts into 1 hour buckets

<commit_msg>added more unit tests<commit_after>/**
 * Created by josh on 5/14/15.
 * @return {number}
 */

var test = require('tape');
var Literals = require("../src/Literals");
var ometajs = require('ometa-js');
var Parser = require('../parser_compiled.js').Parser;
var DefaultFunctions = require('../ui/DefaultFunctions');
var Context = require('../src/Context');
var Symbols = require('../src/Symbols');
var moment  = require('moment');
var DataUtil = require('../src/DataUtil');
var ctx = Context.global();

DefaultFunctions.makeDefaultFunctions(ctx);

/*

summary of table, runs summary on each column, prints max,min, mean,median
-max of list
-max of table
-min of list
-min of table
-mean of list
-mean of table
summary of list
summary of table
NDJSON filename, only loads first 100
parse March 5th as a partial date
FilterByDateRange(data, 'column name', from: March 5th to: March 9th)
-Unique(list)
Unique(table,'column name')
-SplitUnique(table, 'column name')
Histogram(list, bucketcount)
-Histogram(table, bucketcount,'column name')

*/

/*
function makeList(min,max) {
    var arr = [];
    for(var i=min; i<=max; i++) {
        arr.push(Literals.makeNumber(i));
    }
    return Literals.makeList(arr);
}
function makeTable(count, def) {
    var arr = [];
    var cvals = {};
    for(var i=0; i<=count; i++) {
        var row = [];
        def.forEach(function(col){
            var key = col[0];
            if(!cvals.hasOwnProperty(key)) {
                cvals[key] = col[1];
            } else {
                cvals[key] += col[2];
            }
            //console.log("col",key,col[1],col[2]);
            row.push(Literals.makeKeyValue(key,Literals.makeNumber(cvals[key])));
        });
        arr.push(Literals.makeList(row));
    }
    //console.log(cvals);
    return Literals.makeList(arr);
}
*/
function testExpressionToNumber(str,correct,msg) {
    test(msg, function(t){
        var expr = Parser.matchAll(str,'start');
        expr.value(ctx).then(function(v) {
            t.equal(v.getNumber(),correct,msg);
            t.end();
        }).done();
    });
}

testExpressionToNumber("MakeList(10) => Min()",1,'Min(1->10)');
testExpressionToNumber("MakeList(10) => Max()",10,'Max(1->10)');
testExpressionToNumber("MakeList(9) => Mean()",5,'Max(1->9)');

/*
test('table functions', function(t){
    var table = makeTable(10,[['a',0,-1], ['b',0,5]]);
    t.equal(Min(table).itemByKey('a').getNumber(),-10,'min');
    t.equal(Min(table).itemByKey('b').getNumber(),0,'min');
    t.equal(Max(table).itemByKey('a').getNumber(),0,'max');
    t.equal(Max(table).itemByKey('b').getNumber(),50,'max');
    t.equal(Mean(table).itemByKey('a').getNumber(),-5,'mean');
    t.equal(Mean(table).itemByKey('b').getNumber(),25,'mean');
    t.end();
});
*/

test('Unique(list)', function(t) {
    var expr = Parser.matchAll("Unique([1,2,3,2,1,3,2,3,2])",'start');
    expr.value(ctx).then(function(v) {
        t.equal(v.length(),3,'Unique(list)');
        t.end();
    });
});

/*

function SplitUnique(table, tcol) {
    var outputs = {};
    for(var i=0; i<table.length(); i++) {
        var row = table.item(i);
        var col = row.itemByKey(tcol);
        var key = col.getString();
        if(!outputs.hasOwnProperty(key)) {
            outputs[key] = Literals.makeList([]);
        }
        outputs[key]._value.push(row);
    }
    return Literals.makeList(Object.keys(outputs).map(function(key) {
        var value = outputs[key];
        return Literals.makeKeyValue(key, value);
    }));
}

test('split unique', function(t) {
    var jstable = [
        {a:'foo',b:'boo'},
        {a:'far',b:'bar'},
        {a:'faz',b:'baz'},
        {a:'foo',b:'booz'},
        {a:'far',b:'barz'},
        {a:'faz',b:'bazz'},
        {a:'far',b:'bars'},
    ];
    var table = objectTableToLiteral(jstable);
    var tables = SplitUnique(table,'a');
    t.equal(tables.length(),3);
    var table0 = tables.itemByKey('foo');
    t.equal(table0.length(),2);
    var table1 = tables.itemByKey('far');
    t.equal(table1.length(),3);
    var table2 = tables.itemByKey('faz');
    t.equal(table2.length(),2);
    t.end();
});

function numberArrayToLiteral(arr) {
    return Literals.makeList(arr.map(function(v){ return Literals.makeNumber(v)}));
}

function stringChartList(list) {
    var it = list.getIterator();
    while(it.hasNext()) {
        var val = it.next().getNumber();
        var str = "";
        for(var i=0; i<val; i++) {
            str += '#';
        }
        console.log(str);
    }
}

*/

test('histogram list', function(t){
    var str = "Histogram([5,7,1,6,6,6,7,7,2,3,4,3,7,8,5,8,7,9,6,8,5,6,5,2,4,0,7,4,8,5,7,6,3,8,8,8,4,8],5)";
    Parser.matchAll(str,'start').value(ctx).then(function(v) {
        //console.log("the value is",v.toString());
        t.equal(v.item(1).getNumber(),5,"correct bucket value");
        t.end();
    }).done();
});
/*
test('histogram table', function(t){
    var str = "Histogram([ [x:1, y:2], [x:1, y:3], [x:2, y:4] ],5,'x')";
    Parser.matchAll(str,'start').value(ctx).then(function(v) {
        console.log("the value is",v.toString());
        t.equal(v.item(1).getNumber(),5,"correct bucket value");
        t.end();
    }).done();
});
*/

//var data = NDJSON(‘events.json’,100);
test('load ND JSON data', function(t) {
    var str = "NDJSON('events.json',20000)";
    Parser.matchAll(str, 'start').value(ctx).then(function(v) {
        var count = 0;
        var it = v.getIterator();
        var cinfos = v.getColumnInfos();
        var tsinfo = cinfos.filter(function(cinfo) { return cinfo.id() == 'Timestamp'})[0];
        while(it.hasNext()) {
            var val = it.next();
            //console.log('timestamp = ',tsinfo.getValue(val));
            count++;
        }
        console.log('final count = ', count);
        t.equal(count,20000);
        t.end();
    }).done();
});

test('NDJSON => setColumnFormat', function(t) {
    var str = "NDJSON('events.json',100) => setColumnFormat('Timestamp', type:'date', parsePattern:'x')";
    Parser.matchAll(str, 'start').value(ctx).then(function(v) {
        var it = v.getIterator();
        var tsinfo = DataUtil.findColumnInfoFor(v,'Timestamp');
        t.equal(tsinfo.getValue(it.next()).isValid(),true,'valid moment');
        var count = 1;
        while(it.hasNext()) {
            var val = it.next();
            count++;
        }
        t.equal(count,100);
        t.end();
    }).done();
});

//var startDate = Date(month:4, day:1) // may 1st
test('date create', function(t) {
    var str = "Date(month:3, day:1)";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        t.equal(v.type,'date','type is date');
        t.equal(v.getDate().month(),3,'month is april/3');
        t.end();
    }).done();
});

//var endDate = Date(month:’may’, day:10) // may 10th
test('date create 2', function(t) {
    var str = "Date(month:'june', day:19)";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        t.equal(v.type,'date','type is date');
        t.equal(v.getDate().month(),5,'month is june/3');
        t.end();
    }).done();
});

test('set column format for timestamps', function(t) {
    //var str = "[ Date(month:'june', day:19), Date(month:'june', day:20), Date(month:'june', day:21) ]";
    var str = "setColumnFormat([ [ts:'1429041449644', event:1], [ts:'1429041449645', event:2], [ts:'1429042668312', event:3], [ts:'1431898583000',event:4] ], 'ts', type:'date', parsePattern:'x')";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        //t.equal(v.length(),3);
        var it = v.getIterator();
        //var cis = v.getColumnInfos();
        while(it.hasNext()) {
            var row = it.next();
            //console.log("row = ", row.toString());
            //cis.forEach(function(ci) {
            //    console.log("column ", ci.title(), '=', ci.print(row));
            //});
        }
        t.end();
    }).done();
});

//
function makeTimestampData(count,month, date, hour, minute) {
    var rows = [];
    for(var i=0; i<count; i++) {
        var mom = moment();
        mom.date(i*date);
        mom.month(i*month);
        mom.hour(i*hour);
        mom.minute(i*minute);
        //console.log("moment = ", mom.toString());
        var row = Literals.makeList([
            Literals.makeKeyValue('timestamp',Literals.makeDate(mom)),
            Literals.makeKeyValue('event',Literals.makeNumber(i))
        ]);
        rows.push(row);
    }
    return rows;
}

//var d2 = FilterByDateRange(data,column:’timestamp’,start:startDate, end:endDate) // filter by timestamp column
test('filter by date range', function(t) {
    var sym = Symbols.make('data');
    sym.update(Literals.makeList(makeTimestampData(100,0,0,0,1)));
    ctx.register(sym);
    var str = "FilterByDateRange(data, 'timestamp', start:Date(month:'may', day:3), end:Date(month:'may', day:7) )";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        var it = v.getIterator();
        var cis = v.getColumnInfos();
        while(it.hasNext()) {
            var row = it.next();
            //console.log("row = ", row.toString());
            cis.forEach(function(ci) {
                //console.log("column ", ci.title(), '=', ci.print(row));
            });
        }
        t.end();
    }).done();
});

test('function in list literal', function(t) {
    var str = "[9, 4+5, Sum([4,5])]";
    Parser.matchAll(str, 'start').value(ctx).then(function(v) {
        //console.log("v = ", v.toString());
        t.equal(v.length(),3);
        t.equal(v.item(0).getNumber(),9);
        t.equal(v.item(1).getNumber(),9);
        t.equal(v.item(2).getNumber(),9);
        t.end();
    });
});
/*
// BucketByDateTime(data, column:'timestamp', by:'weekday')
test('BucketByDatetime', function(t) {
    var sym = Symbols.make('data');
    sym.update(Literals.makeList(makeTimestampData(100,0,0,1,0)));
    ctx.register(sym);
    var str = "BucketByDateTime(data, column:'timestamp', by:'weekday')";
    Parser.matchAll(str, 'start').value(ctx).then(function(v){
        console.log("got output count",v.toString());
        var it = v.getIterator();
        while(it.hasNext()) {
            var val = it.next();
            console.log('val = ', val.toString());
        }
        t.end();
    }).done();
});
*/

//TChart(d2, xaxis:’timestamp’) // charts into 1 hour buckets

//var d3 = Unique(d2,column:’Publisher’)
//list of unique values
//var d4 = SplitUnique(d2,column:’Publisher’)
//TChart(d4, xid:’timestamp’) // makes three charts into 1 hour buckets

<|endoftext|>


<commit_before>// Copyright (c) 2011, Centrum Cyfrowe
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Centrum Cyfrowe nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var _gui = (function () {

// P U B L I C   I N T E R F A C E
    var that = {};

    that.init_gui = function () {
        $('#table-tab').click( function () {
            $('#table-container').show();
            $('#download-container').hide();
            $('#permalink-container').hide();

            $('#tabs')
                .find('div')
                .removeClass('active')
                .addClass('inactive');

            $(this)
                .addClass('active')
                .removeClass('inactive');
        });
        $('#download-tab').click( function () {
            var html = [];

            $('.snapshot').each( function () {
                var id = $(this).attr('id').split('-');
                var group = id[1];
                var sheet = id[2];
                var name = $(this).attr('title');

                html.push( '
' );
                html.push( '' );
                html.push( '<input type="checkbox" ');
                html.push( 'id="', ( group + '-' + sheet ));
                html.push( '" />	' );
                html.push( '' );
                html.push( '<input type="radio" name="scope-', group, '-', sheet );
                html.push( '" value="sheet" checked />' )
                html.push( '	' );
                html.push( '	' );
                html.push( '' );
                if( sheet === '0' ) {
                    html.push( '<input type="radio" name="scope-', group, '-', sheet );
                    html.push( '" value="full" />' )
                    html.push( '	' );
                    html.push( '' );
                    html.push( name );
                }
                else {
                    html.push( '	' );
                    html.push( '' );
                    html.push( ' > ' + name );
                }
                html.push( '	' );
                html.push( '
' );
            });
            $('#download-table > tbody').empty().append( html.join('') );

            $('#download-table .radio > input').attr( 'disabled', 'true' );
            $('#download-table .check > input' ).change( function () {
                if( $(this).attr('checked') ) {
                    $(this).parent().parent().find('.radio > input').removeAttr('disabled');
                }
                else {
                    $(this).parent().parent().find('.radio > input').attr('disabled', 'true');
                }
            });

            $('#table-container').hide();
            $('#download-container').show();
            $('#permalink-container').hide();

            $('#tabs')
                .find('div')
                .removeClass('active')
                .addClass('inactive');

            $(this)
                .addClass('active')
                .removeClass('inactive');
        });
        $('#permalink-tab').click( function () {
            $('#table-container').hide();
            $('#download-container').hide();
            $('#permalink-container').show();

            $('#tabs')
                .find('div')
                .removeClass('active')
                .addClass('inactive');

            $(this)
                .addClass('active')
                .removeClass('inactive');
        });

        $('#download-button').click( function () {
            var ids = {};
            var table = $('#download-table');
            var checkboxes = table.find('input:checkbox:checked');

            checkboxes.each( function () {
                var box = $(this);
                var box_id = box.attr('id');
                var group = box_id.split('-')[0];
                var sheet = box_id.split('-')[1];
                var scope = table
                                .find('input:radio[name=scope-'+box_id+']:checked')
                                .val();

                if( !ids[group] ) {
                    ids[group] = [];
                }

                ids[group].push({
                    id: sheet,
                    scope: scope
                });
            });

            _download.selected( ids );
        });

        $('#download-container').find('.radio > input').hide();
        $('#download-container').find('.check > input').change( function () {
            var radios = $('#download-container').find('.radio > input');
            if( $(this).attr('checked') ) {
                radios.show();
            }
            else {
                radios.hide();
            }
        });
        _tools.prepare_tools();

        // arm open/close button and hide it!
        $('#open-close-choose-panel')
            .click( function () {
                // if choose panel is open
                if( $('#choose-panel:visible').length > 0 ) {
                    hide_choose_panel();
                }
                else {
                    show_choose_panel();
                }
            });

        // arm back-to-datasets button
        $('#back-to-datasets')
            .hover(
                function () {
                    $(this)
                        .attr( 'src', '/site_media/img/datasets_over.png' )
                        .css('cursor', 'pointer');
                },
                function () {
                    $(this).attr( 'src', '/site_media/img/datasets.png' );
                }
            )
            .click( function () {
                $('#info-title')
                    .html('Dostępne kolekcje');
                $('#info-text')
                    .html('Wybierz jedną z dostępnych kolekcji danych.');

                $(this)
                    .hide();
                $('#choose-perspectives')
                    .hide();
                $('#choose-datasets')
                    .show();

                // clear the perspectives list
                $('#choose-perspectives')
                    .html('');
            });

        init_choose_panel();
    };

    // used once when some perspective is chosen for the very first time
    that.init_app = function ( collection_name ) {
        $('#open-close-choose-panel')
            .show();

        that.create_sheet_tab({
            // TODO move name from func arg to store!!
            name: collection_name,
            group_nr: _store.active_group_index(),
            sheet_nr: _store.active_sheet_index()
        });

        $('#application').show();
        that.make_zebra();
        hide_choose_panel();
    }

    that.create_sheet_tab = function ( args ) {
        var html = [];

        var group_nr = args.group_nr || _store.active_group_index();
        var sheet_nr = args.sheet_nr || _store.active_sheet_index();
        var sheet_name = args.name || "Arkusz " + group_nr + '-' + sheet_nr;

        var group_changed = _store.active_group_index() !== group_nr;
        var sheet_changed = _store.active_sheet_index() !== sheet_nr;
        var new_tab;

        html.push( '
' );
        html.push( sheet_name.length > 13 ?
                   sheet_name.slice( 0, 10 ) + '...' :
                   sheet_name );
        html.push( '
' );

        new_tab = $( html.join('') );

        $('.snapshot').removeClass('active');
        new_tab
            .addClass( 'active' )
            .click( function () {
                var id_elements = $(this).attr('id').split('-');
                var group_nr = id_elements[1];
                var sheet_nr = id_elements[2];

                $('.snapshot').removeClass('active');
                $(this).addClass('active');

                _store.active_group( group_nr );
                _store.active_sheet( sheet_nr );

                $('#sort-form').hide();
                $('#filter-form').hide();
                _table.clean_table();
                _table.init_table();
            });

        if( sheet_nr === 0 ) {
            new_tab.insertBefore( '#save-snapshot' );
        }
        else {
            new_tab.insertAfter( '#snap-'+group_nr+'-'+(sheet_nr-1));
        }

        if( $('.snapshot').length == 10 ) {
            $('#save-snapshot' ).hide();
        }

        _store.active_sheet( sheet_nr );
        _store.active_group( group_nr );

        $('#sort-form').hide();
        $('#filter-form').hide();
        _table.clean_table();
        _table.init_table();
    };

    that.make_zebra = function () {
        $('tbody > tr').not(':hidden').each( function ( i ) {
            if( i % 2 === 0 ) {
                $(this).removeClass( 'odd' );

                $(this).addClass( 'even' );
            }
            else {
                $(this).removeClass( 'even' );
                $(this).addClass( 'odd' );
            }
        });
    };

    that.highlight_node = function () {
        var a_root = $('tr[data-selected="true"]');
        // next a-level node
        var a_root_index = parseInt( a_root.attr( 'data-index' ), 10 );
        var next = $('tr[data-index='+ (a_root_index + 1) +']');

        if( a_root.length === 0 ) {
            // nothing found - nothing to do
            return;
        }
        else {
            // jquery returns a list of objects even for single entity
            a_root = a_root[0];
        }

        a_root
            .siblings()
//            .not(':hidden')
            .addClass('dim');

        // make a-root background black
        $('tr.root').removeClass('root');
        a_root.addClass('root');

        // highlight the subtree
        _utils.with_subtree( a_root.attr('id'), function () {
            // uses 'this' instead of '$(this)' for fun.call reason
            this.removeClass( 'dim' );
        });

        // add the bottom border
        $('.next').removeClass('next');
        next.addClass('next');

//        that.make_zebra();
    }

    return that;

// P R I V A T E   I N T E R F A C E
    function show_choose_panel() {
        $('#choose-panel')
            .slideDown( 400 );

        $('#application')
            .animate({ opacity: 0.25 }, 300 );

        $('#open-close-choose-panel')
            .html('zwiń');
    }

    function hide_choose_panel() {
        $('#choose-panel')
            .slideUp( 400 );

        $('#application')
            .animate({ opacity: 1 }, 300 );

        $('#open-close-choose-panel')
            .html('zmień dane');
    };

    function init_choose_panel() {
        var i;
        var html = [];
        var datasets = _store.meta_datasets();
        var len = datasets.length;
        var mid = len % 2 === 0 ? Math.floor( len / 2 )-1 : Math.floor( len / 2 );

        html.push( '
' );
        for( i = 0; i < len; i += 1 ) {
            html.push( '
' );
            html.push( '
' );
            html.push( datasets[i]['name'] );
            html.push( '
' );
            html.push( '
' );
            html.push( datasets[i]['description'] );
            html.push( '
' );
            html.push( '
Zobacz dane
' );
            html.push( '
' );

            if( i === mid ) {
                html.push( '

' );
            }
        }
        html.push( '
' );
        $('#choose-datasets')
            .append( $( html.join('') ))
            .find( '.position' )
            .find( '.more' )
            .click( function () {
                var dataset_id = $(this).parent().attr( 'data-set-id' );

                create_perspectives_panel( dataset_id );

                $('#choose-datasets')
                    .hide();
            });
    };

    function create_perspectives_panel( dataset_id ) {
        var i, j;
        var html = [];
        var perspectives = _store.meta_perspectives( dataset_id );
        var len = perspectives.length;
        var mid = len % 2 === 0 ? Math.floor( len / 2 )-1 : Math.floor( len / 2 );
        var issues;
        var name;

        html.push( '
' );
        for( i = 0; i < len; i += 1 ) {
            issues = perspectives[i]['issues'];

            html.push( '
' );
            html.push( '
' );
            html.push( perspectives[i]['name'] );
            html.push( '
' );
            html.push( '
' );
            html.push( perspectives[i]['description'] );
            html.push( '
' );
            // iterates in revers order because of CSS "float: right"
            for( j = issues.length-1; j >= 0; j -= 1 ) {
                html.push( '
' );
                html.push( issues[j] );
                html.push( '
' );
            }
            html.push( '
Zobacz dane:
' );
            html.push( '
' );

            if( i === mid ) {
                html.push( '

' );
            }
        }
        html.push( '
' );

        $('#info-title')
            // TODO interface this call!!!
            .html( _store.meta_datasets()[ dataset_id ]['name'] );
        $('#info-text')
            .html('Wybierz jedno z wydań danych.');

        $('#back-to-datasets')
            .show();

        $('#choose-perspectives')
            .append( $( html.join('') ))
            .show()
            .find( '.position' )
            .find( '.more' )
            .click( function () {
                var col_id = {
                    dataset: $(this).parent().attr( 'data-set-id' ),
                    perspective: $(this).parent().attr( 'data-per-id' ),
                    issue: $(this).attr( 'data-issue' )
                };

                // if new group is created, get data and show table
                if( _store.group_exists( col_id ) ) {
                    // get top-level data from db
                    _db.get_init_data(col_id);
                }
                else {
                    // go back to application with focus on requested sheet
                    hide_choose_panel();
                }
            });
    };

})();
<commit_msg>Hiding radio buttons in dwonload panel added<commit_after>// Copyright (c) 2011, Centrum Cyfrowe
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Centrum Cyfrowe nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var _gui = (function () {

// P U B L I C   I N T E R F A C E
    var that = {};

    that.init_gui = function () {
        $('#table-tab').click( function () {
            $('#table-container').show();
            $('#download-container').hide();
            $('#permalink-container').hide();

            $('#tabs')
                .find('div')
                .removeClass('active')
                .addClass('inactive');

            $(this)
                .addClass('active')
                .removeClass('inactive');
        });
        $('#download-tab').click( function () {
            var html = [];

            $('.snapshot').each( function () {
                var id = $(this).attr('id').split('-');
                var group = id[1];
                var sheet = id[2];
                var name = $(this).attr('title');

                html.push( '
' );
                html.push( '' );
                html.push( '<input type="checkbox" ');
                html.push( 'id="', ( group + '-' + sheet ));
                html.push( '" />	' );
                html.push( '' );
                html.push( '<input type="radio" name="scope-', group, '-', sheet );
                html.push( '" value="sheet" checked />' )
                html.push( '	' );
                html.push( '	' );
                html.push( '' );
                if( sheet === '0' ) {
                    html.push( '<input type="radio" name="scope-', group, '-', sheet );
                    html.push( '" value="full" />' )
                    html.push( '	' );
                    html.push( '' );
                    html.push( name );
                }
                else {
                    html.push( '	' );
                    html.push( '' );
                    html.push( ' > ' + name );
                }
                html.push( '	' );
                html.push( '
' );
            });
            $('#download-table > tbody').empty().append( html.join('') );

            $('#download-table .radio > input').hide();
            $('#download-table .check > input' ).change( function () {
                if( $(this).attr('checked') ) {
                    $(this).parent().parent().find('.radio > input').show();
                }
                else {
                    $(this).parent().parent().find('.radio > input').hide();
                }
            });

            $('#table-container').hide();
            $('#download-container').show();
            $('#permalink-container').hide();

            $('#tabs')
                .find('div')
                .removeClass('active')
                .addClass('inactive');

            $(this)
                .addClass('active')
                .removeClass('inactive');
        });
        $('#permalink-tab').click( function () {
            $('#table-container').hide();
            $('#download-container').hide();
            $('#permalink-container').show();

            $('#tabs')
                .find('div')
                .removeClass('active')
                .addClass('inactive');

            $(this)
                .addClass('active')
                .removeClass('inactive');
        });

        $('#download-button').click( function () {
            var ids = {};
            var table = $('#download-table');
            var checkboxes = table.find('input:checkbox:checked');

            checkboxes.each( function () {
                var box = $(this);
                var box_id = box.attr('id');
                var group = box_id.split('-')[0];
                var sheet = box_id.split('-')[1];
                var scope = table
                                .find('input:radio[name=scope-'+box_id+']:checked')
                                .val();

                if( !ids[group] ) {
                    ids[group] = [];
                }

                ids[group].push({
                    id: sheet,
                    scope: scope
                });
            });

            _download.selected( ids );
        });

        _tools.prepare_tools();

        // arm open/close button and hide it!
        $('#open-close-choose-panel')
            .click( function () {
                // if choose panel is open
                if( $('#choose-panel:visible').length > 0 ) {
                    hide_choose_panel();
                }
                else {
                    show_choose_panel();
                }
            });

        // arm back-to-datasets button
        $('#back-to-datasets')
            .hover(
                function () {
                    $(this)
                        .attr( 'src', '/site_media/img/datasets_over.png' )
                        .css('cursor', 'pointer');
                },
                function () {
                    $(this).attr( 'src', '/site_media/img/datasets.png' );
                }
            )
            .click( function () {
                $('#info-title')
                    .html('Dostępne kolekcje');
                $('#info-text')
                    .html('Wybierz jedną z dostępnych kolekcji danych.');

                $(this)
                    .hide();
                $('#choose-perspectives')
                    .hide();
                $('#choose-datasets')
                    .show();

                // clear the perspectives list
                $('#choose-perspectives')
                    .html('');
            });

        init_choose_panel();
    };

    // used once when some perspective is chosen for the very first time
    that.init_app = function ( collection_name ) {
        $('#open-close-choose-panel')
            .show();

        that.create_sheet_tab({
            // TODO move name from func arg to store!!
            name: collection_name,
            group_nr: _store.active_group_index(),
            sheet_nr: _store.active_sheet_index()
        });

        $('#application').show();
        that.make_zebra();
        hide_choose_panel();
    }

    that.create_sheet_tab = function ( args ) {
        var html = [];

        var group_nr = args.group_nr || _store.active_group_index();
        var sheet_nr = args.sheet_nr || _store.active_sheet_index();
        var sheet_name = args.name || "Arkusz " + group_nr + '-' + sheet_nr;

        var group_changed = _store.active_group_index() !== group_nr;
        var sheet_changed = _store.active_sheet_index() !== sheet_nr;
        var new_tab;

        html.push( '
' );
        html.push( sheet_name.length > 13 ?
                   sheet_name.slice( 0, 10 ) + '...' :
                   sheet_name );
        html.push( '
' );

        new_tab = $( html.join('') );

        $('.snapshot').removeClass('active');
        new_tab
            .addClass( 'active' )
            .click( function () {
                var id_elements = $(this).attr('id').split('-');
                var group_nr = id_elements[1];
                var sheet_nr = id_elements[2];

                $('.snapshot').removeClass('active');
                $(this).addClass('active');

                _store.active_group( group_nr );
                _store.active_sheet( sheet_nr );

                $('#sort-form').hide();
                $('#filter-form').hide();
                _table.clean_table();
                _table.init_table();
            });

        if( sheet_nr === 0 ) {
            new_tab.insertBefore( '#save-snapshot' );
        }
        else {
            new_tab.insertAfter( '#snap-'+group_nr+'-'+(sheet_nr-1));
        }

        if( $('.snapshot').length == 10 ) {
            $('#save-snapshot' ).hide();
        }

        _store.active_sheet( sheet_nr );
        _store.active_group( group_nr );

        $('#sort-form').hide();
        $('#filter-form').hide();
        _table.clean_table();
        _table.init_table();
    };

    that.make_zebra = function () {
        $('tbody > tr').not(':hidden').each( function ( i ) {
            if( i % 2 === 0 ) {
                $(this).removeClass( 'odd' );

                $(this).addClass( 'even' );
            }
            else {
                $(this).removeClass( 'even' );
                $(this).addClass( 'odd' );
            }
        });
    };

    that.highlight_node = function () {
        var a_root = $('tr[data-selected="true"]');
        // next a-level node
        var a_root_index = parseInt( a_root.attr( 'data-index' ), 10 );
        var next = $('tr[data-index='+ (a_root_index + 1) +']');

        if( a_root.length === 0 ) {
            // nothing found - nothing to do
            return;
        }
        else {
            // jquery returns a list of objects even for single entity
            a_root = a_root[0];
        }

        a_root
            .siblings()
//            .not(':hidden')
            .addClass('dim');

        // make a-root background black
        $('tr.root').removeClass('root');
        a_root.addClass('root');

        // highlight the subtree
        _utils.with_subtree( a_root.attr('id'), function () {
            // uses 'this' instead of '$(this)' for fun.call reason
            this.removeClass( 'dim' );
        });

        // add the bottom border
        $('.next').removeClass('next');
        next.addClass('next');

//        that.make_zebra();
    }

    return that;

// P R I V A T E   I N T E R F A C E
    function show_choose_panel() {
        $('#choose-panel')
            .slideDown( 400 );

        $('#application')
            .animate({ opacity: 0.25 }, 300 );

        $('#open-close-choose-panel')
            .html('zwiń');
    }

    function hide_choose_panel() {
        $('#choose-panel')
            .slideUp( 400 );

        $('#application')
            .animate({ opacity: 1 }, 300 );

        $('#open-close-choose-panel')
            .html('zmień dane');
    };

    function init_choose_panel() {
        var i;
        var html = [];
        var datasets = _store.meta_datasets();
        var len = datasets.length;
        var mid = len % 2 === 0 ? Math.floor( len / 2 )-1 : Math.floor( len / 2 );

        html.push( '
' );
        for( i = 0; i < len; i += 1 ) {
            html.push( '
' );
            html.push( '
' );
            html.push( datasets[i]['name'] );
            html.push( '
' );
            html.push( '
' );
            html.push( datasets[i]['description'] );
            html.push( '
' );
            html.push( '
Zobacz dane
' );
            html.push( '
' );

            if( i === mid ) {
                html.push( '

' );
            }
        }
        html.push( '
' );
        $('#choose-datasets')
            .append( $( html.join('') ))
            .find( '.position' )
            .find( '.more' )
            .click( function () {
                var dataset_id = $(this).parent().attr( 'data-set-id' );

                create_perspectives_panel( dataset_id );

                $('#choose-datasets')
                    .hide();
            });
    };

    function create_perspectives_panel( dataset_id ) {
        var i, j;
        var html = [];
        var perspectives = _store.meta_perspectives( dataset_id );
        var len = perspectives.length;
        var mid = len % 2 === 0 ? Math.floor( len / 2 )-1 : Math.floor( len / 2 );
        var issues;
        var name;

        html.push( '
' );
        for( i = 0; i < len; i += 1 ) {
            issues = perspectives[i]['issues'];

            html.push( '
' );
            html.push( '
' );
            html.push( perspectives[i]['name'] );
            html.push( '
' );
            html.push( '
' );
            html.push( perspectives[i]['description'] );
            html.push( '
' );
            // iterates in revers order because of CSS "float: right"
            for( j = issues.length-1; j >= 0; j -= 1 ) {
                html.push( '
' );
                html.push( issues[j] );
                html.push( '
' );
            }
            html.push( '
Zobacz dane:
' );
            html.push( '
' );

            if( i === mid ) {
                html.push( '

' );
            }
        }
        html.push( '
' );

        $('#info-title')
            // TODO interface this call!!!
            .html( _store.meta_datasets()[ dataset_id ]['name'] );
        $('#info-text')
            .html('Wybierz jedno z wydań danych.');

        $('#back-to-datasets')
            .show();

        $('#choose-perspectives')
            .append( $( html.join('') ))
            .show()
            .find( '.position' )
            .find( '.more' )
            .click( function () {
                var col_id = {
                    dataset: $(this).parent().attr( 'data-set-id' ),
                    perspective: $(this).parent().attr( 'data-per-id' ),
                    issue: $(this).attr( 'data-issue' )
                };

                // if new group is created, get data and show table
                if( _store.group_exists( col_id ) ) {
                    // get top-level data from db
                    _db.get_init_data(col_id);
                }
                else {
                    // go back to application with focus on requested sheet
                    hide_choose_panel();
                }
            });
    };

})();
<|endoftext|>


<commit_before>import _ from 'lodash'

const channelId = (state = 'HOME_TIMELINE_CHANNEL', action) => {
  switch (action.type) {
  case 'CHANNEL_SELECTED':
    return action.channelId;
  default:
    return state;
  }
};

const homeTimelineTweets = (state = [], action) => {
  switch (action.type) {
  case 'HOME_TIMELINE_TWEET_RECEIVED':
    return [action.tweet, ...state].slice(0, 100);
  case 'HOME_TIMELINE_TWEETS_FETCHED':
    return [...action.tweets, ...state].slice(0, 100);
  case 'TWEET_FAVORITED':
  case 'TWEET_UNFAVORITED':
    const index = _.findIndex(state, (tweet) => {
      return tweet.id_str === action.tweet.id_str;
    });
    if (index === -1) {
      return state;
    } else {
      return [
        ...state.slice(0, index),
        action.tweet,
        ...state.slice(index + 1, -1)
      ];
    }
  default:
    return state;
  }
};

const listId = (state = null, action) => {
  switch (action.type) {
  case 'CHANNEL_SELECTED':
    switch (action.channelId) {
    case 'HOME_TIMELINE_CHANNEL':
    case 'SEARCH_CHANNEL':
      return state;
    default:
      return action.channelId;
    }
  default:
    return state;
  }
};

const lists = (state = [], action) => {
  switch (action.type) {
  case 'LISTS_FETCHED':
    return [...state, ...action.lists];
  default:
    return state;
  }
};

const listTweets = (state = [], action) => {
  switch (action.type) {
  case 'LIST_TWEETS_CLEARE':
    return [];
  case 'LIST_TWEETS_FETCHED':
    return [...action.tweets, ...state];
  case 'TWEET_FAVORITED':
  case 'TWEET_UNFAVORITED':
    const index = _.findIndex(state, (tweet) => {
      return tweet.id_str === action.tweet.id_str;
    });
    if (index === -1) {
      return state;
    } else {
      return [
        ...state.slice(0, index),
        action.tweet,
        ...state.slice(index + 1, -1)
      ];
    }
  default:
    return state;
  }
};

const searchedTweets = (state = [], action) => {
  switch (action.type) {
  case 'FILTERED_TWEET_RECEIVED':
    return [action.tweet, ...state];
  case 'TWEETS_SEARCHED':
    return [...action.tweets, ...state];
  case 'TWEET_FAVORITED':
  case 'TWEET_UNFAVORITED':
    const index = _.findIndex(state, (tweet) => {
      return tweet.id_str === action.tweet.id_str;
    });
    if (index === -1) {
      return state;
    } else {
      return [
        ...state.slice(0, index),
        action.tweet,
        ...state.slice(index + 1, -1)
      ];
    }
  default:
    return state;
  }
};

const user = (state = {}, action) => {
  switch (action.type) {
  case 'USER_FETCHED':
    return action.user;
  default:
    return state;
  }
};

export default (state = {}, action) => {
  return {
    user: user(state.user, action),
    channelId: channelId(state.channelId, action),
    homeTimelineTweets: homeTimelineTweets(state.homeTimelineTweets, action),
    lists: lists(state.lists, action),
    listId: listId(state.listId, action),
    listTweets: listTweets(state.listTweets, action),
    searchedTweets: searchedTweets(state.searchedTweets, action)
  };
}
<commit_msg>Fix bug about clearing tweets on channel changed<commit_after>import _ from 'lodash'

const channelId = (state = 'HOME_TIMELINE_CHANNEL', action) => {
  switch (action.type) {
  case 'CHANNEL_SELECTED':
    return action.channelId;
  default:
    return state;
  }
};

const homeTimelineTweets = (state = [], action) => {
  switch (action.type) {
  case 'HOME_TIMELINE_TWEET_RECEIVED':
    return [action.tweet, ...state].slice(0, 100);
  case 'HOME_TIMELINE_TWEETS_FETCHED':
    return [...action.tweets, ...state].slice(0, 100);
  case 'TWEET_FAVORITED':
  case 'TWEET_UNFAVORITED':
    const index = _.findIndex(state, (tweet) => {
      return tweet.id_str === action.tweet.id_str;
    });
    if (index === -1) {
      return state;
    } else {
      return [
        ...state.slice(0, index),
        action.tweet,
        ...state.slice(index + 1, -1)
      ];
    }
  default:
    return state;
  }
};

const listId = (state = null, action) => {
  switch (action.type) {
  case 'CHANNEL_SELECTED':
    switch (action.channelId) {
    case 'HOME_TIMELINE_CHANNEL':
    case 'SEARCH_CHANNEL':
      return state;
    default:
      return action.channelId;
    }
  default:
    return state;
  }
};

const lists = (state = [], action) => {
  switch (action.type) {
  case 'LISTS_FETCHED':
    return [...state, ...action.lists];
  default:
    return state;
  }
};

const listTweets = (state = [], action) => {
  switch (action.type) {
  case 'LIST_TWEETS_CLEAR':
    return [];
  case 'LIST_TWEETS_FETCHED':
    return [...action.tweets, ...state];
  case 'TWEET_FAVORITED':
  case 'TWEET_UNFAVORITED':
    const index = _.findIndex(state, (tweet) => {
      return tweet.id_str === action.tweet.id_str;
    });
    if (index === -1) {
      return state;
    } else {
      return [
        ...state.slice(0, index),
        action.tweet,
        ...state.slice(index + 1, -1)
      ];
    }
  default:
    return state;
  }
};

const searchedTweets = (state = [], action) => {
  switch (action.type) {
  case 'FILTERED_TWEET_RECEIVED':
    return [action.tweet, ...state];
  case 'TWEETS_SEARCHED':
    return [...action.tweets, ...state];
  case 'TWEET_FAVORITED':
  case 'TWEET_UNFAVORITED':
    const index = _.findIndex(state, (tweet) => {
      return tweet.id_str === action.tweet.id_str;
    });
    if (index === -1) {
      return state;
    } else {
      return [
        ...state.slice(0, index),
        action.tweet,
        ...state.slice(index + 1, -1)
      ];
    }
  default:
    return state;
  }
};

const user = (state = {}, action) => {
  switch (action.type) {
  case 'USER_FETCHED':
    return action.user;
  default:
    return state;
  }
};

export default (state = {}, action) => {
  return {
    user: user(state.user, action),
    channelId: channelId(state.channelId, action),
    homeTimelineTweets: homeTimelineTweets(state.homeTimelineTweets, action),
    lists: lists(state.lists, action),
    listId: listId(state.listId, action),
    listTweets: listTweets(state.listTweets, action),
    searchedTweets: searchedTweets(state.searchedTweets, action)
  };
}
<|endoftext|>


<commit_before>;(function($$){ 'use strict';

  var defaults = {
    animate: true, // whether to show the layout as it's running
    maxSimulationTime: 4000, // max length in ms to run the layout
    fit: true, // on every layout reposition of nodes, fit the viewport
    padding: 30, // padding around the simulation
    boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
    ungrabifyWhileSimulating: false, // so you can't drag nodes during layout

    // callbacks on layout events
    ready: undefined, // callback on layoutready
    stop: undefined, // callback on layoutstop

    // forces used by arbor (use arbor default on undefined)
    repulsion: undefined,
    stiffness: undefined,
    friction: undefined,
    gravity: true,
    fps: undefined,
    precision: undefined,

    // static numbers or functions that dynamically return what these
    // values should be for each element
    // e.g. nodeMass: function(n){ return n.data('weight') }
    nodeMass: undefined,
    edgeLength: undefined,

    stepSize: 0.1, // smoothing of arbor bounding box

    // function that returns true if the system is stable to indicate
    // that the layout can be stopped
    stableEnergy: function( energy ){
      var e = energy;
      return (e.max <= 0.5) || (e.mean <= 0.3);
    },

    // infinite layout options
    infinite: false // overrides all other options for a forces-all-the-time mode
  };

  function ArborLayout(options){
    this._private = {};

    this._private.options = $$.util.extend({}, defaults, options);
  }

  ArborLayout.prototype.run = function(){
    var layout = this;
    var options = this._private.options;

    $$.util.require('arbor', function(arbor){

      var cy = options.cy;
      var eles = options.eles;
      var nodes = eles.nodes().not(':parent');
      var edges = eles.edges();
      var bb = $$.util.makeBoundingBox( options.boundingBox ? options.boundingBox : {
        x1: 0, y1: 0, w: cy.width(), h: cy.height()
      } );
      var simUpdatingPos = false;

      layout.trigger({ type: 'layoutstart', layout: layout });

      // backward compatibility for old animation option
      if( options.liveUpdate !== undefined ){
        options.animate = options.liveUpdate;
      }

      // arbor doesn't work with just 1 node
      if( cy.nodes().size() <= 1 ){
        if( options.fit ){
          cy.reset();
        }

        cy.nodes().position({
          x: Math.round( (bb.x1 + bb.x2)/2 ),
          y: Math.round( (bb.y1 + bb.y2)/2 )
        });

        layout.one('layoutready', options.ready);
        layout.trigger({ type: 'layoutready', layout: layout });

        layout.one('layoutstop', options.stop);
        layout.trigger({ type: 'layoutstop', layout: layout });

        return;
      }

      var sys = layout._private.system = arbor.ParticleSystem();

      sys.parameters({
        repulsion: options.repulsion,
        stiffness: options.stiffness,
        friction: options.friction,
        gravity: options.gravity,
        fps: options.fps,
        dt: options.dt,
        precision: options.precision
      });

      if( options.animate && options.fit ){
        cy.fit( bb, options.padding );
      }

      var doneTime = 250;
      var doneTimeout;

      var ready = false;

      var lastDraw = +new Date();
      var sysRenderer = {
        init: function(system){
        },
        redraw: function(){
          var energy = sys.energy();

          // if we're stable (according to the client), we're done
          if( !options.infinite && options.stableEnergy != null && energy != null && energy.n > 0 && options.stableEnergy(energy) ){
            layout.stop();
            return;
          }

          if( !options.infinite && doneTime != Infinity ){
            clearTimeout(doneTimeout);
            doneTimeout = setTimeout(doneHandler, doneTime);
          }

          var movedNodes = cy.collection();

          sys.eachNode(function(n, point){
            var data = n.data;
            var node = data.element;

            if( node == null ){
              return;
            }

            if( !node.locked() && !node.grabbed() ){
              node.silentPosition({
                x: bb.x1 + point.x,
                y: bb.y1 + point.y
              });

              movedNodes.merge( node );
            }
          });

          if( options.animate && movedNodes.length > 0 ){
            simUpdatingPos = true;

            movedNodes.rtrigger('position');

            if( options.fit ){
              cy.fit( options.padding );
            }

            lastDraw = +new Date();
            simUpdatingPos = false;
          }

          if( !ready ){
            ready = true;
            layout.one('layoutready', options.ready);
            layout.trigger({ type: 'layoutready', layout: layout });
          }
        }

      };
      sys.renderer = sysRenderer;
      sys.screenSize( bb.w, bb.h );
      sys.screenPadding( options.padding, options.padding, options.padding, options.padding );
      sys.screenStep( options.stepSize );

      function calculateValueForElement(element, value){
        if( value == null ){
          return undefined;
        } else if( typeof value == typeof function(){} ){
          return value.apply(element, [element._private.data, {
            nodes: nodes.length,
            edges: edges.length,
            element: element
          }]);
        } else {
          return value;
        }
      }

      var grabHandler;
      nodes.on('grab free position', grabHandler = function(e){
        if( simUpdatingPos ){ return; }

        var pos = this.position();
        var apos = sys.fromScreen( pos );
        if( !apos ){ return; }

        var p = arbor.Point(apos.x, apos.y);
        var padding = options.padding;

        if(
          bb.x1 + padding <= pos.x && pos.x <= bb.x2 - padding &&
          bb.y1 + padding <= pos.y && pos.y <= bb.y2 - padding
        ){
          this.scratch().arbor.p = p;
        }

        switch( e.type ){
        case 'grab':
          this.scratch().arbor.fixed = true;
          break;
        case 'free':
          this.scratch().arbor.fixed = false;
          //this.scratch().arbor.tempMass = 1000;
          break;
        }
      });

      var lockHandler;
      nodes.on('lock unlock', lockHandler = function(e){
        node.scratch().arbor.fixed = node.locked();
      });

      var removeHandler;
      eles.on('remove', removeHandler = function(e){ return; // TODO enable when layout add/remove api added
        // var ele = this;
        // var arborEle = ele.scratch().arbor;

        // if( !arborEle ){ return; }

        // if( ele.isNode() ){
        //   sys.pruneNode( arborEle );
        // } else {
        //   sys.pruneEdge( arborEle );
        // }
      });

      var addHandler;
      cy.on('add', '*', addHandler = function(){ return; // TODO enable when layout add/remove api added
        // var ele = this;

        // if( ele.isNode() ){
        //   addNode( ele );
        // } else {
        //   addEdge( ele );
        // }
      });

      var resizeHandler;
      cy.on('resize', resizeHandler = function(){
        if( options.boundingBox == null && layout._private.system != null ){
          var w = cy.width();
          var h = cy.height();

          sys.screenSize( w, h );
        }
      });

      function addNode( node ){
        if( node.isFullAutoParent() ){ return; } // they don't exist in the sim

        var id = node._private.data.id;
        var mass = calculateValueForElement(node, options.nodeMass);
        var locked = node._private.locked;
        var nPos = node.position();

        var pos = sys.fromScreen({
          x: nPos.x,
          y: nPos.y
        });

        node.scratch().arbor = sys.addNode(id, {
          element: node,
          mass: mass,
          fixed: locked,
          x: locked ? pos.x : undefined,
          y: locked ? pos.y : undefined
        });
      }

      function addEdge( edge ){
        var src = edge.source().id();
        var tgt = edge.target().id();
        var length = calculateValueForElement(edge, options.edgeLength);

        edge.scratch().arbor = sys.addEdge(src, tgt, {
          length: length
        });
      }

      nodes.each(function(i, node){
        addNode( node );
      });

      edges.each(function(i, edge){
        addEdge( edge );
      });

      var grabbableNodes = nodes.filter(":grabbable");
      // disable grabbing if so set
      if( options.ungrabifyWhileSimulating ){
        grabbableNodes.ungrabify();
      }

      var doneHandler = layout._private.doneHandler = function(){
        layout._private.doneHandler = null;

        if( !options.animate ){
          if( options.fit ){
            cy.reset();
          }

          nodes.rtrigger('position');
        }

        // unbind handlers
        nodes.off('grab free position', grabHandler);
        nodes.off('lock unlock', lockHandler);
        eles.off('remove', removeHandler);
        cy.off('add', '*', addHandler);
        cy.off('resize', resizeHandler);

        // enable back grabbing if so set
        if( options.ungrabifyWhileSimulating ){
          grabbableNodes.grabify();
        }

        layout.one('layoutstop', options.stop);
        layout.trigger({ type: 'layoutstop', layout: layout });
      };

      sys.start();
      if( !options.infinite && options.maxSimulationTime != null && options.maxSimulationTime > 0 && options.maxSimulationTime !== Infinity ){
        setTimeout(function(){
          layout.stop();
        }, options.maxSimulationTime);
      }

    }); // require

    return this; // chaining
  };

  ArborLayout.prototype.stop = function(){
    if( this._private.system != null ){
      this._private.system.stop();
    }

    if( this._private.doneHandler ){
      this._private.doneHandler();
    }

    return this; // chaining
  };

  $$('layout', 'arbor', ArborLayout);

})(cytoscape);
<commit_msg>Arbor applies to entire graph if 1 element specified #962<commit_after>;(function($$){ 'use strict';

  var defaults = {
    animate: true, // whether to show the layout as it's running
    maxSimulationTime: 4000, // max length in ms to run the layout
    fit: true, // on every layout reposition of nodes, fit the viewport
    padding: 30, // padding around the simulation
    boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
    ungrabifyWhileSimulating: false, // so you can't drag nodes during layout

    // callbacks on layout events
    ready: undefined, // callback on layoutready
    stop: undefined, // callback on layoutstop

    // forces used by arbor (use arbor default on undefined)
    repulsion: undefined,
    stiffness: undefined,
    friction: undefined,
    gravity: true,
    fps: undefined,
    precision: undefined,

    // static numbers or functions that dynamically return what these
    // values should be for each element
    // e.g. nodeMass: function(n){ return n.data('weight') }
    nodeMass: undefined,
    edgeLength: undefined,

    stepSize: 0.1, // smoothing of arbor bounding box

    // function that returns true if the system is stable to indicate
    // that the layout can be stopped
    stableEnergy: function( energy ){
      var e = energy;
      return (e.max <= 0.5) || (e.mean <= 0.3);
    },

    // infinite layout options
    infinite: false // overrides all other options for a forces-all-the-time mode
  };

  function ArborLayout(options){
    this._private = {};

    this._private.options = $$.util.extend({}, defaults, options);
  }

  ArborLayout.prototype.run = function(){
    var layout = this;
    var options = this._private.options;

    $$.util.require('arbor', function(arbor){

      var cy = options.cy;
      var eles = options.eles;
      var nodes = eles.nodes().not(':parent');
      var edges = eles.edges();
      var bb = $$.util.makeBoundingBox( options.boundingBox ? options.boundingBox : {
        x1: 0, y1: 0, w: cy.width(), h: cy.height()
      } );
      var simUpdatingPos = false;

      layout.trigger({ type: 'layoutstart', layout: layout });

      // backward compatibility for old animation option
      if( options.liveUpdate !== undefined ){
        options.animate = options.liveUpdate;
      }

      // arbor doesn't work with just 1 node
      if( eles.nodes().size() <= 1 ){
        if( options.fit ){
          cy.reset();
        }

        eles.nodes().position({
          x: Math.round( (bb.x1 + bb.x2)/2 ),
          y: Math.round( (bb.y1 + bb.y2)/2 )
        });

        layout.one('layoutready', options.ready);
        layout.trigger({ type: 'layoutready', layout: layout });

        layout.one('layoutstop', options.stop);
        layout.trigger({ type: 'layoutstop', layout: layout });

        return;
      }

      var sys = layout._private.system = arbor.ParticleSystem();

      sys.parameters({
        repulsion: options.repulsion,
        stiffness: options.stiffness,
        friction: options.friction,
        gravity: options.gravity,
        fps: options.fps,
        dt: options.dt,
        precision: options.precision
      });

      if( options.animate && options.fit ){
        cy.fit( bb, options.padding );
      }

      var doneTime = 250;
      var doneTimeout;

      var ready = false;

      var lastDraw = +new Date();
      var sysRenderer = {
        init: function(system){
        },
        redraw: function(){
          var energy = sys.energy();

          // if we're stable (according to the client), we're done
          if( !options.infinite && options.stableEnergy != null && energy != null && energy.n > 0 && options.stableEnergy(energy) ){
            layout.stop();
            return;
          }

          if( !options.infinite && doneTime != Infinity ){
            clearTimeout(doneTimeout);
            doneTimeout = setTimeout(doneHandler, doneTime);
          }

          var movedNodes = cy.collection();

          sys.eachNode(function(n, point){
            var data = n.data;
            var node = data.element;

            if( node == null ){
              return;
            }

            if( !node.locked() && !node.grabbed() ){
              node.silentPosition({
                x: bb.x1 + point.x,
                y: bb.y1 + point.y
              });

              movedNodes.merge( node );
            }
          });

          if( options.animate && movedNodes.length > 0 ){
            simUpdatingPos = true;

            movedNodes.rtrigger('position');

            if( options.fit ){
              cy.fit( options.padding );
            }

            lastDraw = +new Date();
            simUpdatingPos = false;
          }

          if( !ready ){
            ready = true;
            layout.one('layoutready', options.ready);
            layout.trigger({ type: 'layoutready', layout: layout });
          }
        }

      };
      sys.renderer = sysRenderer;
      sys.screenSize( bb.w, bb.h );
      sys.screenPadding( options.padding, options.padding, options.padding, options.padding );
      sys.screenStep( options.stepSize );

      function calculateValueForElement(element, value){
        if( value == null ){
          return undefined;
        } else if( typeof value == typeof function(){} ){
          return value.apply(element, [element._private.data, {
            nodes: nodes.length,
            edges: edges.length,
            element: element
          }]);
        } else {
          return value;
        }
      }

      var grabHandler;
      nodes.on('grab free position', grabHandler = function(e){
        if( simUpdatingPos ){ return; }

        var pos = this.position();
        var apos = sys.fromScreen( pos );
        if( !apos ){ return; }

        var p = arbor.Point(apos.x, apos.y);
        var padding = options.padding;

        if(
          bb.x1 + padding <= pos.x && pos.x <= bb.x2 - padding &&
          bb.y1 + padding <= pos.y && pos.y <= bb.y2 - padding
        ){
          this.scratch().arbor.p = p;
        }

        switch( e.type ){
        case 'grab':
          this.scratch().arbor.fixed = true;
          break;
        case 'free':
          this.scratch().arbor.fixed = false;
          //this.scratch().arbor.tempMass = 1000;
          break;
        }
      });

      var lockHandler;
      nodes.on('lock unlock', lockHandler = function(e){
        node.scratch().arbor.fixed = node.locked();
      });

      var removeHandler;
      eles.on('remove', removeHandler = function(e){ return; // TODO enable when layout add/remove api added
        // var ele = this;
        // var arborEle = ele.scratch().arbor;

        // if( !arborEle ){ return; }

        // if( ele.isNode() ){
        //   sys.pruneNode( arborEle );
        // } else {
        //   sys.pruneEdge( arborEle );
        // }
      });

      var addHandler;
      cy.on('add', '*', addHandler = function(){ return; // TODO enable when layout add/remove api added
        // var ele = this;

        // if( ele.isNode() ){
        //   addNode( ele );
        // } else {
        //   addEdge( ele );
        // }
      });

      var resizeHandler;
      cy.on('resize', resizeHandler = function(){
        if( options.boundingBox == null && layout._private.system != null ){
          var w = cy.width();
          var h = cy.height();

          sys.screenSize( w, h );
        }
      });

      function addNode( node ){
        if( node.isFullAutoParent() ){ return; } // they don't exist in the sim

        var id = node._private.data.id;
        var mass = calculateValueForElement(node, options.nodeMass);
        var locked = node._private.locked;
        var nPos = node.position();

        var pos = sys.fromScreen({
          x: nPos.x,
          y: nPos.y
        });

        node.scratch().arbor = sys.addNode(id, {
          element: node,
          mass: mass,
          fixed: locked,
          x: locked ? pos.x : undefined,
          y: locked ? pos.y : undefined
        });
      }

      function addEdge( edge ){
        var src = edge.source().id();
        var tgt = edge.target().id();
        var length = calculateValueForElement(edge, options.edgeLength);

        edge.scratch().arbor = sys.addEdge(src, tgt, {
          length: length
        });
      }

      nodes.each(function(i, node){
        addNode( node );
      });

      edges.each(function(i, edge){
        addEdge( edge );
      });

      var grabbableNodes = nodes.filter(":grabbable");
      // disable grabbing if so set
      if( options.ungrabifyWhileSimulating ){
        grabbableNodes.ungrabify();
      }

      var doneHandler = layout._private.doneHandler = function(){
        layout._private.doneHandler = null;

        if( !options.animate ){
          if( options.fit ){
            cy.reset();
          }

          nodes.rtrigger('position');
        }

        // unbind handlers
        nodes.off('grab free position', grabHandler);
        nodes.off('lock unlock', lockHandler);
        eles.off('remove', removeHandler);
        cy.off('add', '*', addHandler);
        cy.off('resize', resizeHandler);

        // enable back grabbing if so set
        if( options.ungrabifyWhileSimulating ){
          grabbableNodes.grabify();
        }

        layout.one('layoutstop', options.stop);
        layout.trigger({ type: 'layoutstop', layout: layout });
      };

      sys.start();
      if( !options.infinite && options.maxSimulationTime != null && options.maxSimulationTime > 0 && options.maxSimulationTime !== Infinity ){
        setTimeout(function(){
          layout.stop();
        }, options.maxSimulationTime);
      }

    }); // require

    return this; // chaining
  };

  ArborLayout.prototype.stop = function(){
    if( this._private.system != null ){
      this._private.system.stop();
    }

    if( this._private.doneHandler ){
      this._private.doneHandler();
    }

    return this; // chaining
  };

  $$('layout', 'arbor', ArborLayout);

})(cytoscape);
<|endoftext|>


<commit_before>import {empty, addClass, hasClass} from './../helpers/dom/element';
import {equalsIgnoreCase} from './../helpers/string';
import {EventManager} from './../eventManager';
import {getRenderer, registerRenderer} from './../renderers';
import {isKey} from './../helpers/unicode';
import {partial} from './../helpers/function';
import {stopImmediatePropagation, isImmediatePropagationStopped} from './../helpers/dom/event';

const isListeningKeyDownEvent = new WeakMap();
const isCheckboxListenerAdded = new WeakMap();
const BAD_VALUE_CLASS = 'htBadValue';

/**
 * Checkbox renderer
 *
 * @private
 * @param {Object} instance Handsontable instance
 * @param {Element} TD Table cell where to render
 * @param {Number} row
 * @param {Number} col
 * @param {String|Number} prop Row object property name
 * @param value Value to render (remember to escape unsafe HTML before inserting to DOM!)
 * @param {Object} cellProperties Cell properties (shared by cell renderer and editor)
 */
function checkboxRenderer(instance, TD, row, col, prop, value, cellProperties) {
  getRenderer('base').apply(this, arguments);

  const eventManager = registerEvents(instance);
  let input = createInput();
  const labelOptions = cellProperties.label;
  let badValue = false;

  if (typeof cellProperties.checkedTemplate === 'undefined') {
    cellProperties.checkedTemplate = true;
  }
  if (typeof cellProperties.uncheckedTemplate === 'undefined') {
    cellProperties.uncheckedTemplate = false;
  }

  empty(TD); // TODO identify under what circumstances this line can be removed

  if (value === cellProperties.checkedTemplate || equalsIgnoreCase(value, cellProperties.checkedTemplate)) {
    input.checked = true;

  } else if (value === cellProperties.uncheckedTemplate || equalsIgnoreCase(value, cellProperties.uncheckedTemplate)) {
    input.checked = false;

  } else if (value === null) { // default value
    addClass(input, 'noValue');

  } else {
    input.style.display = 'none';
    addClass(input, BAD_VALUE_CLASS);
    badValue = true;
  }

  input.setAttribute('data-row', row);
  input.setAttribute('data-col', col);

  if (!badValue && labelOptions) {
    let labelText = '';

    if (labelOptions.value) {
      labelText = typeof labelOptions.value === 'function' ? labelOptions.value.call(this, row, col, prop, value) : labelOptions.value;

    } else if (labelOptions.property) {
      labelText = instance.getDataAtRowProp(row, labelOptions.property);
    }
    const label = createLabel(labelText);

    if (labelOptions.position === 'before') {
      label.appendChild(input);
    } else {
      label.insertBefore(input, label.firstChild);
    }
    input = label;
  }

  TD.appendChild(input);

  if (badValue) {
    TD.appendChild(document.createTextNode('#bad-value#'));
  }

  if (!isListeningKeyDownEvent.has(instance)) {
    isListeningKeyDownEvent.set(instance, true);
    instance.addHook('beforeKeyDown', onBeforeKeyDown);
  }

  /**
   * On before key down DOM listener.
   *
   * @private
   * @param {Event} event
   */
  function onBeforeKeyDown(event) {
    const toggleKeys = 'SPACE|ENTER';
    const switchOffKeys = 'DELETE|BACKSPACE';
    const isKeyCode = partial(isKey, event.keyCode);

    if (isKeyCode(`${toggleKeys}|${switchOffKeys}`) && !isImmediatePropagationStopped(event)) {
      eachSelectedCheckboxCell(function() {
        stopImmediatePropagation(event);
        event.preventDefault();
      });
    }
    if (isKeyCode(toggleKeys)) {
      changeSelectedCheckboxesState();
    }
    if (isKeyCode(switchOffKeys)) {
      changeSelectedCheckboxesState(true);
    }
  }

  /**
   * Change checkbox checked property
   *
   * @private
   * @param {Boolean} [uncheckCheckbox=false]
   */
  function changeSelectedCheckboxesState(uncheckCheckbox = false) {
    const selRange = instance.getSelectedRange();
    const changes = [];

    if (!selRange) {
      return;
    }

    const topLeft = selRange.getTopLeftCorner();
    const bottomRight = selRange.getBottomRightCorner();

    for (let row = topLeft.row; row <= bottomRight.row; row += 1) {
      for (let col = topLeft.col; col <= bottomRight.col; col += 1) {
        const cellProperties = instance.getCellMeta(row, col);

        if (cellProperties.type !== 'checkbox') {
          return;
        }

        if (cellProperties.readOnly === true) {
          continue;
        }

        if (typeof cellProperties.checkedTemplate === 'undefined') {
          cellProperties.checkedTemplate = true;
        }
        if (typeof cellProperties.uncheckedTemplate === 'undefined') {
          cellProperties.uncheckedTemplate = false;
        }

        const dataAtCell = instance.getDataAtCell(row, col);

        if (uncheckCheckbox === false) {
          if (dataAtCell === cellProperties.checkedTemplate) {
            changes.push([row, col, cellProperties.uncheckedTemplate]);

          } else if ([cellProperties.uncheckedTemplate, null, void 0].indexOf(dataAtCell) !== -1) {
            changes.push([row, col, cellProperties.checkedTemplate]);
          }

        } else {
          changes.push([row, col, cellProperties.uncheckedTemplate]);
        }
      }
    }

    if (changes.length > 0) {
      instance.setDataAtCell(changes);
    }
  }

  /**
   * Call callback for each found selected cell with checkbox type.
   *
   * @private
   * @param {Function} callback
   */
  function eachSelectedCheckboxCell(callback) {
    const selRange = instance.getSelectedRange();

    if (!selRange) {
      return;
    }
    const topLeft = selRange.getTopLeftCorner();
    const bottomRight = selRange.getBottomRightCorner();

    for (let row = topLeft.row; row <= bottomRight.row; row++) {
      for (let col = topLeft.col; col <= bottomRight.col; col++) {
        let cellProperties = instance.getCellMeta(row, col);

        if (cellProperties.type !== 'checkbox') {
          return;
        }

        let cell = instance.getCell(row, col);

        if (cell == null) {

          callback(row, col, cellProperties);

        } else {
          let checkboxes = cell.querySelectorAll('input[type=checkbox]');

          if (checkboxes.length > 0 && !cellProperties.readOnly) {
            callback(checkboxes);
          }
        }
      }
    }
  }
}

/**
 * Register checkbox listeners.
 *
 * @param {Handsontable} instance Handsontable instance.
 * @returns {EventManager}
 */
function registerEvents(instance) {
  let eventManager = isCheckboxListenerAdded.get(instance);

  if (!eventManager) {
    eventManager = new EventManager(instance);
    eventManager.addEventListener(instance.rootElement, 'click', (event) => onClick(event, instance));
    eventManager.addEventListener(instance.rootElement, 'mouseup', (event) => onMouseUp(event, instance));
    eventManager.addEventListener(instance.rootElement, 'change', (event) => onChange(event, instance));

    isCheckboxListenerAdded.set(instance, eventManager);
  }

  return eventManager;
}

/**
 * Create input element.
 *
 * @returns {Node}
 */
function createInput() {
  let input = document.createElement('input');

  input.className = 'htCheckboxRendererInput';
  input.type = 'checkbox';
  input.setAttribute('autocomplete', 'off');
  input.setAttribute('tabindex', '-1');

  return input.cloneNode(false);
}

/**
 * Create label element.
 *
 * @returns {Node}
 */
function createLabel(text) {
  let label = document.createElement('label');

  label.className = 'htCheckboxRendererLabel';
  label.appendChild(document.createTextNode(text));

  return label.cloneNode(true);
}

/**
 * `mouseup` callback.
 *
 * @private
 * @param {Event} event `mouseup` event.
 * @param {Object} instance Handsontable instance.
 */
function onMouseUp(event, instance) {
  if (!isCheckboxInput(event.target)) {
    return;
  }
  setTimeout(instance.listen, 10);
}

/**
 * `click` callback.
 *
 * @private
 * @param {Event} event `click` event.
 * @param {Object} instance Handsontable instance.
 */
function onClick(event, instance) {
  if (!isCheckboxInput(event.target)) {
    return false;
  }

  const row = parseInt(event.target.getAttribute('data-row'), 10);
  const col = parseInt(event.target.getAttribute('data-col'), 10);
  const cellProperties = instance.getCellMeta(row, col);

  if (cellProperties.readOnly) {
    event.preventDefault();
  }
}

/**
 * `change` callback.
 *
 * @param {Event} event `change` event.
 * @param {Object} instance Handsontable instance.
 * @param {Object} cellProperties Reference to cell properties.
 * @returns {Boolean}
 */
function onChange(event, instance) {
  if (!isCheckboxInput(event.target)) {
    return false;
  }

  const row = parseInt(event.target.getAttribute('data-row'), 10);
  const col = parseInt(event.target.getAttribute('data-col'), 10);
  const cellProperties = instance.getCellMeta(row, col);

  if (!cellProperties.readOnly) {
    let newCheckboxValue = null;

    if (event.target.checked) {
      if (cellProperties.checkedTemplate === void 0) {
        newCheckboxValue = true;
      } else {
        newCheckboxValue = cellProperties.checkedTemplate;
      }

    } else {
      if (cellProperties.uncheckedTemplate === void 0) {
        newCheckboxValue = false;
      } else {
        newCheckboxValue = cellProperties.uncheckedTemplate;
      }
    }

    instance.setDataAtCell(row, col, newCheckboxValue);
  }
}

/**
 * Check if the provided element is the checkbox input.
 *
 * @private
 * @param {HTMLElement} element The element in question.
 * @returns {Boolean}
 */
function isCheckboxInput(element) {
  return element.tagName === 'INPUT' && element.getAttribute('type') === 'checkbox';
}

export {checkboxRenderer};
registerRenderer('checkbox', checkboxRenderer);
<commit_msg>Small change #3881<commit_after>import {empty, addClass, hasClass} from './../helpers/dom/element';
import {equalsIgnoreCase} from './../helpers/string';
import {EventManager} from './../eventManager';
import {getRenderer, registerRenderer} from './../renderers';
import {isKey} from './../helpers/unicode';
import {partial} from './../helpers/function';
import {stopImmediatePropagation, isImmediatePropagationStopped} from './../helpers/dom/event';

const isListeningKeyDownEvent = new WeakMap();
const isCheckboxListenerAdded = new WeakMap();
const BAD_VALUE_CLASS = 'htBadValue';

/**
 * Checkbox renderer
 *
 * @private
 * @param {Object} instance Handsontable instance
 * @param {Element} TD Table cell where to render
 * @param {Number} row
 * @param {Number} col
 * @param {String|Number} prop Row object property name
 * @param value Value to render (remember to escape unsafe HTML before inserting to DOM!)
 * @param {Object} cellProperties Cell properties (shared by cell renderer and editor)
 */
function checkboxRenderer(instance, TD, row, col, prop, value, cellProperties) {
  getRenderer('base').apply(this, arguments);

  const eventManager = registerEvents(instance);
  let input = createInput();
  const labelOptions = cellProperties.label;
  let badValue = false;

  if (typeof cellProperties.checkedTemplate === 'undefined') {
    cellProperties.checkedTemplate = true;
  }
  if (typeof cellProperties.uncheckedTemplate === 'undefined') {
    cellProperties.uncheckedTemplate = false;
  }

  empty(TD); // TODO identify under what circumstances this line can be removed

  if (value === cellProperties.checkedTemplate || equalsIgnoreCase(value, cellProperties.checkedTemplate)) {
    input.checked = true;

  } else if (value === cellProperties.uncheckedTemplate || equalsIgnoreCase(value, cellProperties.uncheckedTemplate)) {
    input.checked = false;

  } else if (value === null) { // default value
    addClass(input, 'noValue');

  } else {
    input.style.display = 'none';
    addClass(input, BAD_VALUE_CLASS);
    badValue = true;
  }

  input.setAttribute('data-row', row);
  input.setAttribute('data-col', col);

  if (!badValue && labelOptions) {
    let labelText = '';

    if (labelOptions.value) {
      labelText = typeof labelOptions.value === 'function' ? labelOptions.value.call(this, row, col, prop, value) : labelOptions.value;

    } else if (labelOptions.property) {
      labelText = instance.getDataAtRowProp(row, labelOptions.property);
    }
    const label = createLabel(labelText);

    if (labelOptions.position === 'before') {
      label.appendChild(input);
    } else {
      label.insertBefore(input, label.firstChild);
    }
    input = label;
  }

  TD.appendChild(input);

  if (badValue) {
    TD.appendChild(document.createTextNode('#bad-value#'));
  }

  if (!isListeningKeyDownEvent.has(instance)) {
    isListeningKeyDownEvent.set(instance, true);
    instance.addHook('beforeKeyDown', onBeforeKeyDown);
  }

  /**
   * On before key down DOM listener.
   *
   * @private
   * @param {Event} event
   */
  function onBeforeKeyDown(event) {
    const toggleKeys = 'SPACE|ENTER';
    const switchOffKeys = 'DELETE|BACKSPACE';
    const isKeyCode = partial(isKey, event.keyCode);

    if (isKeyCode(`${toggleKeys}|${switchOffKeys}`) && !isImmediatePropagationStopped(event)) {
      eachSelectedCheckboxCell(function() {
        stopImmediatePropagation(event);
        event.preventDefault();
      });
    }
    if (isKeyCode(toggleKeys)) {
      changeSelectedCheckboxesState();
    }
    if (isKeyCode(switchOffKeys)) {
      changeSelectedCheckboxesState(true);
    }
  }

  /**
   * Change checkbox checked property
   *
   * @private
   * @param {Boolean} [uncheckCheckbox=false]
   */
  function changeSelectedCheckboxesState(uncheckCheckbox = false) {
    const selRange = instance.getSelectedRange();

    if (!selRange) {
      return;
    }

    const topLeft = selRange.getTopLeftCorner();
    const bottomRight = selRange.getBottomRightCorner();
    const changes = [];

    for (let row = topLeft.row; row <= bottomRight.row; row += 1) {
      for (let col = topLeft.col; col <= bottomRight.col; col += 1) {
        const cellProperties = instance.getCellMeta(row, col);

        if (cellProperties.type !== 'checkbox') {
          return;
        }

        if (cellProperties.readOnly === true) {
          continue;
        }

        if (typeof cellProperties.checkedTemplate === 'undefined') {
          cellProperties.checkedTemplate = true;
        }
        if (typeof cellProperties.uncheckedTemplate === 'undefined') {
          cellProperties.uncheckedTemplate = false;
        }

        const dataAtCell = instance.getDataAtCell(row, col);

        if (uncheckCheckbox === false) {
          if (dataAtCell === cellProperties.checkedTemplate) {
            changes.push([row, col, cellProperties.uncheckedTemplate]);

          } else if ([cellProperties.uncheckedTemplate, null, void 0].indexOf(dataAtCell) !== -1) {
            changes.push([row, col, cellProperties.checkedTemplate]);
          }

        } else {
          changes.push([row, col, cellProperties.uncheckedTemplate]);
        }
      }
    }

    if (changes.length > 0) {
      instance.setDataAtCell(changes);
    }
  }

  /**
   * Call callback for each found selected cell with checkbox type.
   *
   * @private
   * @param {Function} callback
   */
  function eachSelectedCheckboxCell(callback) {
    const selRange = instance.getSelectedRange();

    if (!selRange) {
      return;
    }
    const topLeft = selRange.getTopLeftCorner();
    const bottomRight = selRange.getBottomRightCorner();

    for (let row = topLeft.row; row <= bottomRight.row; row++) {
      for (let col = topLeft.col; col <= bottomRight.col; col++) {
        let cellProperties = instance.getCellMeta(row, col);

        if (cellProperties.type !== 'checkbox') {
          return;
        }

        let cell = instance.getCell(row, col);

        if (cell == null) {

          callback(row, col, cellProperties);

        } else {
          let checkboxes = cell.querySelectorAll('input[type=checkbox]');

          if (checkboxes.length > 0 && !cellProperties.readOnly) {
            callback(checkboxes);
          }
        }
      }
    }
  }
}

/**
 * Register checkbox listeners.
 *
 * @param {Handsontable} instance Handsontable instance.
 * @returns {EventManager}
 */
function registerEvents(instance) {
  let eventManager = isCheckboxListenerAdded.get(instance);

  if (!eventManager) {
    eventManager = new EventManager(instance);
    eventManager.addEventListener(instance.rootElement, 'click', (event) => onClick(event, instance));
    eventManager.addEventListener(instance.rootElement, 'mouseup', (event) => onMouseUp(event, instance));
    eventManager.addEventListener(instance.rootElement, 'change', (event) => onChange(event, instance));

    isCheckboxListenerAdded.set(instance, eventManager);
  }

  return eventManager;
}

/**
 * Create input element.
 *
 * @returns {Node}
 */
function createInput() {
  let input = document.createElement('input');

  input.className = 'htCheckboxRendererInput';
  input.type = 'checkbox';
  input.setAttribute('autocomplete', 'off');
  input.setAttribute('tabindex', '-1');

  return input.cloneNode(false);
}

/**
 * Create label element.
 *
 * @returns {Node}
 */
function createLabel(text) {
  let label = document.createElement('label');

  label.className = 'htCheckboxRendererLabel';
  label.appendChild(document.createTextNode(text));

  return label.cloneNode(true);
}

/**
 * `mouseup` callback.
 *
 * @private
 * @param {Event} event `mouseup` event.
 * @param {Object} instance Handsontable instance.
 */
function onMouseUp(event, instance) {
  if (!isCheckboxInput(event.target)) {
    return;
  }
  setTimeout(instance.listen, 10);
}

/**
 * `click` callback.
 *
 * @private
 * @param {Event} event `click` event.
 * @param {Object} instance Handsontable instance.
 */
function onClick(event, instance) {
  if (!isCheckboxInput(event.target)) {
    return false;
  }

  const row = parseInt(event.target.getAttribute('data-row'), 10);
  const col = parseInt(event.target.getAttribute('data-col'), 10);
  const cellProperties = instance.getCellMeta(row, col);

  if (cellProperties.readOnly) {
    event.preventDefault();
  }
}

/**
 * `change` callback.
 *
 * @param {Event} event `change` event.
 * @param {Object} instance Handsontable instance.
 * @param {Object} cellProperties Reference to cell properties.
 * @returns {Boolean}
 */
function onChange(event, instance) {
  if (!isCheckboxInput(event.target)) {
    return false;
  }

  const row = parseInt(event.target.getAttribute('data-row'), 10);
  const col = parseInt(event.target.getAttribute('data-col'), 10);
  const cellProperties = instance.getCellMeta(row, col);

  if (!cellProperties.readOnly) {
    let newCheckboxValue = null;

    if (event.target.checked) {
      if (cellProperties.checkedTemplate === void 0) {
        newCheckboxValue = true;
      } else {
        newCheckboxValue = cellProperties.checkedTemplate;
      }

    } else {
      if (cellProperties.uncheckedTemplate === void 0) {
        newCheckboxValue = false;
      } else {
        newCheckboxValue = cellProperties.uncheckedTemplate;
      }
    }

    instance.setDataAtCell(row, col, newCheckboxValue);
  }
}

/**
 * Check if the provided element is the checkbox input.
 *
 * @private
 * @param {HTMLElement} element The element in question.
 * @returns {Boolean}
 */
function isCheckboxInput(element) {
  return element.tagName === 'INPUT' && element.getAttribute('type') === 'checkbox';
}

export {checkboxRenderer};
registerRenderer('checkbox', checkboxRenderer);
<|endoftext|>