<commit_before>var data = [],
    solo = [],
    MAX_BARS = 10,
    lang = document.documentElement.lang,
    dataset = $("#bar").attr("dataset"),
    options = $("#bar").attr("options").split(","),
    x = $("#bar").attr("x").split(","),
    currentX = x[0],
    y = $("#bar").attr("y").split(","),
    currentY = y[0],
    filters = $("#bar").attr("filters"),
    url = "http://api.staging.dataviva.info/" +
        dataset + "/year/" + ( options.indexOf('month') != -1 ? 'month/' : '' ) + y.join("/") + ( filters ? "?" + filters : '');

// TODO: Title creator
var title = 'Title';

var visualization;

var uis = [];

if(x.length > 1){
    uis.push({
        'value': x,
        'type': 'drop',
        'label': 'xaxis',
        'method': function(value, viz){
            currentX = value;
            viz.x(value)
               .order({
                    'value': currentX,
                    'sort': 'asc'
                })
               totalOfCurrentX()
            viz.draw()
        }
    });
}

if(y.length > 1){
    uis.push({
        'value': y,
        'type': 'drop',
        'label': 'yaxis',
        'method': function(value, viz){
            currentY = value;
            viz.y(value).id(value).draw();
        }
    });
}

var textHelper = {
    'loading': {
        'en': 'loading ...',
        'pt': 'carregando ...'
    },
    'average_wage': {
        'en': 'Salário Médio Mensal',
        'pt': 'Average Monthly Wage'
    },
    'jobs': {
        'en': 'Jobs',
        'pt': 'Empregos'
    },
    'year': {
        'en': 'Year',
        'pt': 'Ano'
    },
    'scale': {
        'en': "Scale",
        'pt': "Escala"
    },
    'yaxis': {
        'en': "Y-Axis",
        'pt': "Eixo Y"
    },
    'xaxis': {
        'en': "X-Axis",
        'pt': "Eixo X"
    },
    'locale': {
        'en': 'en_US',
        'pt': 'pt_BR'
    },
    'average_wage': {
        'en': "Average Wage",
        'pt': "Salário Médio"
    },
    'kg': {
        'en': 'kg',
        'pt': 'kg'
    },
    'value': {
        'en': "US$",
        'pt': "US$"
    },
    'kg_label': {
        'en': 'Amount [kg]',
        'pt': 'Quantidade [kg]'
    },
    'value_label': {
        'en': "Value [$ USD]",
        'pt': "Valor [$ USD]"
    },
    'average_wage_label': {
        'en': "Average Monthly Wage [$ USD]",
        'pt': "Salário Médio Mensal [$ USD]"
    },
    'jobs_label': {
        'en': "Jobs",
        'pt': "Empregos"
    },
    'kg_pct': {
        'en': "% of kg",
        'pt': "% de kg"
    },
    'value_pct': {
        'en': "% of US$",
        'pt': "% de US$"
    },
    'simple': {
        'en': "Simples",
        'pt': "Simples"
    },
    'establishment_size': {
        'en': "Establishment Size",
        'pt': "Tamanho do Estabelecimento"
    },
    'average_establishment_size': {
        'en': "Average Establishment Size",
        'pt': "Tamanho Médio do Estabelecimento"
    },
    'establishment_count': {
        'en': "Establishments",
        'pt': "Estabelecimentos"
    },
    'wage': {
        'en': "Salary Mass",
        'pt': "Massa Salarial"
    },
    'gender': {
        'en': "Gender",
        'pt': "Gênero"
    },
    'ethnicity': {
        'en': "Ethnicity",
        'pt': "Etnia"
    },
    'literacy': {
        'en': "Literacy",
        'pt': "Escolaridade"
    },
    'month': {
        'en': "Month",
        'pt': "Mês"
    },
    'port': {
        'en': "Port",
        'pt': "Porto"
    },
    'legal_nature': {
        'en': "Legal Nature",
        'pt': "Natureza Jurídica"
    },
    'size_establishment': {
        'en': "Establishment Size ",
        'pt': "Tamanho do Estabelecimento"
    },
    'time_resolution': {
        'en': "Time Resolution",
        'pt': "Resolução Temporal"
    },
    'total_of': {
        'en': "Total in selected years: ",
        'pt': "Total nos anos selecionados: ",
    },
    'data_provided_by': {
        'en': "Data provided by ",
        'pt': "Dados fornecidos por ",
    }
};

var formatNumber = function(digit){
    var lastDigit = digit.slice(-1);

    if(!isNaN(lastDigit))
        return digit;

    var number =  digit.slice(0, -1);

    var scale = {
        'T': {
            'en': number < 2 ? ' Trillion' : ' Trillions',
            'pt': number < 2 ? ' Trilhão' : ' Trilhões'
        },
        'B': {
            'en': number < 2 ? ' Billion' : ' Billions',
            'pt': number < 2 ? ' Bilhão' : ' Bilhões'
        },
        'M': {
            'en': number < 2 ? ' Million' : ' Millions',
            'pt': number < 2 ? ' Milhão' : ' Milhões'
        },
        'k': {
            'en': ' Thousand',
            'pt': ' Mil'
        }
    }

    return number + scale[lastDigit][lang];
}

var formatHelper = {
    "text": function(text, params) {
        if(params.labels == false)
            return text;

        if (textHelper[text] != undefined)
            return textHelper[text][lang];

        return d3plus.string.title(text, params);
    },

    "number": function(number, params) {
        var formatted = d3plus.number.format(number, params);

        formatted = formatNumber(formatted)

        if (params.key == "value" && params.labels == undefined)
            return "$" + formatted + " USD";

        if (params.key == "kg" && params.labels == undefined)
            return formatted + " kg";

        if (params.key == "wage" && params.labels == undefined)
            return "$" + formatted + " BRL";

        if (params.key == "average_wage" && params.labels == undefined)
            return "$" + formatted + " BRL";

        if (params.key == "kg_pct" && params.labels == undefined)
            return parseFloat(formatted).toFixed(1) + "%";

        if (params.key == "value_pct" && params.labels == undefined)
            return parseFloat(formatted).toFixed(1) + "%";

        return formatted;
    }
};

var loadViz = function(data){
     visualization = d3plus.viz()
        .container("#bar")
        .data(data)
        .background("transparent")
        .type("bar")
        .font({
            'size': 13
        })
        .id({
            'value': currentY,
            'solo': solo
        })
        .y({
            "value": currentY,
            "scale": "discrete",
            'label': {
                'font': {
                    'size': 22
                }
            }
        })
        .x({
            "value": currentX,
            'label': {
                'font': {
                    'size': 16
                }
            }
        })
        .ui(uis)
        .format(formatHelper)
        .time({
            'value': 'year',
            'solo': {
                'value': [lastYear(data)],
                'callback': totalOfCurrentX
            }
        })
        .aggs({
            'average_wage': 'mean'
        })
        .order({
            'value': currentX,
            'sort': 'asc'
        })
        .footer({
            "value": textHelper["data_provided_by"][lang] + dataset.toUpperCase()
        })

        if(options.indexOf('singlecolor') != -1){
            visualization.color({
                "value" : function(d){
                    return "#4d90fe";
                }
            }).legend(false)
        }
        totalOfCurrentX();
        visualization.draw()
};

var getSelectedYears = function() {
    var years = $('#timeline #labels [fill="rgba(68,68,68,1)"]').map(function (index, item){
        return +item.innerHTML
    })

    years = Array.from(years);
    years = years.filter(function(item){
        return item > 0;
    });
    return years;
}

var totalOfCurrentX = function(){
    var years = getSelectedYears();
    years = years.length == 0 ? [lastYear(data)] : years;

    var key = currentX.endsWith('_pct') ? currentX.slice(0, currentX.indexOf('_pct')) : currentX;
    var total = data.reduce(function(acc, item){
        if(years.indexOf(item.year) != -1){
            acc += item[key];
        }
        return acc;
    }, 0);

    visualization.title({
        'sub': {
            'value': textHelper["total_of"][lang] + formatHelper.number(total, {key: key}),
            'font': {
                'align': 'left'
            }
        }
    })
};

var buildData = function(responseApi){

    var getAttrByName = function(item, attr){
        var index = headers.indexOf(attr);
        return item[index];
    }

    var data = [];
    var headers = responseApi.headers;

    responseApi.data.forEach(function(item){
        var dataItem = {};

        headers.forEach(function(header){
            dataItem[header] = getAttrByName(item, header);
        });

        data.push(dataItem);
    });

    return data;
}

var addPercentage = function(data){
    if(options.indexOf('percentage') == -1)
        return data;

    var groupBy = options.indexOf('month') != -1 ? 'date' : 'year';

    var total = {};

    data.forEach(function(item){
        x.forEach(function(xValue){
            if(total[xValue] == undefined)
                total[xValue] = {};

            if(total[xValue][item.year] == undefined)
                total[xValue][item.year] = 0;

            total[xValue][item.year] += item[xValue];
        });
    });

    data = data.map(function(item){
        x.forEach(function(xValue){
            item[xValue + '_pct'] = 100 * (item[xValue] / total[xValue][item.year]);
        });

        return item;
    });

    x.forEach(function(xValue){
        x.push(xValue + '_pct')
    });

    return data;
}

var addNameToData = function(data){
    y.forEach(function(itemY){
        data = data.map(function(item){
            if(metadatas[itemY][item[itemY]] == undefined){
                item[itemY] = 'NOT FOUND!';
            }
            else{
                item[itemY] = metadatas[itemY][item[itemY]]['name_' + lang];
            }

            return item;
        });
    });

    data = data.map(function(item){
        if(item['wage'] != undefined)
            item['wage'] = +item['wage'];

        if(item['average_wage'] != undefined)
            item['average_wage'] = +item['average_wage'];

        if(item['month'] != undefined)
            item['date'] = item['year'] + '/' + item['month'];

        return item;
    });

    if(options.indexOf('month') != -1){
        uis.push({
            'method': 'time',
            'label': 'time_resolution',
            'value': [
                {'month': 'date'},
                {'year': 'year'}
            ]
        });
    }

    return data;
};

var groupDataByCurrentY = function(data){
    var sumByItem = {};

    data.forEach(function(item){
        if(sumByItem[item[currentY]] == undefined)
            sumByItem[item[currentY]] = {
                "sum": 0,
                "name": item[currentY]
            };

        sumByItem[item[currentY]].sum += item[currentX];
    });

    var list = [];

    for(var item in sumByItem){
        list.push({
            name: sumByItem[item].name,
            sum: sumByItem[item].sum
        });
    }

    return list;
}

var getTopCurrentYNames = function(groupedData){
    var compare = function(a, b){
        if(a.sum < b.sum)
            return 1;
        if(a.sum > b.sum)
            return -1;

        return 0;
    }

    var list = groupedData.sort(compare).slice(0, MAX_BARS);

    var selected = list.map(function(item){
        return item.name;
    });

    return selected;
}

var updateSolo = function(data){
    var copiedData = (JSON.parse(JSON.stringify(data)));
    var groupedData = groupDataByCurrentY(copiedData);
    solo = getTopCurrentYNames(groupedData);

    return solo;
};

var lastYear = function(data){
    var year = 0;

    data.forEach(function(item){
        if(item.year > year)
            year = item.year;
    });

    return year;
};

var loading = dataviva.ui.loading('.loading').text(textHelper.loading[lang]);

$(document).ready(function(){
    var urls = [url];

    y.forEach(function(item){
        urls.push("http://api.staging.dataviva.info/metadata/" + item);
    });

    ajaxQueue(
        urls,
        function(responses){
            api = responses.shift();
            metadatas = {};

            y.forEach(function(item, index){
                metadatas[item] = responses[index];
            });

            data = buildData(api);
            data = addNameToData(data);
            data = addPercentage(data);
            solo = updateSolo(data);

            loading.hide();
            loadViz(data);
        }
    );
});<commit_msg>Add custon order<commit_after>var data = [],
    solo = [],
    MAX_BARS = 10,
    lang = document.documentElement.lang,
    dataset = $("#bar").attr("dataset"),
    options = $("#bar").attr("options").split(","),
    x = $("#bar").attr("x").split(","),
    currentX = x[0],
    y = $("#bar").attr("y").split(","),
    currentY = y[0],
    filters = $("#bar").attr("filters"),
    url = "http://api.staging.dataviva.info/" +
        dataset + "/year/" + ( options.indexOf('month') != -1 ? 'month/' : '' ) + y.join("/") + ( filters ? "?" + filters : '');

// TODO: Title creator
var title = 'Title';

var visualization;

var uis = [];

if(x.length > 1){
    uis.push({
        'value': x,
        'type': 'drop',
        'label': 'xaxis',
        'method': function(value, viz){
            currentX = value;
            viz.x(value)
               .order({
                    'value': data[0][currentY + '_order'] == undefined ? currentX : currentY + '_order',
                    'sort': data[0][currentY + '_order'] == undefined ? 'asc' : 'desc'
                })
               totalOfCurrentX()
            viz.draw()
        }
    });
}

if(y.length > 1){
    uis.push({
        'value': y,
        'type': 'drop',
        'label': 'yaxis',
        'method': function(value, viz){
            currentY = value;
            viz.y(value)
                .id(value)
                .order({
                    'value': data[0][currentY + '_order'] == undefined ? currentX : currentY + '_order',
                    'sort': data[0][currentY + '_order'] == undefined ? 'asc' : 'desc'
                })
                .draw();
        }
    });
}

var orderHelper = {
    'ethnicity': {
        '-1': 6,
        '1': 5,
        '2': 1,
        '4': 3,
        '6': 4,
        '8': 2
    },
    'gender': {
        '0': 2,
        '1': 1
    },
    'literacy': {
        '-1': 8,
        '1': 7,
        '2': 6,
        '3': 5,
        '4': 4,
        '5': 3,
        '6': 2,
        '7': 1
    },
    'simple': {
        '0': 2,
        '1': 1
    },
    'establishment_size': {
        '-1': 5,
        '0': 6,
        '1': 4,
        '2': 3,
        '3': 2,
        '4': 1
    },
    'legal_nature': {
        '-1': 8,
        '1': 1,
        '2': 2,
        '3': 3,
        '4': 4,
        '5': 5,
        '6': 6,
        '7': 7
    }
}

var addOrder = function(data){
    data = data.map(function(item){
        for(key in orderHelper){
            if(item[key] != undefined)
                item[key + '_order'] = orderHelper[key][item[key]];
        }

        return item;
    });

    return data;
};

var textHelper = {
    'loading': {
        'en': 'loading ...',
        'pt': 'carregando ...'
    },
    'average_wage': {
        'en': 'Salário Médio Mensal',
        'pt': 'Average Monthly Wage'
    },
    'jobs': {
        'en': 'Jobs',
        'pt': 'Empregos'
    },
    'year': {
        'en': 'Year',
        'pt': 'Ano'
    },
    'scale': {
        'en': "Scale",
        'pt': "Escala"
    },
    'yaxis': {
        'en': "Y-Axis",
        'pt': "Eixo Y"
    },
    'xaxis': {
        'en': "X-Axis",
        'pt': "Eixo X"
    },
    'locale': {
        'en': 'en_US',
        'pt': 'pt_BR'
    },
    'average_wage': {
        'en': "Average Wage",
        'pt': "Salário Médio"
    },
    'kg': {
        'en': 'kg',
        'pt': 'kg'
    },
    'value': {
        'en': "US$",
        'pt': "US$"
    },
    'kg_label': {
        'en': 'Amount [kg]',
        'pt': 'Quantidade [kg]'
    },
    'value_label': {
        'en': "Value [$ USD]",
        'pt': "Valor [$ USD]"
    },
    'average_wage_label': {
        'en': "Average Monthly Wage [$ USD]",
        'pt': "Salário Médio Mensal [$ USD]"
    },
    'jobs_label': {
        'en': "Jobs",
        'pt': "Empregos"
    },
    'kg_pct': {
        'en': "% of kg",
        'pt': "% de kg"
    },
    'value_pct': {
        'en': "% of US$",
        'pt': "% de US$"
    },
    'simple': {
        'en': "Simples",
        'pt': "Simples"
    },
    'establishment_size': {
        'en': "Establishment Size",
        'pt': "Tamanho do Estabelecimento"
    },
    'average_establishment_size': {
        'en': "Average Establishment Size",
        'pt': "Tamanho Médio do Estabelecimento"
    },
    'establishment_count': {
        'en': "Establishments",
        'pt': "Estabelecimentos"
    },
    'wage': {
        'en': "Salary Mass",
        'pt': "Massa Salarial"
    },
    'gender': {
        'en': "Gender",
        'pt': "Gênero"
    },
    'ethnicity': {
        'en': "Ethnicity",
        'pt': "Etnia"
    },
    'literacy': {
        'en': "Literacy",
        'pt': "Escolaridade"
    },
    'month': {
        'en': "Month",
        'pt': "Mês"
    },
    'port': {
        'en': "Port",
        'pt': "Porto"
    },
    'legal_nature': {
        'en': "Legal Nature",
        'pt': "Natureza Jurídica"
    },
    'size_establishment': {
        'en': "Establishment Size ",
        'pt': "Tamanho do Estabelecimento"
    },
    'time_resolution': {
        'en': "Time Resolution",
        'pt': "Resolução Temporal"
    },
    'total_of': {
        'en': "Total in selected years: ",
        'pt': "Total nos anos selecionados: ",
    },
    'data_provided_by': {
        'en': "Data provided by ",
        'pt': "Dados fornecidos por ",
    }
};

var formatNumber = function(digit){
    var lastDigit = digit.slice(-1);

    if(!isNaN(lastDigit))
        return digit;

    var number =  digit.slice(0, -1);

    var scale = {
        'T': {
            'en': number < 2 ? ' Trillion' : ' Trillions',
            'pt': number < 2 ? ' Trilhão' : ' Trilhões'
        },
        'B': {
            'en': number < 2 ? ' Billion' : ' Billions',
            'pt': number < 2 ? ' Bilhão' : ' Bilhões'
        },
        'M': {
            'en': number < 2 ? ' Million' : ' Millions',
            'pt': number < 2 ? ' Milhão' : ' Milhões'
        },
        'k': {
            'en': ' Thousand',
            'pt': ' Mil'
        }
    }

    return number + scale[lastDigit][lang];
}

var formatHelper = {
    "text": function(text, params) {
        if(params.labels == false)
            return text;

        if (textHelper[text] != undefined)
            return textHelper[text][lang];

        return d3plus.string.title(text, params);
    },

    "number": function(number, params) {
        var formatted = d3plus.number.format(number, params);

        formatted = formatNumber(formatted)

        if (params.key == "value" && params.labels == undefined)
            return "$" + formatted + " USD";

        if (params.key == "kg" && params.labels == undefined)
            return formatted + " kg";

        if (params.key == "wage" && params.labels == undefined)
            return "$" + formatted + " BRL";

        if (params.key == "average_wage" && params.labels == undefined)
            return "$" + formatted + " BRL";

        if (params.key == "kg_pct" && params.labels == undefined)
            return parseFloat(formatted).toFixed(1) + "%";

        if (params.key == "value_pct" && params.labels == undefined)
            return parseFloat(formatted).toFixed(1) + "%";

        return formatted;
    }
};

var loadViz = function(data){
     visualization = d3plus.viz()
        .container("#bar")
        .data(data)
        .background("transparent")
        .type("bar")
        .font({
            'size': 13
        })
        .id({
            'value': currentY,
            'solo': solo
        })
        .y({
            "value": currentY,
            "scale": "discrete",
            'label': {
                'font': {
                    'size': 22
                }
            }
        })
        .x({
            "value": currentX,
            'label': {
                'font': {
                    'size': 16
                }
            }
        })
        .ui(uis)
        .format(formatHelper)
        .time({
            'value': 'year',
            'solo': {
                'value': [lastYear(data)],
                'callback': totalOfCurrentX
            }
        })
        .aggs({
            'average_wage': 'mean',
            'legal_nature_order': 'mean',
            'ethnicity': 'mean',
            'gender': 'mean',
            'literacy': 'mean',
            'simple': 'mean',
            'establishment_size': 'mean'

        })
        .order({
            'value': data[0][currentY + '_order'] == undefined ? currentX : currentY + '_order',
            'sort': data[0][currentY + '_order'] == undefined ? 'asc' : 'desc'
        })
        .footer({
            "value": textHelper["data_provided_by"][lang] + dataset.toUpperCase()
        })

        if(options.indexOf('singlecolor') != -1){
            visualization.color({
                "value" : function(d){
                    return "#4d90fe";
                }
            }).legend(false)
        }
        totalOfCurrentX();
        visualization.draw()
};

var getSelectedYears = function() {
    var years = $('#timeline #labels [fill="rgba(68,68,68,1)"]').map(function (index, item){
        return +item.innerHTML
    })

    years = Array.from(years);
    years = years.filter(function(item){
        return item > 0;
    });
    return years;
}

var totalOfCurrentX = function(){
    var years = getSelectedYears();
    years = years.length == 0 ? [lastYear(data)] : years;

    var key = currentX.endsWith('_pct') ? currentX.slice(0, currentX.indexOf('_pct')) : currentX;
    var total = data.reduce(function(acc, item){
        if(years.indexOf(item.year) != -1){
            acc += item[key];
        }
        return acc;
    }, 0);

    visualization.title({
        'sub': {
            'value': textHelper["total_of"][lang] + formatHelper.number(total, {key: key}),
            'font': {
                'align': 'left'
            }
        }
    })
};

var buildData = function(responseApi){

    var getAttrByName = function(item, attr){
        var index = headers.indexOf(attr);
        return item[index];
    }

    var data = [];
    var headers = responseApi.headers;

    responseApi.data.forEach(function(item){
        var dataItem = {};

        headers.forEach(function(header){
            dataItem[header] = getAttrByName(item, header);
        });

        data.push(dataItem);
    });

    return data;
}

var addPercentage = function(data){
    if(options.indexOf('percentage') == -1)
        return data;

    var groupBy = options.indexOf('month') != -1 ? 'date' : 'year';

    var total = {};

    data.forEach(function(item){
        x.forEach(function(xValue){
            if(total[xValue] == undefined)
                total[xValue] = {};

            if(total[xValue][item.year] == undefined)
                total[xValue][item.year] = 0;

            total[xValue][item.year] += item[xValue];
        });
    });

    data = data.map(function(item){
        x.forEach(function(xValue){
            item[xValue + '_pct'] = 100 * (item[xValue] / total[xValue][item.year]);
        });

        return item;
    });

    x.forEach(function(xValue){
        x.push(xValue + '_pct')
    });

    return data;
}

var addNameToData = function(data){
    y.forEach(function(itemY){
        data = data.map(function(item){
            if(metadatas[itemY][item[itemY]] == undefined){
                item[itemY] = 'NOT FOUND!';
            }
            else{
                item[itemY] = metadatas[itemY][item[itemY]]['name_' + lang];
            }

            return item;
        });
    });

    data = data.map(function(item){
        if(item['wage'] != undefined)
            item['wage'] = +item['wage'];

        if(item['average_wage'] != undefined)
            item['average_wage'] = +item['average_wage'];

        if(item['month'] != undefined)
            item['date'] = item['year'] + '/' + item['month'];

        return item;
    });

    if(options.indexOf('month') != -1){
        uis.push({
            'method': 'time',
            'label': 'time_resolution',
            'value': [
                {'month': 'date'},
                {'year': 'year'}
            ]
        });
    }

    return data;
};

var groupDataByCurrentY = function(data){
    var sumByItem = {};

    data.forEach(function(item){
        if(sumByItem[item[currentY]] == undefined)
            sumByItem[item[currentY]] = {
                "sum": 0,
                "name": item[currentY]
            };

        sumByItem[item[currentY]].sum += item[currentX];
    });

    var list = [];

    for(var item in sumByItem){
        list.push({
            name: sumByItem[item].name,
            sum: sumByItem[item].sum
        });
    }

    return list;
}

var getTopCurrentYNames = function(groupedData){
    var compare = function(a, b){
        if(a.sum < b.sum)
            return 1;
        if(a.sum > b.sum)
            return -1;

        return 0;
    }

    var list = groupedData.sort(compare).slice(0, MAX_BARS);

    var selected = list.map(function(item){
        return item.name;
    });

    return selected;
}

var updateSolo = function(data){
    var copiedData = (JSON.parse(JSON.stringify(data)));
    var groupedData = groupDataByCurrentY(copiedData);
    solo = getTopCurrentYNames(groupedData);

    return solo;
};

var lastYear = function(data){
    var year = 0;

    data.forEach(function(item){
        if(item.year > year)
            year = item.year;
    });

    return year;
};

var loading = dataviva.ui.loading('.loading').text(textHelper.loading[lang]);

$(document).ready(function(){
    var urls = [url];

    y.forEach(function(item){
        urls.push("http://api.staging.dataviva.info/metadata/" + item);
    });

    ajaxQueue(
        urls,
        function(responses){
            api = responses.shift();
            metadatas = {};

            y.forEach(function(item, index){
                metadatas[item] = responses[index];
            });

            data = buildData(api);
            data = addOrder(data);
            data = addNameToData(data);
            data = addPercentage(data);
            solo = updateSolo(data);

            loading.hide();
            loadViz(data);
        }
    );
});<|endoftext|>


<commit_before>import Error from 'es6-error'
import { request, response } from 'express'

const Request = () => ({
  __proto__: request,
  app: {},
  body: {},
  query: {},
  route: {},
  params: {},
  headers: {},
  cookies: {},
  signedCookies: {}
})

const Response = () => ({
  __proto__: response,
  app: {},
  locals: {}
})

const chainables = ['status', 'vary']

export async function run(setup, middleware, done) {

  setup = setup || ((req, res, next) => next())

  const req = Request()
  const res = Response()

  let err = null
  let isDone = false

  const finish = (_err = null) => {
    err = _err
    isDone = true
    isFunction(done) && done(err, req, res)
  }

  for (let property in res) {
    if (isFunction(res[property])) {
      res[property] = () => chainables.includes(property) ? res : finish()
    }
  }

  let promise

  setup(req, res, (_err = null) => {
    err = _err
    promise = middleware.length <= 3
      ? middleware(req, res, finish)
      : middleware(err, req, res, finish)
  })

  if (!isPromise(promise)) return

  try {
    await promise
    if (isDone || !isFunction(done)) return [err, req, res]
    try {
      done(err, req, res)
    }
    catch (err) {
      throw new ExpressUnitError(null, err)
    }
  }
  catch (err) {
    if (err instanceof ExpressUnitError) throw err.err
    throw new ExpressUnitError('Unhandled rejection in middleware', err)
  }
}

export class ExpressUnitError extends Error {
  constructor(message, err) {
    super(message)
    this.err = err
  }
  toString() {
    const { name, message, err } = this
    return `${name}: ${message}\n${JSON.stringify(err, null, 2)}`
  }
}

function isFunction(value) {
  return typeof value === 'function'
}

function isPromise(value) {
  return value && typeof value === 'object' && isFunction(value.then)
}
<commit_msg>Improve variable names in src/. #45<commit_after>import Error from 'es6-error'
import { request, response } from 'express'

const Request = () => ({
  __proto__: request,
  app: {},
  body: {},
  query: {},
  route: {},
  params: {},
  headers: {},
  cookies: {},
  signedCookies: {}
})

const Response = () => ({
  __proto__: response,
  app: {},
  locals: {}
})

const chainables = ['status', 'vary']

export async function run(setup, middleware, callback) {

  setup = setup || ((req, res, next) => next())

  const req = Request()
  const res = Response()

  let err = null
  let nextCalled = false

  const finish = (_err = null) => {
    err = _err
    nextCalled = true
    isFunction(callback) && callback(err, req, res)
  }

  for (let property in res) {
    if (isFunction(res[property])) {
      res[property] = () => chainables.includes(property) ? res : finish()
    }
  }

  let promise

  setup(req, res, (_err = null) => {
    err = _err
    promise = middleware.length <= 3
      ? middleware(req, res, finish)
      : middleware(err, req, res, finish)
  })

  if (!isPromise(promise)) return

  try {
    await promise
    if (nextCalled || !isFunction(callback)) return [err, req, res]
    try {
      callback(err, req, res)
    }
    catch (err) {
      throw new ExpressUnitError(null, err)
    }
  }
  catch (err) {
    if (err instanceof ExpressUnitError) throw err.err
    throw new ExpressUnitError('Unhandled rejection in middleware', err)
  }
}

export class ExpressUnitError extends Error {
  constructor(message, err) {
    super(message)
    this.err = err
  }
  toString() {
    const { name, message, err } = this
    return `${name}: ${message}\n${JSON.stringify(err, null, 2)}`
  }
}

function isFunction(value) {
  return typeof value === 'function'
}

function isPromise(value) {
  return value && typeof value === 'object' && isFunction(value.then)
}
<|endoftext|>


<commit_before>/**
 * React Native app demonstrating react-native-icons
 * https://github.com/corymsmith/react-native-icons
 */
'use strict';

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ScrollView,
  TouchableHighlight
  //Animated
  } = React;

var BrandColors = {
  Facebook: '#3b5998',
  Twitter: '#55acee'
};

var { Icon, TabBarIOS, Spinner} = require('react-native-icons');
var TabBarItemIOS = TabBarIOS.Item;

var Example = React.createClass({
  getInitialState: function () {
    return {
      selectedTab: 'home',
      notifCount: 0,
      presses: 0,
      //rotation: Animated.Value(0)
    };
  },
  render: function () {
    return (
      <TabBarIOS
        selectedTab={this.state.selectedTab}
        tintColor={'#c1d82f'}
        barTintColor={'#000000'}
        styles={styles.tabBar}>
        <TabBarItemIOS
          name="home"
          iconName={'ion|ios-home-outline'}
          title={''}
          badgeValue={'3'}
          iconSize={32}
          accessibilityLabel="Home Tab"
          selected={this.state.selectedTab === 'home'}
          onPress={() => {
            this.setState({
              selectedTab: 'home',
            });
          }}>
          {this._renderContent()}
        </TabBarItemIOS>
        <TabBarItemIOS
          name="articles"
          iconName={'ion|ios-paper-outline'}
          title={''}
          iconSize={32}
          accessibilityLabel="Articles Tab"
          selected={this.state.selectedTab === 'articles'}
          onPress={() => {
            this.setState({
              selectedTab: 'articles',
            });
          }}>
          {this._renderContent()}
        </TabBarItemIOS>
        <TabBarItemIOS
          name="messages"
          iconName={'ion|chatboxes'}
          title={''}
          iconSize={32}
          accessibilityLabel="Messages Tab"
          selected={this.state.selectedTab === 'messages'}
          onPress={() => {
            this.setState({
              selectedTab: 'messages',
            });
          }}>
          {this._renderContent()}
        </TabBarItemIOS>
        <TabBarItemIOS
          name="settings"
          iconName={'ion|ios-gear'}
          title={''}
          iconSize={32}
          accessibilityLabel="Settings Tab"
          selected={this.state.selectedTab === 'settings'}
          onPress={() => {
            this.setState({
              selectedTab: 'settings',
            });
          }}>
          {this._renderContent()}
        </TabBarItemIOS>
      </TabBarIOS>
    );
  },
  _renderContent: function () {
    return (
      <ScrollView contentContainerStyle={styles.container}>
        <View style={styles.topContainer}>
          <TouchableHighlight
            underlayColor="transparent"
            onPress={() => alert('Beer!')}>
            <Icon
              name='ion|beer'
              size={40}
              color='#887700'
              style={styles.beer}
              />
          </TouchableHighlight>
          <Icon
            name='material|face'
            size={40}
            color='black'
            style={styles.github}
            />
          <Icon
            name='fontawesome|facebook-square'
            size={40}
            color={BrandColors.Facebook}
            style={styles.facebook}
            />
          <Icon
            name='foundation|lightbulb'
            size={40}
            style={styles.lightbulb}/>
        </View>

        <Text style={styles.header}>
          {'Spinners'}
        </Text>

        <View style={styles.topContainer}>

          <Spinner name='fontawesome|spinner' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
          <Spinner name='ion|load-a' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
          <Spinner name='ion|load-b' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
          <Spinner name='ion|load-c' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
          <Spinner name='ion|load-d' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
        </View>

        <Text style={styles.header}>
          {'Stacked Icons!'}
        </Text>
        <Icon
          name='fontawesome|square'
          size={70}
          color={BrandColors.Twitter}
          style={styles.twitterOutline}>
          <Icon
            name='fontawesome|twitter'
            size={40}
            color='#ffffff'
            style={[styles.twitterIcon, {backgroundColor: 'transparent'}]}/>
        </Icon>

        <Icon
          name='octicons|alert'
          size={40}
          color='black'
          style={styles.alert}
        />

        <Text style={styles.header}>
          {'Create social sign in buttons'}
        </Text>
        <View
          style={styles.signInWithTwitterButton}>
          <Icon
            name='fontawesome|twitter'
            size={28}
            color='#ffffff'
            style={styles.signInWithTwitterIcon}/>
          <Text style={styles.signInText}>
            {'Sign in with Twitter'}
          </Text>
        </View>

        <View
          style={styles.signInWithFacebookButton}>
          <Icon
            name='fontawesome|facebook'
            size={28}
            color='#ffffff'
            style={styles.signInWithFacebookIcon}/>
          <Text style={styles.signInText}>
            {'Sign in with Facebook'}
          </Text>
        </View>

      </ScrollView>
    );
  }
});

var styles = StyleSheet.create({
  tabBar: {
    backgroundColor: '#dfdfdf',
    flex: 1,
    color: '#ff0000',
    tintColor: '#877324'
  },
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  topContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between'
  },
  header: {
    flexDirection: 'column',
    backgroundColor: 'transparent',
    fontSize: 18,
    marginTop: 20,
    marginBottom: 10,
    color: '#555555',
  },
  beer: {
    width: 70,
    height: 70,
    margin: 10
  },
  github: {
    width: 70,
    height: 70,
    margin: 10
  },
  facebook: {
    width: 70,
    height: 70,
    margin: 10
  },
  lightbulb: {
    width: 70,
    height: 70,
    margin: 10
  },
  alert: {
    width: 70,
    height: 70,
    margin: 10
  },
  twitterOutline: {
    flexDirection: 'column',
    width: 70,
    height: 70,
    alignItems: 'center'
  },
  twitterIcon: {
    flex: 1,
    width: 40,
    height: 40
  },
  signInWithTwitterButton: {
    backgroundColor: BrandColors.Twitter,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-start',
    width: 210,
    padding: 5,
    borderRadius: 3
  },
  signInWithTwitterIcon: {
    width: 28,
    height: 28,
    marginLeft: 5
  },
  signInText: {
    color: 'white',
    marginLeft: 5,
    fontFamily: 'HelveticaNeue-Medium',
    fontSize: 15
  },
  signInWithFacebookButton: {
    backgroundColor: BrandColors.Facebook,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-start',
    width: 210,
    padding: 5,
    borderRadius: 3,
    marginTop: 10
  },
  signInWithFacebookIcon: {
    width: 28,
    height: 28,
    marginLeft: 5
  }
});

AppRegistry.registerComponent('example', () => Example);
<commit_msg>Adding selectedIconName to iOS example app<commit_after>/**
 * React Native app demonstrating react-native-icons
 * https://github.com/corymsmith/react-native-icons
 */
'use strict';

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ScrollView,
  TouchableHighlight
  //Animated
  } = React;

var BrandColors = {
  Facebook: '#3b5998',
  Twitter: '#55acee'
};

var { Icon, TabBarIOS, Spinner} = require('react-native-icons');
var TabBarItemIOS = TabBarIOS.Item;

var Example = React.createClass({
  getInitialState: function () {
    return {
      selectedTab: 'home',
      notifCount: 0,
      presses: 0,
      //rotation: Animated.Value(0)
    };
  },
  render: function () {
    return (
      <TabBarIOS
        selectedTab={this.state.selectedTab}
        tintColor={'#c1d82f'}
        barTintColor={'#000000'}
        styles={styles.tabBar}>
        <TabBarItemIOS
          name="home"
          iconName={'ion|ios-home-outline'}
          selectedIconName={'ion|ios-home'}
          title={''}
          badgeValue={'3'}
          iconSize={32}
          accessibilityLabel="Home Tab"
          selected={this.state.selectedTab === 'home'}
          onPress={() => {
            this.setState({
              selectedTab: 'home',
            });
          }}>
          {this._renderContent()}
        </TabBarItemIOS>
        <TabBarItemIOS
          name="articles"
          iconName={'ion|ios-paper-outline'}
          title={''}
          iconSize={32}
          accessibilityLabel="Articles Tab"
          selected={this.state.selectedTab === 'articles'}
          onPress={() => {
            this.setState({
              selectedTab: 'articles',
            });
          }}>
          {this._renderContent()}
        </TabBarItemIOS>
        <TabBarItemIOS
          name="messages"
          iconName={'ion|chatboxes'}
          title={''}
          iconSize={32}
          accessibilityLabel="Messages Tab"
          selected={this.state.selectedTab === 'messages'}
          onPress={() => {
            this.setState({
              selectedTab: 'messages',
            });
          }}>
          {this._renderContent()}
        </TabBarItemIOS>
        <TabBarItemIOS
          name="settings"
          iconName={'ion|ios-gear'}
          title={''}
          iconSize={32}
          accessibilityLabel="Settings Tab"
          selected={this.state.selectedTab === 'settings'}
          onPress={() => {
            this.setState({
              selectedTab: 'settings',
            });
          }}>
          {this._renderContent()}
        </TabBarItemIOS>
      </TabBarIOS>
    );
  },
  _renderContent: function () {
    return (
      <ScrollView contentContainerStyle={styles.container}>
        <View style={styles.topContainer}>
          <TouchableHighlight
            underlayColor="transparent"
            onPress={() => alert('Beer!')}>
            <Icon
              name='ion|beer'
              size={40}
              color='#887700'
              style={styles.beer}
              />
          </TouchableHighlight>
          <Icon
            name='material|face'
            size={40}
            color='black'
            style={styles.github}
            />
          <Icon
            name='fontawesome|facebook-square'
            size={40}
            color={BrandColors.Facebook}
            style={styles.facebook}
            />
          <Icon
            name='foundation|lightbulb'
            size={40}
            style={styles.lightbulb}/>
        </View>

        <Text style={styles.header}>
          {'Spinners'}
        </Text>

        <View style={styles.topContainer}>

          <Spinner name='fontawesome|spinner' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
          <Spinner name='ion|load-a' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
          <Spinner name='ion|load-b' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
          <Spinner name='ion|load-c' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
          <Spinner name='ion|load-d' size={24} style={{width: 24, height: 24, backgroundColor: 'transparent'}}
                   color='#777'/>
        </View>

        <Text style={styles.header}>
          {'Stacked Icons!'}
        </Text>
        <Icon
          name='fontawesome|square'
          size={70}
          color={BrandColors.Twitter}
          style={styles.twitterOutline}>
          <Icon
            name='fontawesome|twitter'
            size={40}
            color='#ffffff'
            style={[styles.twitterIcon, {backgroundColor: 'transparent'}]}/>
        </Icon>

        <Icon
          name='octicons|alert'
          size={40}
          color='black'
          style={styles.alert}
        />

        <Text style={styles.header}>
          {'Create social sign in buttons'}
        </Text>
        <View
          style={styles.signInWithTwitterButton}>
          <Icon
            name='fontawesome|twitter'
            size={28}
            color='#ffffff'
            style={styles.signInWithTwitterIcon}/>
          <Text style={styles.signInText}>
            {'Sign in with Twitter'}
          </Text>
        </View>

        <View
          style={styles.signInWithFacebookButton}>
          <Icon
            name='fontawesome|facebook'
            size={28}
            color='#ffffff'
            style={styles.signInWithFacebookIcon}/>
          <Text style={styles.signInText}>
            {'Sign in with Facebook'}
          </Text>
        </View>

      </ScrollView>
    );
  }
});

var styles = StyleSheet.create({
  tabBar: {
    backgroundColor: '#dfdfdf',
    flex: 1,
    color: '#ff0000',
    tintColor: '#877324'
  },
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  topContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between'
  },
  header: {
    flexDirection: 'column',
    backgroundColor: 'transparent',
    fontSize: 18,
    marginTop: 20,
    marginBottom: 10,
    color: '#555555',
  },
  beer: {
    width: 70,
    height: 70,
    margin: 10
  },
  github: {
    width: 70,
    height: 70,
    margin: 10
  },
  facebook: {
    width: 70,
    height: 70,
    margin: 10
  },
  lightbulb: {
    width: 70,
    height: 70,
    margin: 10
  },
  alert: {
    width: 70,
    height: 70,
    margin: 10
  },
  twitterOutline: {
    flexDirection: 'column',
    width: 70,
    height: 70,
    alignItems: 'center'
  },
  twitterIcon: {
    flex: 1,
    width: 40,
    height: 40
  },
  signInWithTwitterButton: {
    backgroundColor: BrandColors.Twitter,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-start',
    width: 210,
    padding: 5,
    borderRadius: 3
  },
  signInWithTwitterIcon: {
    width: 28,
    height: 28,
    marginLeft: 5
  },
  signInText: {
    color: 'white',
    marginLeft: 5,
    fontFamily: 'HelveticaNeue-Medium',
    fontSize: 15
  },
  signInWithFacebookButton: {
    backgroundColor: BrandColors.Facebook,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-start',
    width: 210,
    padding: 5,
    borderRadius: 3,
    marginTop: 10
  },
  signInWithFacebookIcon: {
    width: 28,
    height: 28,
    marginLeft: 5
  }
});

AppRegistry.registerComponent('example', () => Example);
<|endoftext|>


<commit_before>var Scrollable = require('./Scrollable');

/**
 * Simple slider with min. and max. value
 *
 * @class Slider
 * @extends GOWN.Scrollable
 * @memberof GOWN
 * @constructor
 */

function Slider(thumb, theme, skinName) {
    this.skinName = skinName || Slider.SKIN_NAME;

    this._minimum = this._minimum || 0;
    this._maximum = this._maximum || 100;
    this.step = this.step || 0; //TODO: implement me!
    this.page = this.page || 10; //TODO: implement me!
    this._value = this.minimum;
    this.change = null;

    Scrollable.call(this, thumb, theme);
}

Slider.prototype = Object.create( Scrollable.prototype );
Slider.prototype.constructor = Slider;
module.exports = Slider;

Slider.SKIN_NAME = 'scroll_bar';

/**
 * thumb has been moved - calculate new value
 *
 * @param x x-position to scroll to (ignored when vertical)
 * @param y y-position to scroll to (ignored when horizontal)
 */
Slider.prototype.thumbMoved = function(x, y) {
    var pos = 0;
    if (this.direction === Scrollable.HORIZONTAL) {
        pos = x;
    } else {
        pos = y;
    }
    this.value = this.pixelToValue(pos);
};
<commit_msg>Fix constructor parameter<commit_after>var Scrollable = require('./Scrollable');

/**
 * Simple slider with min. and max. value
 *
 * @class Slider
 * @extends GOWN.Scrollable
 * @memberof GOWN
 * @constructor
 */
// TODO: move stuff from Scrollable back here?
function Slider(theme) {
    this._skinName = this._skinName || Slider.SKIN_NAME;

    Scrollable.call(this, theme);
}

Slider.prototype = Object.create( Scrollable.prototype );
Slider.prototype.constructor = Slider;
module.exports = Slider;

Slider.SKIN_NAME = 'scroll_bar';
<|endoftext|>


<commit_before>import {MODE_IDLE, MODE_PANNING} from '../constants';
import {set, getSVGPoint} from './common';
import {fromObject, translate, transform, applyToPoints, inverse} from 'transformation-matrix';

/**
 *
 * @param value
 * @param SVGDeltaX
 * @param SVGDeltaY
 * @param panLimit
 * @returns {Object}
 */
export function pan(value, SVGDeltaX, SVGDeltaY, panLimit = undefined) {

  let matrix = transform(
    fromObject(value),              //2
    translate(SVGDeltaX, SVGDeltaY) //1
  );

  // apply pan limits
  if (panLimit) {
    let [{x:x1, y:y1}, {x:x2, y:y2}] = applyToPoints(matrix, [
      {x: panLimit, y: panLimit},
      {x: value.SVGWidth - panLimit, y: value.SVGHeight - panLimit}
    ]);

    //x limit
    let moveX = 0;
    if (value.viewerWidth - x1 < 0)
      moveX = value.viewerWidth - x1;
    else if (x2 < 0) moveX = -x2;

    //y limit
    let moveY = 0;
    if (value.viewerHeight - y1 < 0)
      moveY = value.viewerHeight - y1;
    else if (y2 < 0) moveY = -y2;

    //apply limits
    matrix = transform(
      translate(moveX, moveY),
      matrix
    )
  }

  return set(value, {
    mode: MODE_IDLE,
    ...matrix,
  });
}

export function startPanning(value, viewerX, viewerY) {
  return set(value, {
    mode: MODE_PANNING,
    startX: viewerX,
    startY: viewerY,
    endX: viewerX,
    endY: viewerY
  });
}

export function updatePanning(value, viewerX, viewerY, panLimit) {
  if (value.mode !== MODE_PANNING) throw new Error('update pan not allowed in this mode ' + value.mode);

  let {endX, endY} = value;

  let start = getSVGPoint(value, endX, endY);
  let end = getSVGPoint(value, viewerX, viewerY);

  let deltaX = end.x - start.x;
  let deltaY = end.y - start.y;

  let nextValue = pan(value, deltaX, deltaY, panLimit);
  return set(nextValue, {
    mode: MODE_PANNING,
    endX: viewerX,
    endY: viewerY,
  });
}

export function stopPanning(value) {
  return set(value, {
      mode: MODE_IDLE,
      startX: null,
      startY: null,
      endX: null,
      endY: null
    }
  );
}

export function autoPanIfNeeded(value, viewerX, viewerY) {
  let deltaX = 0;
  let deltaY = 0;

  if (viewerY <= 20) deltaY = 20;
  if (value.viewerWidth - viewerX <= 20) deltaX = -20;
  if (value.viewerHeight - viewerY <= 20) deltaY = -20;
  if (viewerX <= 20) deltaX = 20;

  return (deltaX === 0 && deltaY === 0) ? value : pan(value, deltaX, deltaY);
}
<commit_msg>improve autopan delta shift<commit_after>import {MODE_IDLE, MODE_PANNING} from '../constants';
import {set, getSVGPoint} from './common';
import {fromObject, translate, transform, applyToPoints, inverse} from 'transformation-matrix';

/**
 *
 * @param value
 * @param SVGDeltaX
 * @param SVGDeltaY
 * @param panLimit
 * @returns {Object}
 */
export function pan(value, SVGDeltaX, SVGDeltaY, panLimit = undefined) {

  let matrix = transform(
    fromObject(value),              //2
    translate(SVGDeltaX, SVGDeltaY) //1
  );

  // apply pan limits
  if (panLimit) {
    let [{x: x1, y: y1}, {x: x2, y: y2}] = applyToPoints(matrix, [
      {x: panLimit, y: panLimit},
      {x: value.SVGWidth - panLimit, y: value.SVGHeight - panLimit}
    ]);

    //x limit
    let moveX = 0;
    if (value.viewerWidth - x1 < 0)
      moveX = value.viewerWidth - x1;
    else if (x2 < 0) moveX = -x2;

    //y limit
    let moveY = 0;
    if (value.viewerHeight - y1 < 0)
      moveY = value.viewerHeight - y1;
    else if (y2 < 0) moveY = -y2;

    //apply limits
    matrix = transform(
      translate(moveX, moveY),
      matrix
    )
  }

  return set(value, {
    mode: MODE_IDLE,
    ...matrix,
  });
}

export function startPanning(value, viewerX, viewerY) {
  return set(value, {
    mode: MODE_PANNING,
    startX: viewerX,
    startY: viewerY,
    endX: viewerX,
    endY: viewerY
  });
}

export function updatePanning(value, viewerX, viewerY, panLimit) {
  if (value.mode !== MODE_PANNING) throw new Error('update pan not allowed in this mode ' + value.mode);

  let {endX, endY} = value;

  let start = getSVGPoint(value, endX, endY);
  let end = getSVGPoint(value, viewerX, viewerY);

  let deltaX = end.x - start.x;
  let deltaY = end.y - start.y;

  let nextValue = pan(value, deltaX, deltaY, panLimit);
  return set(nextValue, {
    mode: MODE_PANNING,
    endX: viewerX,
    endY: viewerY,
  });
}

export function stopPanning(value) {
  return set(value, {
      mode: MODE_IDLE,
      startX: null,
      startY: null,
      endX: null,
      endY: null
    }
  );
}

export function autoPanIfNeeded(value, viewerX, viewerY) {
  let deltaX = 0;
  let deltaY = 0;

  if (viewerY <= 20) deltaY = 20;
  if (value.viewerWidth - viewerX <= 20) deltaX = -20;
  if (value.viewerHeight - viewerY <= 20) deltaY = -20;
  if (viewerX <= 20) deltaX = 20;

  deltaX = deltaX / value.d;
  deltaY = deltaY / value.d;

  return (deltaX === 0 && deltaY === 0) ? value : pan(value, deltaX, deltaY);
}
<|endoftext|>


<commit_before>export const fetchTimeout = (fetchPromise, timeout) => {
    let abortFunc = null;
    let abortFromise = new Promise((resolve, reject) => {
        abortFunc = msg => {
            reject(msg || 'timeout');
        }
    });

    let abortablePromise = Promise.race([
        fetchPromise,
        abortPromise,
    ]);

    setTimeout(() => abortFunc(), timeout);

    return abortablePromise;
};

<commit_msg>modify fetch timeout<commit_after>export const fetchTimeout = (fetchPromise, timeout = 6000) => {
    let abortFunc = null;
    let abortPromise = new Promise((resolve, reject) => {
        abortFunc = msg => {
            reject(msg || 'timeout');
        }
    });

    let abortablePromise = Promise.race([
        fetchPromise,
        abortPromise,
    ]);

    setTimeout(() => abortFunc(), timeout);

    return abortablePromise;
};

<|endoftext|>


<commit_before>/**
 * @author David Corvoysier / Copyright Orange 2013
 */
(function(global) {

    // Register this filter
    global.addFilter(
        // Filter name
        'skin',
        // Vertex Shader (null uses default)
        null,
        // Fragment Shader (null uses default)
        '\
        uniform sampler2D texture;\
        varying vec2 texCoord;\
        void main() {\
            vec4 color = texture2D(texture, texCoord);\
            float r = color.r;\
            float g = color.g;\
            float b = color.b;\
            \
            if ((r<=45.0/255.0)||(g<=40.0/255.0)||(b<=20.0/255.0)\
                ||(r<=g)||(r<=b)\
                ||((r-min(g,b))<=15.0/255.0)\
                ||(abs(r-g)<=15.0/255.0)){\
                gl_FragColor = vec4(0.0,0.0,0.0,color.a);\
            } else {\
                gl_FragColor = color;\
            }\
        }\
        ',
        // Uniforms callback
        null
    );

})(glimp);
<commit_msg>skin filter: do not retain initial alpha on black pixels when applying skin mask<commit_after>/**
 * @author David Corvoysier / Copyright Orange 2013
 */
(function(global) {

    // Register this filter
    global.addFilter(
        // Filter name
        'skin',
        // Vertex Shader (null uses default)
        null,
        // Fragment Shader (null uses default)
        '\
        uniform sampler2D texture;\
        varying vec2 texCoord;\
        void main() {\
            vec4 color = texture2D(texture, texCoord);\
            float r = color.r;\
            float g = color.g;\
            float b = color.b;\
            \
            if ((r<=45.0/255.0)||(g<=40.0/255.0)||(b<=20.0/255.0)\
                ||(r<=g)||(r<=b)\
                ||((r-min(g,b))<=15.0/255.0)\
                ||(abs(r-g)<=15.0/255.0)){\
                gl_FragColor = vec4(0.0,0.0,0.0,0.0);\
            } else {\
                gl_FragColor = color;\
            }\
        }\
        ',
        // Uniforms callback
        null
    );

})(glimp);
<|endoftext|>


<commit_before>/**
 * Web Worker to handle communications between the front-end and the core.
 *
 * @author n1474335 [n1474335@gmail.com]
 * @copyright Crown Copyright 2017
 * @license Apache-2.0
 */

import Chef from "./Chef";
import OperationConfig from "./config/OperationConfig.json";
import OpModules from "./config/modules/OpModules";

// Add ">" to the start of all log messages in the Chef Worker
import loglevelMessagePrefix from "loglevel-message-prefix";

loglevelMessagePrefix(log, {
    prefixes: [],
    staticPrefixes: [">"],
    prefixFormat: "%p"
});

// Set up Chef instance
self.chef = new Chef();

self.OpModules = OpModules;
self.OperationConfig = OperationConfig;
self.inputNum = -1;

// Tell the app that the worker has loaded and is ready to operate
self.postMessage({
    action: "workerLoaded",
    data: {}
});

/**
 * Respond to message from parent thread.
 *
 * Messages should have the following format:
 * {
 *     action: "bake" | "silentBake",
 *     data: {
 *         input: {string},
 *         recipeConfig: {[Object]},
 *         options: {Object},
 *         progress: {number},
 *         step: {boolean}
 *     } | undefined
 * }
 */
self.addEventListener("message", function(e) {
    // Handle message
    const r = e.data;
    log.debug("ChefWorker receiving command '" + r.action + "'");

    switch (r.action) {
        case "bake":
            bake(r.data);
            break;
        case "silentBake":
            silentBake(r.data);
            break;
        case "getDishAs":
            getDishAs(r.data);
            break;
        case "docURL":
            // Used to set the URL of the current document so that scripts can be
            // imported into an inline worker.
            self.docURL = r.data;
            break;
        case "highlight":
            calculateHighlights(
                r.data.recipeConfig,
                r.data.direction,
                r.data.pos
            );
            break;
        case "setLogLevel":
            log.setLevel(r.data, false);
            break;
        default:
            break;
    }
});

/**
 * Baking handler
 *
 * @param {Object} data
 */
async function bake(data) {
    // Ensure the relevant modules are loaded
    self.loadRequiredModules(data.recipeConfig);
    try {
        self.inputNum = parseInt(data.inputNum, 10);
        const response = await self.chef.bake(
            data.input,          // The user's input
            data.recipeConfig,   // The configuration of the recipe
            data.options,        // Options set by the user
            data.progress,       // The current position in the recipe
            data.step            // Whether or not to take one step or execute the whole recipe
        );
        self.postMessage({
            action: "bakeComplete",
            data: Object.assign(response, {
                id: data.id,
                inputNum: data.inputNum,
                bakeId: data.bakeId
            })
        });
    } catch (err) {
        self.postMessage({
            action: "bakeError",
            data: {
                error: err.message || err,
                id: data.id,
                inputNum: data.inputNum
            }
        });
    }
    self.inputNum = -1;
}

/**
 * Silent baking handler
 */
function silentBake(data) {
    const duration = self.chef.silentBake(data.recipeConfig);

    self.postMessage({
        action: "silentBakeComplete",
        data: duration
    });
}

/**
 * Translates the dish to a given type.
 */
async function getDishAs(data) {
    const value = await self.chef.getDishAs(data.dish, data.type);

    self.postMessage({
        action: "dishReturned",
        data: {
            value: value,
            id: data.id
        }
    });
}

/**
 * Calculates highlight offsets if possible.
 *
 * @param {Object[]} recipeConfig
 * @param {string} direction
 * @param {Object} pos - The position object for the highlight.
 * @param {number} pos.start - The start offset.
 * @param {number} pos.end - The end offset.
 */
async function calculateHighlights(recipeConfig, direction, pos) {
    pos = await self.chef.calculateHighlights(recipeConfig, direction, pos);

    self.postMessage({
        action: "highlightsCalculated",
        data: pos
    });
}

/**
 * Checks that all required modules are loaded and loads them if not.
 *
 * @param {Object} recipeConfig
 */
self.loadRequiredModules = function(recipeConfig) {
    recipeConfig.forEach(op => {
        const module = self.OperationConfig[op.op].module;

        if (!OpModules.hasOwnProperty(module)) {
            log.info(`Loading ${module} module`);
            self.sendStatusMessage(`Loading ${module} module`);
            self.importScripts(`${self.docURL}/modules/${module}.js`);
            self.sendStatusMessage("");
        }
    });
};

/**
 * Send status update to the app.
 *
 * @param {string} msg
 */
self.sendStatusMessage = function(msg) {
    self.postMessage({
        action: "statusMessage",
        data: {
            message: msg,
            inputNum: self.inputNum || -1
        }
    });
};

/**
 * Send an option value update to the app.
 *
 * @param {string} option
 * @param {*} value
 */
self.setOption = function(option, value) {
    self.postMessage({
        action: "optionUpdate",
        data: {
            option: option,
            value: value
        }
    });
};

/**
 * Send register values back to the app.
 *
 * @param {number} opIndex
 * @param {number} numPrevRegisters
 * @param {string[]} registers
 */
self.setRegisters = function(opIndex, numPrevRegisters, registers) {
    self.postMessage({
        action: "setRegisters",
        data: {
            opIndex: opIndex,
            numPrevRegisters: numPrevRegisters,
            registers: registers
        }
    });
};
<commit_msg>Use transferable objects to send arraybuffers back to the workerwaiter<commit_after>/**
 * Web Worker to handle communications between the front-end and the core.
 *
 * @author n1474335 [n1474335@gmail.com]
 * @copyright Crown Copyright 2017
 * @license Apache-2.0
 */

import Chef from "./Chef";
import OperationConfig from "./config/OperationConfig.json";
import OpModules from "./config/modules/OpModules";

// Add ">" to the start of all log messages in the Chef Worker
import loglevelMessagePrefix from "loglevel-message-prefix";

loglevelMessagePrefix(log, {
    prefixes: [],
    staticPrefixes: [">"],
    prefixFormat: "%p"
});

// Set up Chef instance
self.chef = new Chef();

self.OpModules = OpModules;
self.OperationConfig = OperationConfig;
self.inputNum = -1;

// Tell the app that the worker has loaded and is ready to operate
self.postMessage({
    action: "workerLoaded",
    data: {}
});

/**
 * Respond to message from parent thread.
 *
 * Messages should have the following format:
 * {
 *     action: "bake" | "silentBake",
 *     data: {
 *         input: {string},
 *         recipeConfig: {[Object]},
 *         options: {Object},
 *         progress: {number},
 *         step: {boolean}
 *     } | undefined
 * }
 */
self.addEventListener("message", function(e) {
    // Handle message
    const r = e.data;
    log.debug("ChefWorker receiving command '" + r.action + "'");

    switch (r.action) {
        case "bake":
            bake(r.data);
            break;
        case "silentBake":
            silentBake(r.data);
            break;
        case "getDishAs":
            getDishAs(r.data);
            break;
        case "docURL":
            // Used to set the URL of the current document so that scripts can be
            // imported into an inline worker.
            self.docURL = r.data;
            break;
        case "highlight":
            calculateHighlights(
                r.data.recipeConfig,
                r.data.direction,
                r.data.pos
            );
            break;
        case "setLogLevel":
            log.setLevel(r.data, false);
            break;
        default:
            break;
    }
});

/**
 * Baking handler
 *
 * @param {Object} data
 */
async function bake(data) {
    // Ensure the relevant modules are loaded
    self.loadRequiredModules(data.recipeConfig);
    try {
        self.inputNum = parseInt(data.inputNum, 10);
        const response = await self.chef.bake(
            data.input,          // The user's input
            data.recipeConfig,   // The configuration of the recipe
            data.options,        // Options set by the user
            data.progress,       // The current position in the recipe
            data.step            // Whether or not to take one step or execute the whole recipe
        );

        if (data.input instanceof ArrayBuffer) {
            self.postMessage({
                action: "bakeComplete",
                data: Object.assign(response, {
                    id: data.id,
                    inputNum: data.inputNum,
                    bakeId: data.bakeId
                })
            }, [data.input]);
        } else {
            self.postMessage({
                action: "bakeComplete",
                data: Object.assign(response, {
                    id: data.id,
                    inputNum: data.inputNum,
                    bakeId: data.bakeId
                })
            });
        }
    } catch (err) {
        self.postMessage({
            action: "bakeError",
            data: {
                error: err.message || err,
                id: data.id,
                inputNum: data.inputNum
            }
        });
    }
    self.inputNum = -1;
}

/**
 * Silent baking handler
 */
function silentBake(data) {
    const duration = self.chef.silentBake(data.recipeConfig);

    self.postMessage({
        action: "silentBakeComplete",
        data: duration
    });
}

/**
 * Translates the dish to a given type.
 */
async function getDishAs(data) {
    const value = await self.chef.getDishAs(data.dish, data.type);

    self.postMessage({
        action: "dishReturned",
        data: {
            value: value,
            id: data.id
        }
    });
}

/**
 * Calculates highlight offsets if possible.
 *
 * @param {Object[]} recipeConfig
 * @param {string} direction
 * @param {Object} pos - The position object for the highlight.
 * @param {number} pos.start - The start offset.
 * @param {number} pos.end - The end offset.
 */
async function calculateHighlights(recipeConfig, direction, pos) {
    pos = await self.chef.calculateHighlights(recipeConfig, direction, pos);

    self.postMessage({
        action: "highlightsCalculated",
        data: pos
    });
}

/**
 * Checks that all required modules are loaded and loads them if not.
 *
 * @param {Object} recipeConfig
 */
self.loadRequiredModules = function(recipeConfig) {
    recipeConfig.forEach(op => {
        const module = self.OperationConfig[op.op].module;

        if (!OpModules.hasOwnProperty(module)) {
            log.info(`Loading ${module} module`);
            self.sendStatusMessage(`Loading ${module} module`);
            self.importScripts(`${self.docURL}/modules/${module}.js`);
            self.sendStatusMessage("");
        }
    });
};

/**
 * Send status update to the app.
 *
 * @param {string} msg
 */
self.sendStatusMessage = function(msg) {
    self.postMessage({
        action: "statusMessage",
        data: {
            message: msg,
            inputNum: self.inputNum || -1
        }
    });
};

/**
 * Send an option value update to the app.
 *
 * @param {string} option
 * @param {*} value
 */
self.setOption = function(option, value) {
    self.postMessage({
        action: "optionUpdate",
        data: {
            option: option,
            value: value
        }
    });
};

/**
 * Send register values back to the app.
 *
 * @param {number} opIndex
 * @param {number} numPrevRegisters
 * @param {string[]} registers
 */
self.setRegisters = function(opIndex, numPrevRegisters, registers) {
    self.postMessage({
        action: "setRegisters",
        data: {
            opIndex: opIndex,
            numPrevRegisters: numPrevRegisters,
            registers: registers
        }
    });
};
<|endoftext|>


<commit_before>import Mixin, { HasInstance } from './Mixin'
import { isInstanceof } from './Utility'

test('TODO: Mixin test', () => {
    const Foo = Mixin(Base => class Foo extends Base { })

    expect( typeof Foo ).toBe( 'function' )
    expect( typeof Foo.mixin ).toBe( 'function' )

    class Bar {}
    const Baz = Foo.mixin( Bar )
    const Lorem = Foo.mixin( Bar )

    expect( typeof Baz ).toBe( 'function' )
    expect( typeof Lorem ).toBe( 'function' )

    // caching of the same mixin application
    expect( Baz ).toBe( Lorem )

    const baz = new Baz

    expect( baz ).toBeInstanceOf( Foo )
    expect( isInstanceof( baz, Foo ) ).toBe( true )

    expect( baz ).toBeInstanceOf( Bar )
    expect( isInstanceof( baz, Bar ) ).toBe( true )

    expect( baz ).toBeInstanceOf( Baz )
    expect( isInstanceof( baz, Baz ) ).toBe( true )

    expect( baz ).toBeInstanceOf( Lorem )
    expect( isInstanceof( baz, Lorem ) ).toBe( true )

    expect( {} ).not.toBeInstanceOf( Baz )

    class Thing extends Baz {}

    expect( new Thing ).toBeInstanceOf( Thing )

    { // When Symbol is supported
        const Ipsum = Mixin(Base => class Ipsum extends Base { })
        class Blah {}
        const One = Ipsum.mixin( Blah )

        const one = new One

        expect( one ).toBeInstanceOf( One )

        // there's two versions of Ipsum in play, the original one, and the one
        // created when making `One`, but instanceof checks still work:
        expect( one ).toBeInstanceOf( Ipsum )
    }

    { // when Symbol is not supported
        function test() {
            const Ipsum = Mixin(Base => class Ipsum extends Base { })
            class Blah {}
            const One = Ipsum.mixin( Blah )

            const one = new One

            expect( one ).toBeInstanceOf( One )

            // Without Symbol.hasInstance, the internal trick doesn't work, so
            // instanceof won't be useful like we'd like it to be:
            expect( one ).not.toBeInstanceOf( Ipsum )
        }

        const originalSymbol = Symbol

        Symbol = () => Math.random()

        test()

        Symbol = void 0

        test()

        Symbol = originalSymbol
    }

    { // if a class already has it's own Symbol.hasInstance method, we don't override it
        function fn() {}

        const FooMixin = HasInstance(Base => {
            const Class = class Foo extends Base {}

            Object.defineProperty(Class, Symbol.hasInstance, { value: fn })

            return Class
        })

        const Foo = FooMixin(class{})

        expect( Foo[Symbol.hasInstance] ).toBe( fn )

        const BarMixin = HasInstance(Base => class Bar extends Base {})

        const Bar = BarMixin(class{})

        expect( Bar[Symbol.hasInstance] ).not.toBe( fn )
    }

    { // configuring a default base class
        const Foo = Mixin(Base => class Foo extends Base { }, Map)
        const Bar = class Bar extends Foo {}
        const bar = new Bar
        const Baz = class Baz extends Foo.mixin(WeakMap) {}
        const baz = new Baz

        expect( bar ).toBeInstanceOf( Map )

        expect( baz ).not.toBeInstanceOf( Map )
        expect( baz ).toBeInstanceOf( WeakMap )
    }

    { // check there's no duplicate applications of a mixin in a class hierarchy
        const Foo = Mixin(Base => class Foo extends Base { }, Map)
        class Bar extends Foo {}

        // because Bar already has Foo
        expect( Foo.mixin( Bar ) ).toBe( Bar )
    }
})
<commit_msg>clean up Mixin tests, using 'test()' for each one<commit_after>import Mixin, { HasInstance } from './Mixin'
import { isInstanceof } from './Utility'

test('Mixin returns a Function', () => {
    const Foo = Mixin(Base => class Foo extends Base { })
    class Bar {}
    const Baz = Foo.mixin( Bar )
    const Lorem = Foo.mixin( Bar )

    expect( typeof Foo ).toBe( 'function' )
    expect( typeof Foo.mixin ).toBe( 'function' )
    expect( typeof Baz ).toBe( 'function' )
    expect( typeof Lorem ).toBe( 'function' )
})

test('Mixin applications are cached', () => {
    const Foo = Mixin(Base => class Foo extends Base { })
    class Bar {}
    const Baz = Foo.mixin( Bar )
    const Lorem = Foo.mixin( Bar )

    // caching of the same mixin application
    expect( Baz ).toBe( Lorem )
})

test('instanceof works with multiple classes generated from the same Mixin', () => {
    const Foo = Mixin(Base => class Foo extends Base { })
    class Bar {}
    const Baz = Foo.mixin( Bar )
    const Lorem = Foo.mixin( Bar )

    const baz = new Baz

    expect( baz ).toBeInstanceOf( Foo )
    expect( isInstanceof( baz, Foo ) ).toBe( true )

    expect( baz ).toBeInstanceOf( Bar )
    expect( isInstanceof( baz, Bar ) ).toBe( true )

    expect( baz ).toBeInstanceOf( Baz )
    expect( isInstanceof( baz, Baz ) ).toBe( true )

    expect( baz ).toBeInstanceOf( Lorem )
    expect( isInstanceof( baz, Lorem ) ).toBe( true )
})

test('HasInstance delegates to super Symbol.hasInstance method, so regular instanceof works', () => {
    const Foo = Mixin(Base => class Foo extends Base { })
    class Bar {}
    const Baz = Foo.mixin( Bar )
    const Lorem = Foo.mixin( Bar )

    expect( {} ).not.toBeInstanceOf( Baz )

    class Thing extends Baz {}

    expect( new Thing ).toBeInstanceOf( Thing )
})

test('When Symbol is supported, instanceof works', () => {

    const Ipsum = Mixin(Base => class Ipsum extends Base { })
    class Blah {}
    const One = Ipsum.mixin( Blah )

    const one = new One

    expect( one ).toBeInstanceOf( One )

    // there's two versions of Ipsum in play, the original one, and the one
    // created when making `One`, but instanceof checks still work:
    expect( one ).toBeInstanceOf( Ipsum )
})

test('When Symbol is not supported, instanceof does not work', () => {

    function test() {
        const Ipsum = Mixin(Base => class Ipsum extends Base { })
        class Blah {}
        const One = Ipsum.mixin( Blah )

        const one = new One

        expect( one ).toBeInstanceOf( One )

        // Without Symbol.hasInstance, the internal trick doesn't work, so
        // instanceof won't be useful like we'd like it to be:
        expect( one ).not.toBeInstanceOf( Ipsum )
    }

    const originalSymbol = Symbol

    Symbol = () => Math.random()

    test()

    Symbol = void 0

    test()

    Symbol = originalSymbol
})

test('if a class already has its own Symbol.hasInstance method, we do not override it', () => {
    function fn() {}

    const FooMixin = HasInstance(Base => {
        const Class = class Foo extends Base {}

        Object.defineProperty(Class, Symbol.hasInstance, { value: fn })

        return Class
    })

    const Foo = FooMixin(class{})

    expect( Foo[Symbol.hasInstance] ).toBe( fn )

    const BarMixin = HasInstance(Base => class Bar extends Base {})

    const Bar = BarMixin(class{})

    expect( Bar[Symbol.hasInstance] ).not.toBe( fn )
})

test('configuring a default base class', () => {

    const Foo = Mixin(Base => class Foo extends Base { }, Map)
    const Bar = class Bar extends Foo {}
    const bar = new Bar
    const Baz = class Baz extends Foo.mixin(WeakMap) {}
    const baz = new Baz

    expect( bar ).toBeInstanceOf( Map )

    expect( baz ).not.toBeInstanceOf( Map )
    expect( baz ).toBeInstanceOf( WeakMap )
})

test('check there are no duplicate applications of a mixin in a class hierarchy', () => {

    const Foo = Mixin(Base => class Foo extends Base { }, Map)
    class Bar extends Foo {}

    // because Bar already has Foo
    expect( Foo.mixin( Bar ) ).toBe( Bar )
})
<|endoftext|>


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

function HttpProbe() {
	Probe.call(this, 'http');
	this.config = {
			filters: []
	};
}
util.inherits(HttpProbe, Probe);

HttpProbe.prototype.attach = function(name, target, am) {
	var that = this;
	if( name == 'http' ) {
		if(target.__probeAttached__) return;
	    target.__probeAttached__ = true;
	    var methods = ['on', 'addListener'];
	    aspect.before(target.Server.prototype, methods,
	      function(obj, args) {
	        if(args[0] !== 'request') return;
	        if(obj.__httpProbe__) return;
	        obj.__httpProbe__ = true;
	        aspect.aroundCallback(args, function(obj, args) {
	            var req = args[0];
	            var res = args[1];
	            // Filter out urls where filter.to is ''
	            var traceUrl = that.filterUrl(req);
	            if (traceUrl !== '') {
	            	that.metricsProbeStart(req.method, traceUrl);
	            	that.requestProbeStart(req.method, traceUrl);
	            	aspect.after(res, 'end',function(obj, args, ret) {
            			that.metricsProbeEnd(req.method, traceUrl, am);
            			that.requestProbeEnd(req.method, traceUrl);
	            	});
	            }
	        });
	    });
	}
	return target;
};

/*
 * Ignore requests for URLs which we've been configured via regex to ignore
 */
HttpProbe.prototype.filterUrl = function(req) {
    var resultUrl = url.parse( req.url, true ).pathname;
    var filters = this.config.filters;
    if (filters.length == 0) return resultUrl;

    var identifier = req.method + ' ' + resultUrl;
    for (var i = 0; i < filters.length; ++i) {
        var filter = filters[i];
        if (filter.regex.test(identifier)) {
            return filter.to;
        }
    }
    return resultUrl;
}

/*
 * Lightweight metrics probe for HTTP requests
 *
 * These provide:
 * 		time:		time event started
 * 		method:		HTTP method, eg. GET, POST, etc
 * 		url:		The url requested
 * 		duration:	the time for the request to respond
 */

HttpProbe.prototype.metricsEnd = function(method, url, am) {
	am.emit('http', {time: start, method: method, url: url, duration: this.getDuration()});
};

/*
 * Heavyweight request probes for HTTP requests
 */

HttpProbe.prototype.requestStart = function (method, url, am) {
    var reqType = 'HTTP';
    // Mark as a root request as this happens due to an external event
    tr = request.startRequest(reqType, url, true);
};

HttpProbe.prototype.requestEnd = function (method, url, am) {
    tr.stop({url: url });
};

/*
 * Set configuration by merging passed in config with current one
 */
HttpProbe.prototype.setConfig = function (newConfig) {
	if (typeof(newConfig.filters) !== 'undefined') {
		newConfig.filters.forEach(function(filter) {
			if (typeof(filter.regex) === 'undefined') {
				filter.regex = new RegExp(filter.pattern);
			}
		});
	}
	for (var prop in newConfig) {
		if (typeof(newConfig[prop]) !== 'undefined') {
			this.config[prop] = newConfig[prop];
		}
	}
};

module.exports = HttpProbe;<commit_msg>Improve performance of HttpProbe.filterUrl()<commit_after>/*******************************************************************************
 * Copyright 2015 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
var Probe = require('../lib/probe.js');
var aspect = require('../lib/aspect.js');
var request = require('../lib/request.js');
var util = require('util');
var url = require('url');

function HttpProbe() {
	Probe.call(this, 'http');
	this.config = {
			filters: []
	};
}
util.inherits(HttpProbe, Probe);

HttpProbe.prototype.attach = function(name, target, am) {
	var that = this;
	if( name == 'http' ) {
		if(target.__probeAttached__) return;
	    target.__probeAttached__ = true;
	    var methods = ['on', 'addListener'];
	    aspect.before(target.Server.prototype, methods,
	      function(obj, args) {
	        if(args[0] !== 'request') return;
	        if(obj.__httpProbe__) return;
	        obj.__httpProbe__ = true;
	        aspect.aroundCallback(args, function(obj, args) {
	            var req = args[0];
	            var res = args[1];
	            // Filter out urls where filter.to is ''
	            var traceUrl = that.filterUrl(req);
	            if (traceUrl !== '') {
	            	that.metricsProbeStart(req.method, traceUrl);
	            	that.requestProbeStart(req.method, traceUrl);
	            	aspect.after(res, 'end',function(obj, args, ret) {
            			that.metricsProbeEnd(req.method, traceUrl, am);
            			that.requestProbeEnd(req.method, traceUrl);
	            	});
	            }
	        });
	    });
	}
	return target;
};

/*
 * Custom req.url parser that strips out any trailing query
 */
var parse = function (url) {
	['?','#'].forEach(function (separator)  {
		var index = url.indexOf(separator);
		if (index !== -1) url = url.substring(0, index);
	});
	return url;
};

/*
 * Ignore requests for URLs which we've been configured via regex to ignore
 */
HttpProbe.prototype.filterUrl = function(req) {
    var resultUrl = parse(req.url);
    var filters = this.config.filters;
    if (filters.length == 0) return resultUrl;

    var identifier = req.method + ' ' + resultUrl;
    for (var i = 0; i < filters.length; ++i) {
        var filter = filters[i];
        if (filter.regex.test(identifier)) {
            return filter.to;
        }
    }
    return resultUrl;
}

/*
 * Lightweight metrics probe for HTTP requests
 *
 * These provide:
 * 		time:		time event started
 * 		method:		HTTP method, eg. GET, POST, etc
 * 		url:		The url requested
 * 		duration:	the time for the request to respond
 */

HttpProbe.prototype.metricsEnd = function(method, url, am) {
	am.emit('http', {time: start, method: method, url: url, duration: this.getDuration()});
};

/*
 * Heavyweight request probes for HTTP requests
 */

HttpProbe.prototype.requestStart = function (method, url, am) {
    var reqType = 'HTTP';
    // Mark as a root request as this happens due to an external event
    tr = request.startRequest(reqType, url, true);
};

HttpProbe.prototype.requestEnd = function (method, url, am) {
    tr.stop({url: url });
};

/*
 * Set configuration by merging passed in config with current one
 */
HttpProbe.prototype.setConfig = function (newConfig) {
	if (typeof(newConfig.filters) !== 'undefined') {
		newConfig.filters.forEach(function(filter) {
			if (typeof(filter.regex) === 'undefined') {
				filter.regex = new RegExp(filter.pattern);
			}
		});
	}
	for (var prop in newConfig) {
		if (typeof(newConfig[prop]) !== 'undefined') {
			this.config[prop] = newConfig[prop];
		}
	}
};

module.exports = HttpProbe;<|endoftext|>


<commit_before>describe("Hello World", function() {
    it("should return hello world", function() {
        expect(helloWorld()).toEqual('Hello World');
    });
});
<commit_msg>run jslint on helloWorld spec in order to resolve spacing warnings<commit_after>describe("Hello World", function() {
    "use strict";
    it("should return hello world", function() {
        expect(helloWorld()).toEqual('Hello World');
    });
});
<|endoftext|>