<commit_before>'use strict';

var createScatterLine = require('./line-with-markers'),
    tinycolor = require('tinycolor2'),
    arrtools = require('arraytools'),
    calculateError = require('./calc-errors');

function Scatter3D (config) {

    this.config = config;

}

module.exports = Scatter3D;

var proto = Scatter3D.prototype;

proto.attributes = {
    x: {type: 'data_array'},
    y: {type: 'data_array'},
    z: {type: 'data_array'},
    text: {from: 'Scatter'},
    surfaceaxis: {
        type: 'enumerated',
        values: [-1,0,1,2],
        dflt: -1
    },
    surfacecolor: {
        type: 'color'
    },
    projection: {
        x: {
            show: {
                type: 'boolean',
                dflt: false
            },
            opacity: {
                type: 'number',
                min: 0,
                max: 1,
                dflt: 1
            },
            scale: {
                type: 'number',
                min: 0,
                max: 10,
                dflt: 2/3
            }
        },
        y: {
            show: {
                type: 'boolean',
                dflt: false
            },
            opacity: {
                type: 'number',
                min: 0,
                max: 1,
                dflt: 1
            },
            scale: {
                type: 'number',
                min: 0,
                max: 10,
                dflt: 2/3
            }
        },
        z: {
            show: {
                type: 'boolean',
                dflt: false
            },
            opacity: {
                type: 'number',
                min: 0,
                max: 1,
                dflt: 1
            },
            scale: {
                type: 'number',
                min: 0,
                max: 10,
                dflt: 2/3
            }
        }
    },
    mode: {from: 'Scatter'},
    line: {
        color: {from: 'Scatter'},
        width: {from: 'Scatter'},
        dash: {from: 'Scatter'}
    },
    marker: {
        symbol: {from: 'Scatter'},
        size: {from: 'Scatter'},
        opacity: {from: 'Scatter'},
        line: {
            color: {from: 'Scatter'},
            width: {from: 'Scatter'}
        }

    },
    textposition: {from: 'Scatter'},
    textfont: {from: 'Scatter'}
};

proto.supplyXYZ = function (traceIn, traceOut) {
    var _this = this;
    var Plotly = this.config.Plotly;

    function coerce(attr, dflt) {
        return Plotly.Lib.coerce(traceIn, traceOut, _this.attributes, attr, dflt);
    }

    var len = 0,
        x = coerce('x'),
        y = coerce('y'),
        z = coerce('z');

    if (x && y && z) {
        len = Math.min(x.length, y.length, z.length);
        if(len<x.length) traceOut.x = x.slice(0, len);
        if(len<y.length) traceOut.y = y.slice(0, len);
        if(len<z.length) traceOut.z = z.slice(0, len);
    }

    return len;
};

proto.supplyDefaults = function (traceIn, traceOut, defaultColor, layout) {
    var _this = this;
    var Plotly = this.config.Plotly;
    var Scatter = Plotly.Scatter;
    var linecolor, markercolor;

    function coerce(attr, dflt) {
        return Plotly.Lib.coerce(traceIn, traceOut, _this.attributes, attr, dflt);
    }

    function coerceScatter(attr, dflt) {
        return Plotly.Lib.coerce(traceIn, traceOut, Scatter.attributes, attr, dflt);
    }

    this.supplyXYZ(traceIn, traceOut);

    if (coerceScatter('text')) coerceScatter('mode', 'lines+markers+text');
    else coerceScatter('mode', 'lines+markers');

    if (Scatter.hasLines(traceOut)) {
        linecolor = coerceScatter('line.color', (traceIn.marker||{}).color || defaultColor);
        coerceScatter('line.width');
        coerceScatter('line.dash');
    }

    if (Scatter.hasMarkers(traceOut)) {
        markercolor = coerceScatter('marker.color', defaultColor);
        coerceScatter('marker.symbol');
        coerceScatter('marker.size', 8);
        coerceScatter('marker.opacity', 1);
        coerceScatter('marker.line.width', 0);
        // TODO parity with scatter.js
        coerceScatter('marker.line.color', 'rgb(0,0,0)');
    }

    if (Scatter.hasText(traceOut)) {
        coerceScatter('textposition', 'top center');
        coerceScatter('textfont', layout.font);
    }

    if (coerce('surfaceaxis') >= 0) coerce('surfacecolor', linecolor || markercolor);

    var dims = ['x','y','z'];
    for (var i = 0; i < 3; ++i) {
        var projection = 'projection.' + dims[i];
        if (coerce(projection+'.show')) {
            // adaptor until Mikola makes axes independent projection configs
            coerce('projection.x.opacity');
            coerce('projection.x.scale');
            //
            coerce(projection+'.opacity');
            coerce(projection+'.scale');
        }
    }

    Plotly.ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'z'});
    Plotly.ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y', inherit: 'z'});
    Plotly.ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'z'});

};

function isTrue (bool) {
    return bool;
}

function calculateErrorParams(errors) {
    /*jshint camelcase: false */
    var capSize = [0.0, 0.0, 0.0], i, e;
    var color = [[0,0,0],[0,0,0],[0,0,0]];
    var lineWidth = [0.0, 0.0, 0.0];
    for(i=0; i<3; ++i) {
        e = errors[i];
        if (e && e.copy_zstyle !== false) {
            e = errors[2];
        }
        if(!e) continue;
        capSize[i] = e.width / 100.0;  //Ballpark rescaling, attempt to make consistent with plot.ly
        color[i] = str2RgbaArray(e.color);
        lineWidth = e.thickness;

    }
    return {capSize: capSize, color: color, lineWidth: lineWidth};
}

function calculateTextOffset(textposition) {
    //Read out text properties
    var textOffset = [0,0];
    if (textposition.indexOf('bottom') >= 0) {
        textOffset[1] += 1;
    }
    if (textposition.indexOf('top') >= 0) {
        textOffset[1] -= 1;
    }
    if (textposition.indexOf('left') >= 0) {
        textOffset[0] -= 1;
    }
    if (textposition.indexOf('right') >= 0) {
        textOffset[0] += 1;
    }
    return textOffset;
}

function str2RgbaArray(color) {
    color = tinycolor(color);
    return arrtools.str2RgbaArray(color.toRgbString());
}

function colorFormatter(colorIn, opacityIn) {
    var colorOut = null,
        colorLength = colorIn.length
    if (Array.isArray(colorIn)) {
        colorOut = [];
        for (var i = 0; i < colorLength; ++i) {
            colorOut[i]     = str2RgbaArray(colorIn[i]);
            colorOut[i][3] *= opacityIn;
        }
    } else {
            colorOut     = str2RgbaArray(colorIn);
            colorOut[3] *= opacityIn;
    }
    return colorOut
}

proto.update = function update (scene, sceneLayout, data, scatter) {
    /*jshint camelcase: false */
    // handle visible trace cases

    var params, i,
        points = [],
        xaxis = sceneLayout.xaxis,
        yaxis = sceneLayout.yaxis,
        zaxis = sceneLayout.zaxis,
        errorParams = calculateErrorParams([ data.error_x, data.error_y, data.error_z ]),
        xc, x = data.x,
        yc, y = data.y,
        zc, z = data.z,
        len = x.length;

    //Convert points
    for (i = 0; i < len; i++) {
        // sanitize numbers and apply transforms based on axes.type
        xc = xaxis.d2l(x[i]);
        yc = yaxis.d2l(y[i]);
        zc = zaxis.d2l(z[i]);

        points[i] = [xc, yc, zc];
    }

    //Build object parameters
    params = {
        position: points,
        mode:     data.mode
    };

    if ('line' in data) {
        params.lineColor     = str2RgbaArray(data.line.color);
        params.lineWidth     = data.line.width;
        params.lineDashes    = data.line.dash;
    }

    if ('marker' in data) {
        params.scatterColor         = colorFormatter(data.marker.color, data.marker.opacity);
        params.scatterSize          = 2*data.marker.size;  // rough parity with Plotly 2D markers
        params.scatterMarker        = this.markerSymbols[data.marker.symbol];
        params.scatterLineWidth     = data.marker.line.width;
        params.scatterLineColor     = str2RgbaArray(data.marker.line.color);
        params.scatterLineColor[3] *= data.marker.opacity;
        params.scatterAngle         = 0;
    }

    if ('textposition' in data) {
        params.text           = data.text;
        params.textOffset     = calculateTextOffset(data.textposition);
        params.textColor      = str2RgbaArray(data.textfont.color);
        params.textSize       = data.textfont.size;
        params.textFont       = data.textfont.family;
        params.textAngle      = 0;
    }

    var dims = ['x', 'y', 'z'];
    params.project = [];
    for (i = 0; i < 3; ++i) {
        var projection = data.projection[dims[i]];
        if ((params.project[i] = projection.show)) {
            // Mikolas API doesn't current support axes dependent
            // configuration. Its coming though.
            params.projectOpacity = data.projection.x.opacity;
            params.projectScale = data.projection.x.scale;
        }
    }

    params.errorBounds    = calculateError(data);
    params.errorColor     = errorParams.color;
    params.errorLineWidth = errorParams.lineWidth;
    params.errorCapSize   = errorParams.capSize;

    params.delaunayAxis       = data.surfaceaxis;
    params.delaunayColor      = str2RgbaArray(data.surfacecolor);

    if (scatter) scatter.update(params);
    else {
        var pickIds = scene.allocIds(4);

        params.pickId0   = pickIds.ids[0];
        params.pickId1   = pickIds.ids[1];
        params.pickId2   = pickIds.ids[2];
        params.pickId3   = pickIds.ids[3];
        scatter          = createScatterLine(scene.shell.gl, params);
        scatter.groupId  = pickIds.group;
        scatter.plotlyType  = data.type;
    }

    scatter.uid = data.uid;

    return scatter;
};

proto.markerSymbols = {
    'circle': '●',
    'circle-open': '○',
    'square': '■',
    'square-open': '□',
    'diamond': '◆',
    'diamond-open': '◇',
    'cross': '+',
    'x': '❌'
};
<commit_msg>add missing ;<commit_after>'use strict';

var createScatterLine = require('./line-with-markers'),
    tinycolor = require('tinycolor2'),
    arrtools = require('arraytools'),
    calculateError = require('./calc-errors');

function Scatter3D (config) {

    this.config = config;

}

module.exports = Scatter3D;

var proto = Scatter3D.prototype;

proto.attributes = {
    x: {type: 'data_array'},
    y: {type: 'data_array'},
    z: {type: 'data_array'},
    text: {from: 'Scatter'},
    surfaceaxis: {
        type: 'enumerated',
        values: [-1,0,1,2],
        dflt: -1
    },
    surfacecolor: {
        type: 'color'
    },
    projection: {
        x: {
            show: {
                type: 'boolean',
                dflt: false
            },
            opacity: {
                type: 'number',
                min: 0,
                max: 1,
                dflt: 1
            },
            scale: {
                type: 'number',
                min: 0,
                max: 10,
                dflt: 2/3
            }
        },
        y: {
            show: {
                type: 'boolean',
                dflt: false
            },
            opacity: {
                type: 'number',
                min: 0,
                max: 1,
                dflt: 1
            },
            scale: {
                type: 'number',
                min: 0,
                max: 10,
                dflt: 2/3
            }
        },
        z: {
            show: {
                type: 'boolean',
                dflt: false
            },
            opacity: {
                type: 'number',
                min: 0,
                max: 1,
                dflt: 1
            },
            scale: {
                type: 'number',
                min: 0,
                max: 10,
                dflt: 2/3
            }
        }
    },
    mode: {from: 'Scatter'},
    line: {
        color: {from: 'Scatter'},
        width: {from: 'Scatter'},
        dash: {from: 'Scatter'}
    },
    marker: {
        symbol: {from: 'Scatter'},
        size: {from: 'Scatter'},
        opacity: {from: 'Scatter'},
        line: {
            color: {from: 'Scatter'},
            width: {from: 'Scatter'}
        }

    },
    textposition: {from: 'Scatter'},
    textfont: {from: 'Scatter'}
};

proto.supplyXYZ = function (traceIn, traceOut) {
    var _this = this;
    var Plotly = this.config.Plotly;

    function coerce(attr, dflt) {
        return Plotly.Lib.coerce(traceIn, traceOut, _this.attributes, attr, dflt);
    }

    var len = 0,
        x = coerce('x'),
        y = coerce('y'),
        z = coerce('z');

    if (x && y && z) {
        len = Math.min(x.length, y.length, z.length);
        if(len<x.length) traceOut.x = x.slice(0, len);
        if(len<y.length) traceOut.y = y.slice(0, len);
        if(len<z.length) traceOut.z = z.slice(0, len);
    }

    return len;
};

proto.supplyDefaults = function (traceIn, traceOut, defaultColor, layout) {
    var _this = this;
    var Plotly = this.config.Plotly;
    var Scatter = Plotly.Scatter;
    var linecolor, markercolor;

    function coerce(attr, dflt) {
        return Plotly.Lib.coerce(traceIn, traceOut, _this.attributes, attr, dflt);
    }

    function coerceScatter(attr, dflt) {
        return Plotly.Lib.coerce(traceIn, traceOut, Scatter.attributes, attr, dflt);
    }

    this.supplyXYZ(traceIn, traceOut);

    if (coerceScatter('text')) coerceScatter('mode', 'lines+markers+text');
    else coerceScatter('mode', 'lines+markers');

    if (Scatter.hasLines(traceOut)) {
        linecolor = coerceScatter('line.color', (traceIn.marker||{}).color || defaultColor);
        coerceScatter('line.width');
        coerceScatter('line.dash');
    }

    if (Scatter.hasMarkers(traceOut)) {
        markercolor = coerceScatter('marker.color', defaultColor);
        coerceScatter('marker.symbol');
        coerceScatter('marker.size', 8);
        coerceScatter('marker.opacity', 1);
        coerceScatter('marker.line.width', 0);
        // TODO parity with scatter.js
        coerceScatter('marker.line.color', 'rgb(0,0,0)');
    }

    if (Scatter.hasText(traceOut)) {
        coerceScatter('textposition', 'top center');
        coerceScatter('textfont', layout.font);
    }

    if (coerce('surfaceaxis') >= 0) coerce('surfacecolor', linecolor || markercolor);

    var dims = ['x','y','z'];
    for (var i = 0; i < 3; ++i) {
        var projection = 'projection.' + dims[i];
        if (coerce(projection+'.show')) {
            // adaptor until Mikola makes axes independent projection configs
            coerce('projection.x.opacity');
            coerce('projection.x.scale');
            //
            coerce(projection+'.opacity');
            coerce(projection+'.scale');
        }
    }

    Plotly.ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'z'});
    Plotly.ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y', inherit: 'z'});
    Plotly.ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'z'});

};

function isTrue (bool) {
    return bool;
}

function calculateErrorParams(errors) {
    /*jshint camelcase: false */
    var capSize = [0.0, 0.0, 0.0], i, e;
    var color = [[0,0,0],[0,0,0],[0,0,0]];
    var lineWidth = [0.0, 0.0, 0.0];
    for(i=0; i<3; ++i) {
        e = errors[i];
        if (e && e.copy_zstyle !== false) {
            e = errors[2];
        }
        if(!e) continue;
        capSize[i] = e.width / 100.0;  //Ballpark rescaling, attempt to make consistent with plot.ly
        color[i] = str2RgbaArray(e.color);
        lineWidth = e.thickness;

    }
    return {capSize: capSize, color: color, lineWidth: lineWidth};
}

function calculateTextOffset(textposition) {
    //Read out text properties
    var textOffset = [0,0];
    if (textposition.indexOf('bottom') >= 0) {
        textOffset[1] += 1;
    }
    if (textposition.indexOf('top') >= 0) {
        textOffset[1] -= 1;
    }
    if (textposition.indexOf('left') >= 0) {
        textOffset[0] -= 1;
    }
    if (textposition.indexOf('right') >= 0) {
        textOffset[0] += 1;
    }
    return textOffset;
}

function str2RgbaArray(color) {
    color = tinycolor(color);
    return arrtools.str2RgbaArray(color.toRgbString());
}

function colorFormatter(colorIn, opacityIn) {
    var colorOut = null,
        colorLength = colorIn.length;
    if (Array.isArray(colorIn)) {
        colorOut = [];
        for (var i = 0; i < colorLength; ++i) {
            colorOut[i]     = str2RgbaArray(colorIn[i]);
            colorOut[i][3] *= opacityIn;
        }
    } else {
            colorOut     = str2RgbaArray(colorIn);
            colorOut[3] *= opacityIn;
    }
    return colorOut;
}

proto.update = function update (scene, sceneLayout, data, scatter) {
    /*jshint camelcase: false */
    // handle visible trace cases

    var params, i,
        points = [],
        xaxis = sceneLayout.xaxis,
        yaxis = sceneLayout.yaxis,
        zaxis = sceneLayout.zaxis,
        errorParams = calculateErrorParams([ data.error_x, data.error_y, data.error_z ]),
        xc, x = data.x,
        yc, y = data.y,
        zc, z = data.z,
        len = x.length;

    //Convert points
    for (i = 0; i < len; i++) {
        // sanitize numbers and apply transforms based on axes.type
        xc = xaxis.d2l(x[i]);
        yc = yaxis.d2l(y[i]);
        zc = zaxis.d2l(z[i]);

        points[i] = [xc, yc, zc];
    }

    //Build object parameters
    params = {
        position: points,
        mode:     data.mode
    };

    if ('line' in data) {
        params.lineColor     = str2RgbaArray(data.line.color);
        params.lineWidth     = data.line.width;
        params.lineDashes    = data.line.dash;
    }

    if ('marker' in data) {
        params.scatterColor         = colorFormatter(data.marker.color, data.marker.opacity);
        params.scatterSize          = 2*data.marker.size;  // rough parity with Plotly 2D markers
        params.scatterMarker        = this.markerSymbols[data.marker.symbol];
        params.scatterLineWidth     = data.marker.line.width;
        params.scatterLineColor     = str2RgbaArray(data.marker.line.color);
        params.scatterLineColor[3] *= data.marker.opacity;
        params.scatterAngle         = 0;
    }

    if ('textposition' in data) {
        params.text           = data.text;
        params.textOffset     = calculateTextOffset(data.textposition);
        params.textColor      = str2RgbaArray(data.textfont.color);
        params.textSize       = data.textfont.size;
        params.textFont       = data.textfont.family;
        params.textAngle      = 0;
    }

    var dims = ['x', 'y', 'z'];
    params.project = [];
    for (i = 0; i < 3; ++i) {
        var projection = data.projection[dims[i]];
        if ((params.project[i] = projection.show)) {
            // Mikolas API doesn't current support axes dependent
            // configuration. Its coming though.
            params.projectOpacity = data.projection.x.opacity;
            params.projectScale = data.projection.x.scale;
        }
    }

    params.errorBounds    = calculateError(data);
    params.errorColor     = errorParams.color;
    params.errorLineWidth = errorParams.lineWidth;
    params.errorCapSize   = errorParams.capSize;

    params.delaunayAxis       = data.surfaceaxis;
    params.delaunayColor      = str2RgbaArray(data.surfacecolor);

    if (scatter) scatter.update(params);
    else {
        var pickIds = scene.allocIds(4);

        params.pickId0   = pickIds.ids[0];
        params.pickId1   = pickIds.ids[1];
        params.pickId2   = pickIds.ids[2];
        params.pickId3   = pickIds.ids[3];
        scatter          = createScatterLine(scene.shell.gl, params);
        scatter.groupId  = pickIds.group;
        scatter.plotlyType  = data.type;
    }

    scatter.uid = data.uid;

    return scatter;
};

proto.markerSymbols = {
    'circle': '●',
    'circle-open': '○',
    'square': '■',
    'square-open': '□',
    'diamond': '◆',
    'diamond-open': '◇',
    'cross': '+',
    'x': '❌'
};
<|endoftext|>


<commit_before>var cliff = require('cliff'),
    benchmark = require('benchmark'),
    fs = require('fs'),
    data = require('./data'),
    Vow = require('..'),
    Q = require('q'),
    When = require('when'),
    Bluebird = require('bluebird'),
    Pinkie = require('pinkie-promise'),
    tests = {
        'Q' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var defer = Q.defer();
                Q.all(data[key].map(function(val) {
                        var defer = Q.defer();
                        toResolve.push({ defer : defer, val : val });
                        return defer.promise;
                    }))
                    .then(function(val) {
                        defer.resolve(val);
                    });
                topPromises.push(defer.promise);
            });

            Q.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.defer.resolve(obj.val);
            });
        },

        'When' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var defer = When.defer();
                When.all(data[key].map(function(val) {
                        var defer = When.defer();
                        toResolve.push({ defer : defer, val : val });
                        return defer.promise;
                    }))
                    .then(function(val) {
                        defer.resolve(val);
                    });
                topPromises.push(defer.promise);
            });

            When.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.defer.resolve(obj.val);
            });
        },

        'Bluebird' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var resolve,
                    promise = new Bluebird(function(_resolve) {
                        resolve = _resolve;
                    });
                Bluebird.all(data[key].map(function(val) {
                        return new Bluebird(function(_resolve) {
                            toResolve.push({ resolve : _resolve, val : val });
                        });
                    }))
                    .then(function(val) {
                        resolve(val);
                    });
                topPromises.push(promise);
            });

            Bluebird.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.resolve(obj.val);
            });
        },

        'Pinkie' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var resolve,
                    promise = new Pinkie(function(_resolve) {
                        resolve = _resolve;
                    });
                Pinkie.all(data[key].map(function(val) {
                        return new Pinkie(function(_resolve) {
                            toResolve.push({ resolve : _resolve, val : val });
                        });
                    }))
                    .then(function(val) {
                        resolve(val);
                    });
                topPromises.push(promise);
            });

            Pinkie.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.resolve(obj.val);
            });
        },

        'ES2015 Promise' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var resolve,
                    promise = new Promise(function(_resolve) {
                        resolve = _resolve;
                    });
                Promise.all(data[key].map(function(val) {
                        return new Promise(function(_resolve) {
                            toResolve.push({ resolve : _resolve, val : val });
                        });
                    }))
                    .then(function(val) {
                        resolve(val);
                    });
                topPromises.push(promise);
            });

            Promise.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.resolve(obj.val);
            });
        },

        'Vow' : function(deferred) {
            var toResolve = [],
                topPromises = Object.keys(data).map(function(key) {
                    var defer = Vow.defer();
                    Vow.all(data[key].map(function(val) {
                            var resolver = Vow.defer();
                            toResolve.push({ defer : defer, val : val });
                            return resolver.promise();
                        }))
                        .then(function(val) {
                            defer.resolve(val);
                        });
                    return defer.promise();
                });

            Vow.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.defer.resolve(obj.val);
            });
        }
    },
    results = [],
    onTestCompleted = function(name) {
        results.push({
            ''          : name,
            'mean time' : (this.stats.mean * 1000).toFixed(3) + 'ms',
            'ops/sec'   : (1 / this.stats.mean).toFixed(0)
        });
    };

var suite = new benchmark.Suite(
    'comparison',
    {
        onStart : function() {
            console.log('Starts\n');
        },

        onComplete : function() {
            console.log(cliff.stringifyObjectRows(
                results,
                ['', 'mean time', 'ops/sec'],
                ['red', 'green', 'blue']) + '\n');
            results = [];
        }
    });

Object.keys(tests).forEach(function(name) {
    var i = 0;
    suite.add(
        name,
        function(deferred) {
            tests[name](deferred);
        },
        {
            defer      : true,
            onStart    : function() {
                //console.log(name  + ' \n');
            },
            onCycle    : function() {
                console.log('\033[1A' + new Array(i++).join('.'));
            },
            onComplete : function() {
                console.log('');
                onTestCompleted.call(this, name);
            }
        });
});

suite.run();
<commit_msg>Fix comparison<commit_after>var cliff = require('cliff'),
    benchmark = require('benchmark'),
    fs = require('fs'),
    data = require('./data'),
    Vow = require('..'),
    Q = require('q'),
    When = require('when'),
    Bluebird = require('bluebird'),
    Pinkie = require('pinkie-promise'),
    tests = {
        'Q' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var defer = Q.defer();
                Q.all(data[key].map(function(val) {
                        var defer = Q.defer();
                        toResolve.push({ defer : defer, val : val });
                        return defer.promise;
                    }))
                    .then(function(val) {
                        defer.resolve(val);
                    });
                topPromises.push(defer.promise);
            });

            Q.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.defer.resolve(obj.val);
            });
        },

        'When' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var defer = When.defer();
                When.all(data[key].map(function(val) {
                        var defer = When.defer();
                        toResolve.push({ defer : defer, val : val });
                        return defer.promise;
                    }))
                    .then(function(val) {
                        defer.resolve(val);
                    });
                topPromises.push(defer.promise);
            });

            When.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.defer.resolve(obj.val);
            });
        },

        'Bluebird' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var resolve,
                    promise = new Bluebird(function(_resolve) {
                        resolve = _resolve;
                    });
                Bluebird.all(data[key].map(function(val) {
                        return new Bluebird(function(_resolve) {
                            toResolve.push({ resolve : _resolve, val : val });
                        });
                    }))
                    .then(function(val) {
                        resolve(val);
                    });
                topPromises.push(promise);
            });

            Bluebird.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.resolve(obj.val);
            });
        },

        'Pinkie' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var resolve,
                    promise = new Pinkie(function(_resolve) {
                        resolve = _resolve;
                    });
                Pinkie.all(data[key].map(function(val) {
                        return new Pinkie(function(_resolve) {
                            toResolve.push({ resolve : _resolve, val : val });
                        });
                    }))
                    .then(function(val) {
                        resolve(val);
                    });
                topPromises.push(promise);
            });

            Pinkie.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.resolve(obj.val);
            });
        },

        'ES2015 Promise' : function(deferred) {
            var toResolve = [],
                topPromises = [];

            Object.keys(data).forEach(function(key) {
                var resolve,
                    promise = new Promise(function(_resolve) {
                        resolve = _resolve;
                    });
                Promise.all(data[key].map(function(val) {
                        return new Promise(function(_resolve) {
                            toResolve.push({ resolve : _resolve, val : val });
                        });
                    }))
                    .then(function(val) {
                        resolve(val);
                    });
                topPromises.push(promise);
            });

            Promise.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.resolve(obj.val);
            });
        },

        'Vow' : function(deferred) {
            var toResolve = [],
                topPromises = Object.keys(data).map(function(key) {
                    var defer = Vow.defer();
                    Vow.all(data[key].map(function(val) {
                            var defer = Vow.defer();
                            toResolve.push({ defer : defer, val : val });
                            return defer.promise();
                        }))
                        .then(function(val) {
                            defer.resolve(val);
                        });
                    return defer.promise();
                });

            Vow.all(topPromises).then(function() {
                deferred.resolve();
            });

            toResolve.forEach(function(obj) {
                obj.defer.resolve(obj.val);
            });
        }
    },
    results = [],
    onTestCompleted = function(name) {
        results.push({
            ''          : name,
            'mean time' : (this.stats.mean * 1000).toFixed(3) + 'ms',
            'ops/sec'   : (1 / this.stats.mean).toFixed(0)
        });
    };

var suite = new benchmark.Suite(
    'comparison',
    {
        onStart : function() {
            console.log('Starts\n');
        },

        onComplete : function() {
            console.log(cliff.stringifyObjectRows(
                results,
                ['', 'mean time', 'ops/sec'],
                ['red', 'green', 'blue']) + '\n');
            results = [];
        }
    });

Object.keys(tests).forEach(function(name) {
    var i = 0;
    suite.add(
        name,
        function(deferred) {
            tests[name](deferred);
        },
        {
            defer      : true,
            onStart    : function() {
                //console.log(name  + ' \n');
            },
            onCycle    : function() {
                console.log('\033[1A' + new Array(i++).join('.'));
            },
            onComplete : function() {
                console.log('');
                onTestCompleted.call(this, name);
            }
        });
});

suite.run();
<|endoftext|>


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

/**
 * dimensions and resolution
 *
 * Checks dimensions and resolution for x, y, z, and time across subjects to
 * ensure they are consistent.
 *
 * The fields we are interested in are all arrays and we are only looking at
 * the first for values in those arrays. To handle single values or longer
 * arrays more arguments will need to be added to headerField.
 */

var headerFields = function headerFields(headers) {
    var issues = [];
    issues = issues.concat(headerField(headers, 'dim'));
    issues = issues.concat(headerField(headers, 'pixdim'));
    return issues;
}

/**
 * Key to headerField working is the fact that we take and array of values
 * from the nifti header and convert it to a string. This string is used to
 * comapre the header field value against other header field values and is used
 * as an attribute in the object nifti_types. Nifti types refers to the
 * different types of nifti files we are comparing across subjects. Only the
 * dimensionality of similar anatomy/funtional/dwi headers are being compared.
 */

var headerField = function headerField(headers, field) {
    var nifti_types = {};
    var issues = [];
    for (var header_index in headers) {
        var field_value;
        var file = headers[header_index][0];
        var filename;
        var header = headers[header_index][1];
        var match;
        var path = utils.files.relativePath(file);
        var run;
        var subject;

        if (field === 'dim') {
            field_value = header[field].slice(1, header[field][0]+1).toString();
        } else if (field === 'pixdim') {
            field_value = [];
            var pix_dim = header[field].slice(0,4);
            var units = header['xyzt_units'].slice(0,4);
            for (var i = 0; i < 4; i++) {
                field_value.push('' + pix_dim[i] + units[i]);
            }
            field_value = field_value.toString();
        }

        if (!file || (typeof window != 'undefined' && !file.webkitRelativePath)) {
            continue;
        }

        if (!utils.type.isBIDS(path)) {
            continue;
        }
        //match the subject identifier up to the '/' in the full path to a file.
        match = path.match(/sub-(.*?)(?=\/)/);
        if (match === null) {
            continue;
        } else {
            subject = match[0];
        }
        // files are prepended with subject name, the following two commands
        // remove the subject from the file name to allow filenames to be more
        // easily compared
        filename = path.substring(path.match(subject).index + subject.length);
        filename = filename.replace(subject, '<sub>');

        // generalize the run number so we can compare counts across all runs
        match = filename.match(/run-\d+/);
        if (match === null) {
            continue;
        } else {
            run = match[0];
        }

        filename = filename.substring(filename.match(run).index + run.length);
        filename = filename.replace(run, '<run>');

        if (!nifti_types.hasOwnProperty(filename)) {
            nifti_types[filename] = {}
            nifti_types[filename][field_value] = {'count': 1, 'files': [file]};
        } else {
            if (!nifti_types[filename].hasOwnProperty(field_value)) {
                nifti_types[filename][field_value] = {'count': 1, 'files': [file]};
            } else {
                nifti_types[filename][field_value].count += 1;
                nifti_types[filename][field_value].files.push(file);
            }
        }
    }
    for (var nifti_key in nifti_types) {
        nifti_type = nifti_types[nifti_key];
        var max_field_value = Object.keys(nifti_type)[0];
        for (var field_value_key in nifti_type) {
            var field_value = nifti_type[field_value_key];
            if (field_value.count > nifti_type[max_field_value].count) {
                max_field_value = field_value_key;
            }
        }
        for (var field_value_key in nifti_type) {
            var field_value = nifti_type[field_value_key];
            if (max_field_value !== field_value_key && headerFieldCompare(max_field_value, field_value_key)) {
                for (var nifti_file_index in field_value.files) {
                    var nifti_file = field_value.files[nifti_file_index];
                    var evidence;
                    if (field === 'dim') {
                        evidence = "For the field " + field +
                                  " The most common values is: " +
                                  max_field_value + "(voxels), This file has the value: " +
                                  field_value_key + "(voxels)"
                    } else if (field === 'pixdim') {
                        evidence = "For the field " + field +
                                  " The most common values is: " +
                                  max_field_value.replace(/,/g, ' x ') + ", This file has the value: " +
                                  field_value_key.replace(/,/g, ' x ')
                    }
                    issues.push(new utils.Issue({
                        file: nifti_file,
                        evidence: evidence,
                        code: 39
                    }));
                }
            }
        }
    }
    return issues;
};

/**
 * if elements of the two arrays differ by less than one we won't raise a
 * warning about them. There are a large number of floating point rounding
 * errors that cause resolutions to be slightly different. Returns true if
 * the two headers are signifigantly different
 */
function headerFieldCompare(header1, header2) {
    hdr1 = header1.split(',');
    hdr2 = header2.split(',');
    for (var i = 0; i < hdr1.length; i++) {
        var hdr1_val = Number(hdr1[i].match(/-?\d*\.?\d*/));
        var hdr2_val  = Number(hdr2[i].match(/-?\d*\.?\d*/));
        // Matching alphas with * will return '' on headers without units
        var hdr1_unit = hdr1[i].match(/[A-Za-z]*$/)[0];
        var hdr2_unit = hdr2[i].match(/[A-Za-z]*$/)[0];
        if (Math.abs(hdr1_val - hdr2_val) > .00001) {
            return true;
        }
        if (hdr1_unit !== hdr2_unit) {
            return true;
        }
    }
    return false;
}

module.exports = headerFields;

<commit_msg>Added check to see if function is being called with an unsupported header field<commit_after>var utils  = require('../utils');

/**
 * dimensions and resolution
 *
 * Checks dimensions and resolution for x, y, z, and time across subjects to
 * ensure they are consistent.
 *
 * The fields we are interested in are all arrays and we are only looking at
 * the first for values in those arrays. To handle single values or longer
 * arrays more arguments will need to be added to headerField.
 */

var headerFields = function headerFields(headers) {
    var issues = [];
    issues = issues.concat(headerField(headers, 'dim'));
    issues = issues.concat(headerField(headers, 'pixdim'));
    return issues;
}

/**
 * Key to headerField working is the fact that we take and array of values
 * from the nifti header and convert it to a string. This string is used to
 * comapre the header field value against other header field values and is used
 * as an attribute in the object nifti_types. Nifti types refers to the
 * different types of nifti files we are comparing across subjects. Only the
 * dimensionality of similar anatomy/funtional/dwi headers are being compared.
 */

var headerField = function headerField(headers, field) {
    var nifti_types = {};
    var issues = [];
    for (var header_index in headers) {
        var field_value;
        var file = headers[header_index][0];
        var filename;
        var header = headers[header_index][1];
        var match;
        var path = utils.files.relativePath(file);
        var run;
        var subject;

        if (field === 'dim') {
            field_value = header[field].slice(1, header[field][0]+1).toString();
        } else if (field === 'pixdim') {
            field_value = [];
            var pix_dim = header[field].slice(0,4);
            var units = header['xyzt_units'].slice(0,4);
            for (var i = 0; i < 4; i++) {
                field_value.push('' + pix_dim[i] + units[i]);
            }
            field_value = field_value.toString();
        } else {
            console.log("Checks against header field: " + field + " are currently unsupported.")
            return;
        }

        if (!file || (typeof window != 'undefined' && !file.webkitRelativePath)) {
            continue;
        }

        if (!utils.type.isBIDS(path)) {
            continue;
        }
        //match the subject identifier up to the '/' in the full path to a file.
        match = path.match(/sub-(.*?)(?=\/)/);
        if (match === null) {
            continue;
        } else {
            subject = match[0];
        }
        // files are prepended with subject name, the following two commands
        // remove the subject from the file name to allow filenames to be more
        // easily compared
        filename = path.substring(path.match(subject).index + subject.length);
        filename = filename.replace(subject, '<sub>');

        // generalize the run number so we can compare counts across all runs
        match = filename.match(/run-\d+/);
        if (match === null) {
            continue;
        } else {
            run = match[0];
        }

        filename = filename.substring(filename.match(run).index + run.length);
        filename = filename.replace(run, '<run>');

        if (!nifti_types.hasOwnProperty(filename)) {
            nifti_types[filename] = {}
            nifti_types[filename][field_value] = {'count': 1, 'files': [file]};
        } else {
            if (!nifti_types[filename].hasOwnProperty(field_value)) {
                nifti_types[filename][field_value] = {'count': 1, 'files': [file]};
            } else {
                nifti_types[filename][field_value].count += 1;
                nifti_types[filename][field_value].files.push(file);
            }
        }
    }
    for (var nifti_key in nifti_types) {
        nifti_type = nifti_types[nifti_key];
        var max_field_value = Object.keys(nifti_type)[0];
        for (var field_value_key in nifti_type) {
            var field_value = nifti_type[field_value_key];
            if (field_value.count > nifti_type[max_field_value].count) {
                max_field_value = field_value_key;
            }
        }
        for (var field_value_key in nifti_type) {
            var field_value = nifti_type[field_value_key];
            if (max_field_value !== field_value_key && headerFieldCompare(max_field_value, field_value_key)) {
                for (var nifti_file_index in field_value.files) {
                    var nifti_file = field_value.files[nifti_file_index];
                    var evidence;
                    if (field === 'dim') {
                        evidence = "For the field " + field +
                                  " The most common values is: " +
                                  max_field_value + "(voxels), This file has the value: " +
                                  field_value_key + "(voxels)"
                    } else if (field === 'pixdim') {
                        evidence = "For the field " + field +
                                  " The most common values is: " +
                                  max_field_value.replace(/,/g, ' x ') + ", This file has the value: " +
                                  field_value_key.replace(/,/g, ' x ')
                    }
                        issues.push(new utils.Issue({
                        file: nifti_file,
                        evidence: evidence,
                        code: 39
                    }));
                }
            }
        }
    }
    return issues;
};

/**
 * if elements of the two arrays differ by less than one we won't raise a
 * warning about them. There are a large number of floating point rounding
 * errors that cause resolutions to be slightly different. Returns true if
 * the two headers are signifigantly different
 */
function headerFieldCompare(header1, header2) {
    hdr1 = header1.split(',');
    hdr2 = header2.split(',');
    for (var i = 0; i < hdr1.length; i++) {
        var hdr1_val = Number(hdr1[i].match(/-?\d*\.?\d*/));
        var hdr2_val  = Number(hdr2[i].match(/-?\d*\.?\d*/));
        // Matching alphas with * will return '' on headers without units
        var hdr1_unit = hdr1[i].match(/[A-Za-z]*$/)[0];
        var hdr2_unit = hdr2[i].match(/[A-Za-z]*$/)[0];
        if (Math.abs(hdr1_val - hdr2_val) > .00001) {
            return true;
        }
        if (hdr1_unit !== hdr2_unit) {
            return true;
        }
    }
    return false;
}

module.exports = headerFields;

<|endoftext|>


<commit_before>// Automatic Dictionary Switcher Add-on for Firefox
// Copyright 2015 Daniel Naber
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

const minimumCharacterLength = 25,
      // The regex is obtained from http://www.regexlib.com/REDetails.aspx?regexp_id=146
      filterRegEx = /(http|https|ftp)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?\/?([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*/gi;

let userPreferences,
    // input element which the addon operates on currently (e.g. textarea):
    ignoreSignature;

// Listen to a message from the main script containing user preferences
self.port.on("config", function (prefs) {
    // We require the presence of user preferences just to know the languages for which the user chose "Don't
    // detect this language" option so that we disable spell checking if we encountered text in these languages
    userPreferences = prefs;
    ignoreSignature = userPreferences["ignoreSignature"];
});

function detectAndSetLanguage(targetElement, text) {
    // If spell checking was disabled by the page developer, we honor the setting and don't set the value of
    // the spellcheck attribute to true, but we still try to detect the language and keep the ordinary operation
    // of the addon because the user can manually enable spell checking from the context menu, and the spellcheck
    // attribute stays false.
    // I set the developerDisabledSpellChecking to indicate this case
    // (see how the firefoxDictSwitcherDisabledSpellCheck flag is set later)
    const developerDisabledSpellChecking = targetElement.spellcheck === false &&
                                          !targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck;

    // text is an optional parameter used when handling the paste event only. Otherwise, we read the text from
    // the element itself
    // Also, per issue #15, we remove URLs from the text
    text = (text || targetElement.value || targetElement.textContent).replace(filterRegEx, "");

    if(ignoreSignature) {
        let signatureDelimiterPos = text.indexOf("-- \n");
        if(signatureDelimiterPos >= 0) {
            // cut off signature: it may be written in a different language than
            // the main text and would thus decrease language detection quality:
            text = text.substring(0, signatureDelimiterPos);
        }
    }

    // we are only going to check language if there is some amount of text available as
    // that will increase our chances of detecting language correctly.
    if(text.length >= minimumCharacterLength) {
        //const startTime = performance.now();
        // Looks like we have enough text to reliably detect the language.
        let detectableLanguages = getDetectableLanguages();
        var language;
        try {
            language = franc(text,
                {
                    // top 20 + user settings:
                    'whitelist': detectableLanguages
                });
        } catch (e) {
            self.port.emit("showFeedbackInToolbar", "?", "Error: " + e.toString(), false);
            return;
        }

        var shortCode;
        var langName;
        if(language === "und") {  // 'unknown'
            self.port.emit("showFeedbackInToolbar", "?", "Could not detect language", false);
            return;
        } else {
            var languageInfo = iso6393[language];
            if(!languageInfo) {
                self.port.emit("showFeedbackInToolbar", "?", "Could not find language for code " + language, false);
                return;
            }
            shortCode = languageInfo.iso6391;
            langName = languageInfo.name;
        }
        //console.log(`Detected language code is (${language}) in ${performance.now() - startTime} ms`);

        // If the language was detected successfully enable spell checking and send its code to the main script
        if(!developerDisabledSpellChecking) {
            // We enable spell checking only if the attribute wasn't already set to false by the page developer
            targetElement.spellcheck = true;
        }
        self.port.emit("changeDictionary", shortCode, langName);

    } else {
        // Because we can't detect the language, disable spell checking
        targetElement.spellcheck = false;

        // And set a flag to indicate that it's our code who disabled spell checking not the page developer's
        // We do so only if the attribute wasn't already set to false be the page developer
        if(!developerDisabledSpellChecking) {
            targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck = "1";
        }

        // Sending null to the main script also indicates that the input text is too short to detect the language
        self.port.emit("showFeedbackInToolbar", "...", "Need at least " + minimumCharacterLength + " characters to detect language", false);
    }
}

function getDetectableLanguages() {
    let detectableLanguages = ['eng', 'spa', 'por', 'deu'];  // the four languages with variants that have a drop-down in our settings
    addAdditionalLanguages(detectableLanguages);
    removeDisabledLanguages(detectableLanguages);
    return detectableLanguages;
}

function addAdditionalLanguages(detectableLanguages) {
    for (var i = 1; i <= 3; i++) {
        let additionalLanguage = userPreferences["additionalLanguage"+i];
        if (additionalLanguage && additionalLanguage !== '-') {
            detectableLanguages.push(additionalLanguage);
        }
    }
}

function removeDisabledLanguages(detectableLanguages)
{
    const potentiallyDisabledLanguages = [
        { short: 'en', long: 'eng' },
        { short: 'es', long: 'spa' },
        { short: 'de', long: 'deu' },
        { short: 'pt', long: 'por' } ];  // these have variants and a "Don't detect this language" option in the settings
    for (var idx in potentiallyDisabledLanguages) {
        let shortCode = potentiallyDisabledLanguages[idx].short;
        if (userPreferences[shortCode] === '-') {
            let longCode = potentiallyDisabledLanguages[idx].long;
            let pos = detectableLanguages.indexOf(longCode);
            if (pos > -1) {
                detectableLanguages.splice(pos, 1);
            }
        }
    }
}

function isEligible(element) {
    // The addon operates only on textarea elements or elements with the contentEditable attribute set.
    // If the body itself is contentEditable we have to give up, as that would mean we put our feedback
    // div in the editable area and that causes a mess (happened on languagetool.org, where the text
    // area is a contentEditable body inside an iframe):
    return element.tagName == "TEXTAREA" ||
          (element.isContentEditable && element.tagName != "BODY");
}

// Note from Ashraf: I commented the old code that initially iterates over all TEXTAREA elements and instead
// used event delegation by attaching event handlers only to the root element. This is better for performance
// and covers the elements that are later appended to the DOM
document.documentElement.addEventListener("keydown", function (evt) {
    if(isEligible(evt.target)) {
        var key = evt.keyCode || evt.charCode;
        // check if user has finished entering a word
        if(key == 32/*space*/ || key == 188/*comma*/ || key == 190/*dot*/) {
            detectAndSetLanguage(evt.target);
        }
    }
});

// HACK: The focus event doesn't bubble, so I set the useCapture parameter of addEventListener to true.
// We should use the focusin event which bubbles but it's not supported on Firefox as of the date I wrote this.
// See the note on this article https://developer.mozilla.org/en-US/docs/Web/Events/focusin
document.documentElement.addEventListener("focus", function (evt) {
    // Set correct language when we set the focus to already filled textarea
    if(isEligible(evt.target)) {
        detectAndSetLanguage(evt.target);
    }
}, true);

document.documentElement.addEventListener("paste", function (e)
{
    // We act only when data is pasted on a TEXTAREA element
    if(isEligible(e.target)) {
        // We try to detect only if the pasted data is available in plain text format
        const text = e.clipboardData.getData("text/plain");
        if(text) {
            detectAndSetLanguage(e.target, text);
        }
    }
});
<commit_msg>code cleanup<commit_after>// Automatic Dictionary Switcher Add-on for Firefox
// Copyright 2015 Daniel Naber
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

const minimumCharacterLength = 25,
      // The regex is obtained from http://www.regexlib.com/REDetails.aspx?regexp_id=146
      filterRegEx = /(http|https|ftp)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?\/?([a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~])*/gi;

let userPreferences,
    // input element which the addon operates on currently (e.g. textarea):
    ignoreSignature;

// Listen to a message from the main script containing user preferences
self.port.on("config", function (prefs) {
    // We require the presence of user preferences just to know the languages for which the user chose "Don't
    // detect this language" option so that we disable spell checking if we encountered text in these languages
    userPreferences = prefs;
    ignoreSignature = userPreferences["ignoreSignature"];
});

function detectAndSetLanguage(targetElement, text) {
    // If spell checking was disabled by the page developer, we honor the setting and don't set the value of
    // the spellcheck attribute to true, but we still try to detect the language and keep the ordinary operation
    // of the addon because the user can manually enable spell checking from the context menu, and the spellcheck
    // attribute stays false.
    // I set the developerDisabledSpellChecking to indicate this case
    // (see how the firefoxDictSwitcherDisabledSpellCheck flag is set later)
    const developerDisabledSpellChecking = targetElement.spellcheck === false &&
                                          !targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck;

    if(ignoreSignature) {
        let signatureDelimiterPos = text.indexOf("-- \n");
        if(signatureDelimiterPos >= 0) {
            // cut off signature: it may be written in a different language than
            // the main text and would thus decrease language detection quality:
            text = text.substring(0, signatureDelimiterPos);
        }
    }

    // Remove URLs from text (issue #15):
    text = text.replace(filterRegEx, "");

    // we are only going to check language if there is some amount of text available as
    // that will increase our chances of detecting language correctly.
    if(text.length >= minimumCharacterLength) {
        //const startTime = performance.now();
        // Looks like we have enough text to reliably detect the language.
        var francLangCode;
        try {
            francLangCode = franc(text, { whitelist: getDetectableLanguages() });
        } catch (e) {
            self.port.emit("showFeedbackInToolbar", "?", "Error: " + e.toString(), false);
            return;
        }
        var shortCode;
        var langName;
        if(francLangCode === "und") {  // franc's code for 'unknown'
            self.port.emit("showFeedbackInToolbar", "?", "Could not detect language", false);
            return;
        } else {
            var languageInfo = iso6393[francLangCode];
            if(!languageInfo) {
                self.port.emit("showFeedbackInToolbar", "?", "Could not find language for code " + francLangCode, false);
                return;
            }
            shortCode = languageInfo.iso6391;
            langName = languageInfo.name;
        }
        //console.log(`Detected language code is (${language}) in ${performance.now() - startTime} ms`);

        // If the language was detected successfully enable spell checking and send its code to the main script
        if(!developerDisabledSpellChecking) {
            // We enable spell checking only if the attribute wasn't already set to false by the page developer
            targetElement.spellcheck = true;
        }
        self.port.emit("changeDictionary", shortCode, langName);

    } else {
        // Because we can't detect the language, disable spell checking
        targetElement.spellcheck = false;

        // And set a flag to indicate that it's our code who disabled spell checking not the page developer's
        // We do so only if the attribute wasn't already set to false be the page developer
        if(!developerDisabledSpellChecking) {
            targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck = "1";
        }

        // Sending null to the main script also indicates that the input text is too short to detect the language
        self.port.emit("showFeedbackInToolbar", "...", "Need at least " + minimumCharacterLength + " characters to detect language", false);
    }
}

function getDetectableLanguages() {
    let detectableLanguages = ['eng', 'spa', 'por', 'deu'];  // the four languages with variants that have a drop-down in our settings
    addAdditionalLanguages(detectableLanguages);
    removeDisabledLanguages(detectableLanguages);
    return detectableLanguages;
}

function addAdditionalLanguages(detectableLanguages) {
    for (var i = 1; i <= 3; i++) {
        let additionalLanguage = userPreferences["additionalLanguage"+i];
        if (additionalLanguage && additionalLanguage !== '-') {
            detectableLanguages.push(additionalLanguage);
        }
    }
}

function removeDisabledLanguages(detectableLanguages)
{
    const potentiallyDisabledLanguages = [
        { short: 'en', long: 'eng' },
        { short: 'es', long: 'spa' },
        { short: 'de', long: 'deu' },
        { short: 'pt', long: 'por' } ];  // these have variants and a "Don't detect this language" option in the settings
    for (var idx in potentiallyDisabledLanguages) {
        let shortCode = potentiallyDisabledLanguages[idx].short;
        if (userPreferences[shortCode] === '-') {
            let longCode = potentiallyDisabledLanguages[idx].long;
            let pos = detectableLanguages.indexOf(longCode);
            if (pos > -1) {
                detectableLanguages.splice(pos, 1);
            }
        }
    }
}

function isEligible(element) {
    // The addon operates only on textarea elements or elements with the contentEditable attribute set.
    // If the body itself is contentEditable we have to give up, as that would mean we put our feedback
    // div in the editable area and that causes a mess (happened on languagetool.org, where the text
    // area is a contentEditable body inside an iframe):
    return element.tagName == "TEXTAREA" ||
          (element.isContentEditable && element.tagName != "BODY");
}

// Note from Ashraf: I commented the old code that initially iterates over all TEXTAREA elements and instead
// used event delegation by attaching event handlers only to the root element. This is better for performance
// and covers the elements that are later appended to the DOM
document.documentElement.addEventListener("keydown", function (evt) {
    if(isEligible(evt.target)) {
        var key = evt.keyCode || evt.charCode;
        // check if user has finished entering a word
        if(key == 32/*space*/ || key == 188/*comma*/ || key == 190/*dot*/) {
            let text = evt.target.value || evt.target.textContent;
            detectAndSetLanguage(evt.target, text);
        }
    }
});

// HACK: The focus event doesn't bubble, so I set the useCapture parameter of addEventListener to true.
// We should use the focusin event which bubbles but it's not supported on Firefox as of the date I wrote this.
// See the note on this article https://developer.mozilla.org/en-US/docs/Web/Events/focusin
document.documentElement.addEventListener("focus", function (evt) {
    // Set correct language when we set the focus to already filled textarea
    if(isEligible(evt.target)) {
        let text = evt.target.value || evt.target.textContent;
        detectAndSetLanguage(evt.target, text);
    }
}, true);

document.documentElement.addEventListener("paste", function (e)
{
    // We act only when data is pasted on a TEXTAREA element
    if(isEligible(e.target)) {
        // We try to detect only if the pasted data is available in plain text format
        let text = e.clipboardData.getData("text/plain");
        if(text) {
            detectAndSetLanguage(e.target, text);
        }
    }
});
<|endoftext|>


<commit_before>/*
This module supports video playback.

### video players
We support DTube, BitChute, Rumble, Odysee, lbryplayer, UGETube, GabTV, Vimeo, YouTube, Google Video, Facebook, and uStream.

### raw formats
We support mp4, ogg, webm, and mov.

Typically, you'll want to use the fit() function.

### CT.video.fit(video)
	- returns stringified html for a video node fitting snugly inside its parent
*/

CT.video = {
	// video link detection and parsing
	"rawVidTypes": ["mp4", "ogg", "webm", "mov"],
	"player2url": {
		"facebook": "www.facebook.com/",
		"google": "video.google.com?docid=",
		"youtube": "www.youtube.com/watch?v=",
		"vimeo": "vimeo.com/",
		"ustream": "www.ustream.tv/recorded/",
		"mp4": "", "ogg": "", "webm": "", "mov": ""
	},
	"embed_url": {
		"facebook": "https://www.facebook.com/plugins/video.php?href=https%3A%2F%2Fwww.facebook.com%2F",
		"youtube": location.protocol + "//www.youtube.com/embed/",
		"vimeo": "//player.vimeo.com/video/",
		"dtube": "https://emb.d.tube/#!/",
		"rumble": "https://rumble.com/embed/",
		"odysee": "https://odysee.com/$/embed/",
		"ugetube": "https://ugetube.com/embed/",
		"bitchute": "https://www.bitchute.com/embed/",
		"gab": "https://tv.gab.com/channel/",
		"lbryplayer": "https://cdn.lbryplayer.xyz/api/v4/streams/free/"
	},
	"urlFromData": function(player, docid) {
		return player ? (location.protocol + "//" + CT.video.player2url[player] + docid) : "";
	},
	"getQSParam": function(url, param) {
		var s = url.indexOf(param + "=") + param.length + 1,
			e = url.indexOf("&", s);
		if (e == -1)
			e = url.indexOf("#", s);
		if (e == -1)
			return url.slice(s);
		return url.slice(s, e);
	},
	"docidFromUrl": function(url) {
		if (url.indexOf("d.tube") != -1)
			return url.split("/v/")[1];
		else if (url.indexOf("bitchute.com") != -1)
			return url.split("/video/")[1];
		else if (url.indexOf("rumble.com") != -1)
			return url.split("/embed/")[1];
		else if (url.indexOf("video.google.com") != -1)
			return CT.video.getQSParam(url, "docid");
		else if (url.indexOf("youtube.com") != -1)
			return CT.video.getQSParam(url, "v");
		else if (url.indexOf("vimeo.com") != -1)
			return url.slice(url.lastIndexOf('/') + 1);
		else if (url.indexOf("facebook.com") != -1 && url.indexOf("video") != -1)
			return encodeURIComponent(url.split("facebook.com/")[1]);
		else if (url.indexOf("ustream.tv/recorded/") != -1)
			return url.split("recorded/")[1].split("?")[0];
		else if (url.indexOf("lbryplayer.xyz") != -1)
			return url.slice(47);
		else if (url.indexOf("odysee.com") != -1)
			return url.slice(27);
		else if (url.indexOf("ugetube.com") != -1)
			return url.split("_")[1].split(".")[0];
		else if (url.indexOf("tv.gab.com") != -1)
			return url.split("/channel/")[1].replace("/view/", "/embed/");
		var spliturl = url.split('.'),
			ext = spliturl[spliturl.length - 1];
		if (CT.video.rawVidTypes.indexOf(ext) != -1) // eventually do more about ssl
			return url;//.replace("http://", "").replace("https://", "");
		return "";
	},
	"playerFromUrl": function(url) {
		if (url.indexOf("bitchute.com") != -1)
			return "bitchute";
		if (url.indexOf("rumble.com") != -1)
			return "rumble";
		if (url.indexOf("d.tube") != -1)
			return "dtube";
		if (url.indexOf("facebook.com") != -1 && url.indexOf("video") != -1)
			return "facebook";
		if (url.indexOf("video.google.com") != -1)
			return "google";
		if (url.indexOf("youtube.com") != -1)
			return "youtube";
		if (url.indexOf("vimeo.com") != -1)
			return "vimeo";
		if (url.indexOf("ustream.tv") != -1)
			return "ustream";
		if (url.indexOf("lbryplayer.xyz") != -1)
			return "lbryplayer";
		if (url.indexOf("odysee.com") != -1)
			return "odysee";
		if (url.indexOf("ugetube.com") != -1)
			return "ugetube";
		if (url.indexOf("tv.gab.com") != -1)
			return "gab";
		var spliturl = url.split('.'),
			ext = spliturl[spliturl.length - 1];
		if (CT.video.rawVidTypes.indexOf(ext) != -1)
			return ext;
		return "";
	},
	"videoData": function(vlink) {
		var player = CT.video.playerFromUrl(vlink);
		return player ? {
			player: player,
			docid: CT.video.docidFromUrl(vlink)
		} : null;
	},
	"_embed": function(video, w, h) {
		var dims = "width=" + w;
		if (h)
			dims += " height=" + h;
		if (["facebook", "dtube", "bitchute"].indexOf(video.player) != -1) {
			if (typeof w == "number") {
				h = w * 315 / 560; // w 560 h 315
				dims = "width=" + w + " height=" + h;
			} else
				w = 254; // maybe?
//		} else { // eh ........
//			w = "100%";
//			dims = "width=100% height=auto";
		}
		var ifs = ' style="border:none;overflow:hidden;max-width:100%;"',
			iurl = CT.video.embed_url[video.player] + video.docid,
			vsp = 'onclick="arguments[0].stopPropagation();"';
		if (["odysee", "ugetube", "dtube", "bitchute", "rumble", "gab"].includes(video.player))
			return '';
		else if (video.player == "facebook")
			return '';
		else if (video.player == "google")
			return "<embed " + dims + " id=VideoPlayback src=" + location.protocol + "//video.google.com/googleplayer.swf?docid=" + video.docid + "&hl=en&fs=true allowFullScreen=true allowScriptAccess=always type=application/x-shockwave-flash> </embed>";
		else if (video.player == "youtube" || video.player == "vimeo")
			return "";
		else if (video.player == "ustream")
			return "<object type=application/x-shockwave-flash data=" + location.protocol + "//static-cdn1.ustream.tv/swf/live/viewerqos:21.swf " + dims + "id=utv" + video.docid + " name=utv" + video.docid + "><param name=flashvars value=autoplay=true&locale=en_US&referrer=http%3A%2F%2Fwww.ustream.tv%2Frecorded%2F" + video.docid + "%3Futm_campaign%3Dustre.am%26utm_source%3Dustre.am%2F%3A44gEy%26utm_medium%3Dsocial%26utm_content%3D20150324210416&autoResize=false&enablejsapi=true&sv=6&volume=1&ts=1427256261325&vid=" + video.docid + "&loc=" + video.docid + "&hasticket=false><param name=allowfullscreen value=true><param name=allowscriptaccess value=always><param name=bgcolor value=000000><param name=wmode value=transparent></object>"
		else if (video.player == "lbryplayer")
			return "<video " + dims + " controls style='max-width:100%' src=" + iurl + " " + vsp + "></video>";
		else if (CT.video.rawVidTypes.indexOf(video.player) != -1)
			return "<video " + dims + " " + vsp + " controls style='max-width:100%'><source src=" + video.docid + " type=video/" + video.player + "></video>";
		else
			alert("unknown video player: "+video.player);
	},
	"embed": function(video, small) {
		var w = small ? 375 : 400;
//		var h = small ? 315 : 335;
		var h = small ? 180 : 220;
		return CT.video._embed(video, w, h);
	},
	"fit": function(video) {
		return CT.video._embed(video, "90%", "90%");
	},
	"full": function(video) {
		return CT.video._embed(video, "100%", "100%");
	},
	"unthumb": function(key, rand, e) {
		var n = CT.dom.id("thumb" + rand + key);
		n.classList.remove("vidthumb");
		CT.dom.setContent(n, CT.video.embed(CT.data.get(key)));
		e && e.stopPropagation();
	},
	"thumbnail": function(video, htmlSafe) {
		if (htmlSafe) {
			var rand = Math.floor(Math.random() * 1000);
			return '

';
		}
		var thumb = CT.dom.img(video.thumbnail, "w1", function() {
			thumb.parentNode.innerHTML = CT.video.embed(video);
		}, null, null, null, null, "vidthumb");
		return CT.dom.div(thumb);
	}
};<commit_msg>CT.video: attach unthumb() (as __ctv_unthumb) to window; thumbnail() uses __ctv_unthumb<commit_after>/*
This module supports video playback.

### video players
We support DTube, BitChute, Rumble, Odysee, lbryplayer, UGETube, GabTV, Vimeo, YouTube, Google Video, Facebook, and uStream.

### raw formats
We support mp4, ogg, webm, and mov.

Typically, you'll want to use the fit() function.

### CT.video.fit(video)
	- returns stringified html for a video node fitting snugly inside its parent
*/

CT.video = {
	// video link detection and parsing
	"rawVidTypes": ["mp4", "ogg", "webm", "mov"],
	"player2url": {
		"facebook": "www.facebook.com/",
		"google": "video.google.com?docid=",
		"youtube": "www.youtube.com/watch?v=",
		"vimeo": "vimeo.com/",
		"ustream": "www.ustream.tv/recorded/",
		"mp4": "", "ogg": "", "webm": "", "mov": ""
	},
	"embed_url": {
		"facebook": "https://www.facebook.com/plugins/video.php?href=https%3A%2F%2Fwww.facebook.com%2F",
		"youtube": location.protocol + "//www.youtube.com/embed/",
		"vimeo": "//player.vimeo.com/video/",
		"dtube": "https://emb.d.tube/#!/",
		"rumble": "https://rumble.com/embed/",
		"odysee": "https://odysee.com/$/embed/",
		"ugetube": "https://ugetube.com/embed/",
		"bitchute": "https://www.bitchute.com/embed/",
		"gab": "https://tv.gab.com/channel/",
		"lbryplayer": "https://cdn.lbryplayer.xyz/api/v4/streams/free/"
	},
	"urlFromData": function(player, docid) {
		return player ? (location.protocol + "//" + CT.video.player2url[player] + docid) : "";
	},
	"getQSParam": function(url, param) {
		var s = url.indexOf(param + "=") + param.length + 1,
			e = url.indexOf("&", s);
		if (e == -1)
			e = url.indexOf("#", s);
		if (e == -1)
			return url.slice(s);
		return url.slice(s, e);
	},
	"docidFromUrl": function(url) {
		if (url.indexOf("d.tube") != -1)
			return url.split("/v/")[1];
		else if (url.indexOf("bitchute.com") != -1)
			return url.split("/video/")[1];
		else if (url.indexOf("rumble.com") != -1)
			return url.split("/embed/")[1];
		else if (url.indexOf("video.google.com") != -1)
			return CT.video.getQSParam(url, "docid");
		else if (url.indexOf("youtube.com") != -1)
			return CT.video.getQSParam(url, "v");
		else if (url.indexOf("vimeo.com") != -1)
			return url.slice(url.lastIndexOf('/') + 1);
		else if (url.indexOf("facebook.com") != -1 && url.indexOf("video") != -1)
			return encodeURIComponent(url.split("facebook.com/")[1]);
		else if (url.indexOf("ustream.tv/recorded/") != -1)
			return url.split("recorded/")[1].split("?")[0];
		else if (url.indexOf("lbryplayer.xyz") != -1)
			return url.slice(47);
		else if (url.indexOf("odysee.com") != -1)
			return url.slice(27);
		else if (url.indexOf("ugetube.com") != -1)
			return url.split("_")[1].split(".")[0];
		else if (url.indexOf("tv.gab.com") != -1)
			return url.split("/channel/")[1].replace("/view/", "/embed/");
		var spliturl = url.split('.'),
			ext = spliturl[spliturl.length - 1];
		if (CT.video.rawVidTypes.indexOf(ext) != -1) // eventually do more about ssl
			return url;//.replace("http://", "").replace("https://", "");
		return "";
	},
	"playerFromUrl": function(url) {
		if (url.indexOf("bitchute.com") != -1)
			return "bitchute";
		if (url.indexOf("rumble.com") != -1)
			return "rumble";
		if (url.indexOf("d.tube") != -1)
			return "dtube";
		if (url.indexOf("facebook.com") != -1 && url.indexOf("video") != -1)
			return "facebook";
		if (url.indexOf("video.google.com") != -1)
			return "google";
		if (url.indexOf("youtube.com") != -1)
			return "youtube";
		if (url.indexOf("vimeo.com") != -1)
			return "vimeo";
		if (url.indexOf("ustream.tv") != -1)
			return "ustream";
		if (url.indexOf("lbryplayer.xyz") != -1)
			return "lbryplayer";
		if (url.indexOf("odysee.com") != -1)
			return "odysee";
		if (url.indexOf("ugetube.com") != -1)
			return "ugetube";
		if (url.indexOf("tv.gab.com") != -1)
			return "gab";
		var spliturl = url.split('.'),
			ext = spliturl[spliturl.length - 1];
		if (CT.video.rawVidTypes.indexOf(ext) != -1)
			return ext;
		return "";
	},
	"videoData": function(vlink) {
		var player = CT.video.playerFromUrl(vlink);
		return player ? {
			player: player,
			docid: CT.video.docidFromUrl(vlink)
		} : null;
	},
	"_embed": function(video, w, h) {
		var dims = "width=" + w;
		if (h)
			dims += " height=" + h;
		if (["facebook", "dtube", "bitchute"].indexOf(video.player) != -1) {
			if (typeof w == "number") {
				h = w * 315 / 560; // w 560 h 315
				dims = "width=" + w + " height=" + h;
			} else
				w = 254; // maybe?
//		} else { // eh ........
//			w = "100%";
//			dims = "width=100% height=auto";
		}
		var ifs = ' style="border:none;overflow:hidden;max-width:100%;"',
			iurl = CT.video.embed_url[video.player] + video.docid,
			vsp = 'onclick="arguments[0].stopPropagation();"';
		if (["odysee", "ugetube", "dtube", "bitchute", "rumble", "gab"].includes(video.player))
			return '';
		else if (video.player == "facebook")
			return '';
		else if (video.player == "google")
			return "<embed " + dims + " id=VideoPlayback src=" + location.protocol + "//video.google.com/googleplayer.swf?docid=" + video.docid + "&hl=en&fs=true allowFullScreen=true allowScriptAccess=always type=application/x-shockwave-flash> </embed>";
		else if (video.player == "youtube" || video.player == "vimeo")
			return "";
		else if (video.player == "ustream")
			return "<object type=application/x-shockwave-flash data=" + location.protocol + "//static-cdn1.ustream.tv/swf/live/viewerqos:21.swf " + dims + "id=utv" + video.docid + " name=utv" + video.docid + "><param name=flashvars value=autoplay=true&locale=en_US&referrer=http%3A%2F%2Fwww.ustream.tv%2Frecorded%2F" + video.docid + "%3Futm_campaign%3Dustre.am%26utm_source%3Dustre.am%2F%3A44gEy%26utm_medium%3Dsocial%26utm_content%3D20150324210416&autoResize=false&enablejsapi=true&sv=6&volume=1&ts=1427256261325&vid=" + video.docid + "&loc=" + video.docid + "&hasticket=false><param name=allowfullscreen value=true><param name=allowscriptaccess value=always><param name=bgcolor value=000000><param name=wmode value=transparent></object>"
		else if (video.player == "lbryplayer")
			return "<video " + dims + " controls style='max-width:100%' src=" + iurl + " " + vsp + "></video>";
		else if (CT.video.rawVidTypes.indexOf(video.player) != -1)
			return "<video " + dims + " " + vsp + " controls style='max-width:100%'><source src=" + video.docid + " type=video/" + video.player + "></video>";
		else
			alert("unknown video player: "+video.player);
	},
	"embed": function(video, small) {
		var w = small ? 375 : 400;
//		var h = small ? 315 : 335;
		var h = small ? 180 : 220;
		return CT.video._embed(video, w, h);
	},
	"fit": function(video) {
		return CT.video._embed(video, "90%", "90%");
	},
	"full": function(video) {
		return CT.video._embed(video, "100%", "100%");
	},
	"unthumb": function(key, rand, e) {
		var n = CT.dom.id("thumb" + rand + key);
		n.classList.remove("vidthumb");
		CT.dom.setContent(n, CT.video.embed(CT.data.get(key)));
		e && e.stopPropagation();
	},
	"thumbnail": function(video, htmlSafe) {
		if (htmlSafe) {
			var rand = Math.floor(Math.random() * 1000);
			return '

';
		}
		var thumb = CT.dom.img(video.thumbnail, "w1", function() {
			thumb.parentNode.innerHTML = CT.video.embed(video);
		}, null, null, null, null, "vidthumb");
		return CT.dom.div(thumb);
	}
};

window.__ctv_unthumb = CT.video.unthumb;<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const SourceMapConsumer = require('source-map').SourceMapConsumer;

const cssString = fs.readFileSync(path.join(__dirname, 'lib', './style.css'), 'utf8');
const jsString = fs.readFileSync(path.join(__dirname, 'lib', './pluginmain.js'), 'utf8');

const COMMONJS_PLUGIN_PREFIX = '\u0000commonjs-proxy:';

module.exports = function(opts) {
  opts = opts || {};
  var filename = opts.filename || 'stats.html';
  var useSourceMap = !!opts.sourcemap

  return {
    ongenerate({ bundle }, rendered) {

      let root = {
        name: 'root',
        children: []
      };

      if (useSourceMap) {
        addMinifiedSizesToModules(bundle, rendered);
      }

      bundle.modules.forEach(module => {
        let name = module.id;
        let m = {
          //dependencies: module.dependencies,
          size: useSourceMap ? (module.minifiedSize || 0) : Buffer.byteLength(module.code, 'utf8'),
          originalSize: Buffer.byteLength(module.originalCode, 'utf8')
        };

        if (name.indexOf(COMMONJS_PLUGIN_PREFIX) === 0) {
          m.name = COMMONJS_PLUGIN_PREFIX;
          m = {
            plugin: [
              m
            ]
          };
          name = name.substr(COMMONJS_PLUGIN_PREFIX.length);
        }

        addToPath(root, name.split(path.sep), m);

      });
      flattenTree(root);

      var html = `<!doctype html>
          <title>RollUp Visualizer</title>
          <meta charset="utf-8">

RollUp Visualizer

                  of bundle size

      `;
      fs.writeFileSync(filename, html);
    }
  };
};

function getDeepMoreThenOneChild(tree) {
  if (tree.children && tree.children.length === 1) {
    return getDeepMoreThenOneChild(tree.children[0]);
  }
  return tree;
}
  // if root children have only on child we can flatten this
function flattenTree(root) {
  let newChildren = [];
  root.children.forEach((child) => {
    let commonParent = getDeepMoreThenOneChild(child);
    newChildren = newChildren.concat(commonParent.children);
  });
  root.children = newChildren;
}

function addToPath(tree, p, value) {
  if (p[0] === '') {
    p.shift();
  }

  let child = tree.children.filter(c => c.name === p[0])[0];
  if (!child) {
    child = {
      name: p[0],
      children: []
    };
    tree.children.push(child);
  }
  if (p.length === 1) {
    Object.assign(child, value);
    return;
  }
  p.shift();
  addToPath(child, p, value);
}

function getBytesPerFileUsingSourceMap(rendered) {

  let map = new SourceMapConsumer(rendered.map);
  let lines = rendered.code.split(/[\r\n]/);

  let bytesPerFile = {};

  // For every byte in the minified code, do a sourcemap lookup.
  for (let line = 0; line < lines.length; line++) {
    for (let col = 0; col < lines[line].length; col++) {
      let result = map.originalPositionFor({ line: line + 1, column: col });
      let source = result.source || 'root'
      if (!bytesPerFile[source]) bytesPerFile[source] = 0;
      bytesPerFile[source]++
    }
  }
  return Object.keys(bytesPerFile).map(file =>
      ({ file: path.resolve(file), bytes: bytesPerFile[file] }));
}

// Given a file /path/to/file/on/filesystem.js
// - remove extension
// - return `segmentCount` number of path segments, starting from the tail and working backwards
// segments('/path/to/file/on/filesystem.js')(3) === ['filesystem', 'on', 'file']
const segments = filepath => segmentCount =>
  filepath.replace(/\.\w{1,6}$/, "").split(path.sep).reverse().slice(0, segmentCount);

// Adds a .minifiedSize property to each module in the bundle (using sourcemap data)
// If the minified size could not be computed, no property is added.
// Module id are mapped to sources by finding the best match.
// Matching is done by removing the file extensions and comparing path segments
function addMinifiedSizesToModules(bundle, rendered) {
  let fileSizes = getBytesPerFileUsingSourceMap(rendered);

  const findBestMatchingModule = filename => {
    let totalSegments = filename.split(path.sep).length;

    for (let i = 1; i <= totalSegments; i++) {

      let leftVals = segments(filename)(i);

      let matches = bundle.modules.filter(module => {
        let rightVals = segments(module.id)(i)
        return rightVals.every((rightVal, i) => rightVal === leftVals[i]);
      })

      if (matches.length === 1) {
        return matches[0];
      }
    }

    return null;
  }

  fileSizes.forEach(tuple => {
    let module = findBestMatchingModule(tuple.file);
    if (module) module.minifiedSize = tuple.bytes;
  })

}<commit_msg>Fix style issues<commit_after>const fs = require('fs');
const path = require('path');
const SourceMapConsumer = require('source-map').SourceMapConsumer;

const cssString = fs.readFileSync(path.join(__dirname, 'lib', './style.css'), 'utf8');
const jsString = fs.readFileSync(path.join(__dirname, 'lib', './pluginmain.js'), 'utf8');

const COMMONJS_PLUGIN_PREFIX = '\u0000commonjs-proxy:';

module.exports = function(opts) {
  opts = opts || {};
  var filename = opts.filename || 'stats.html';
  var useSourceMap = !!opts.sourcemap

  return {
    ongenerate({ bundle }, rendered) {

      let root = {
        name: 'root',
        children: []
      };

      if (useSourceMap) {
        addMinifiedSizesToModules(bundle, rendered);
      }

      bundle.modules.forEach(module => {
        let name = module.id;
        let m = {
          //dependencies: module.dependencies,
          size: useSourceMap ? (module.minifiedSize || 0) : Buffer.byteLength(module.code, 'utf8'),
          originalSize: Buffer.byteLength(module.originalCode, 'utf8')
        };

        if (name.indexOf(COMMONJS_PLUGIN_PREFIX) === 0) {
          m.name = COMMONJS_PLUGIN_PREFIX;
          m = {
            plugin: [
              m
            ]
          };
          name = name.substr(COMMONJS_PLUGIN_PREFIX.length);
        }

        addToPath(root, name.split(path.sep), m);

      });
      flattenTree(root);

      var html = `<!doctype html>
          <title>RollUp Visualizer</title>
          <meta charset="utf-8">

RollUp Visualizer

                  of bundle size

      `;
      fs.writeFileSync(filename, html);
    }
  };
};

function getDeepMoreThenOneChild(tree) {
  if (tree.children && tree.children.length === 1) {
    return getDeepMoreThenOneChild(tree.children[0]);
  }
  return tree;
}
  // if root children have only on child we can flatten this
function flattenTree(root) {
  let newChildren = [];
  root.children.forEach((child) => {
    let commonParent = getDeepMoreThenOneChild(child);
    newChildren = newChildren.concat(commonParent.children);
  });
  root.children = newChildren;
}

function addToPath(tree, p, value) {
  if (p[0] === '') {
    p.shift();
  }

  let child = tree.children.filter(c => c.name === p[0])[0];
  if (!child) {
    child = {
      name: p[0],
      children: []
    };
    tree.children.push(child);
  }
  if (p.length === 1) {
    Object.assign(child, value);
    return;
  }
  p.shift();
  addToPath(child, p, value);
}

function getBytesPerFileUsingSourceMap(rendered) {

  let map = new SourceMapConsumer(rendered.map);
  let lines = rendered.code.split(/[\r\n]/);

  let bytesPerFile = {};

  // For every byte in the minified code, do a sourcemap lookup.
  for (let line = 0; line < lines.length; line++) {
    for (let col = 0; col < lines[line].length; col++) {
      let result = map.originalPositionFor({ line: line + 1, column: col });
      let source = result.source || 'root';
      if (!bytesPerFile[source]) {
        bytesPerFile[source] = 0;
      }
      bytesPerFile[source]++;
    }
  }
  return Object.keys(bytesPerFile).map(file =>
      ({ file: path.resolve(file), bytes: bytesPerFile[file] }));
}

// Given a file C:/path/to/file/on/filesystem.js
// - remove extension
// - strip filesytstem root
// - return path segments, starting from the tail and working backwards
// segments('C:/path/to/file/on/filesystem.js') === ['filesystem', 'on', 'file', 'to', 'path']
function segments(filepath) {
  let parsed = path.parse(filepath);
  let dirWithoutRoot = parsed.dir.substring(parsed.root.length);

  return dirWithoutRoot.split(path.sep).concat(parsed.name).reverse();
}

// Adds a .minifiedSize property to each module in the bundle (using sourcemap data)
// If the minified size could not be computed, no property is added.
// Module id are mapped to sources by finding the best match.
// Matching is done by removing the file extensions and comparing path segments
function addMinifiedSizesToModules(bundle, rendered) {
  let fileSizes = getBytesPerFileUsingSourceMap(rendered);

  const findBestMatchingModule = filename => {
    let filenameSegments = segments(filename);

    for (let i = 1; i <= filenameSegments.length; i++) {
      let leftVals = filenameSegments.slice(0, i);

      let matches = bundle.modules.filter(module => {
        let moduleSegments = segments(module.id);
        let rightVals = moduleSegments.slice(0, i)
        if (rightVals.length !== leftVals.length) {
          return false;
        }
        return rightVals.every((rightVal, i) => rightVal === leftVals[i]);
      });

      if (matches.length === 1) {
        return matches[0];
      }
    }

    return null;
  }

  fileSizes.forEach(tuple => {
    let module = findBestMatchingModule(tuple.file);
    if (module) module.minifiedSize = tuple.bytes;
  });
}

<|endoftext|>


<commit_before>
import path from 'path'
import os from '../util/os'
import localizer from '../localizer'
import {app, Menu, Tray} from '../electron'

import {createSelector} from 'reselect'

import * as actions from '../actions'

import {partial} from 'underline'

let tray

function makeTray (store) {
  // cf. https://github.com/itchio/itch/issues/462
  // windows still displays a 16x16, whereas
  // some linux DEs don't know what to do with a @x2, etc.
  const iconName = os.platform() === 'linux' ? `${app.getName()}.png` : `${app.getName()}-small.png`
  const iconPath = path.resolve(`${__dirname}/../static/images/tray/${iconName}`)
  tray = new Tray(iconPath)
  tray.setToolTip('itch.io')
  tray.on('click', () => store.dispatch(actions.focusWindow({toggle: true})))
  tray.on('double-click', () => store.dispatch(actions.focusWindow()))
}

function setMenu (trayMenu, store) {
  if (os.platform() === 'darwin') {
    app.dock.setMenu(trayMenu)
  } else {
    if (!tray) {
      makeTray(store)
    }
    tray.setContextMenu(trayMenu)
  }
}

async function go (store, path) {
  store.dispatch(actions.focusWindow())
  store.dispatch(actions.navigate(path))
}

function refreshTray (store, i18n) {
  const t = localizer.getT(i18n.strings, i18n.lang)
  const menuTemplate = [
    {label: t('sidebar.owned'), click: () => go(store, 'library')},
    {label: t('sidebar.dashboard'), click: () => go(store, 'dashboard')}
  ]

  if (os.platform() !== 'darwin') {
    menuTemplate.push({type: 'separator'})
    menuTemplate.push({
      label: t('menu.file.quit'),
      click: () => store.dispatch(actions.quit())
    })
  }

  const trayMenu = Menu.buildFromTemplate(menuTemplate)
  setMenu(trayMenu, store)
}

// TODO: make the tray a lot more useful? that'd be good.
// (like: make it display recent stuff / maybe the last few tabs)

let traySelector
const makeTraySelector = (store) => createSelector(
  (state) => state.i18n,
  (i18n) => {
    setImmediate(() => {
      refreshTray(store, i18n)
    })
  }
)

let hasBooted = false

async function boot (store, action) {
  hasBooted = true
}

async function catchAll (store, action) {
  if (!hasBooted) return
  if (!traySelector) {
    traySelector = makeTraySelector(store)
  }
  traySelector(store.getState())
}

export function getTray () {
  return tray
}

export default {boot, catchAll}
<commit_msg>:shirt: lint, remove unused import<commit_after>
import path from 'path'
import os from '../util/os'
import localizer from '../localizer'
import {app, Menu, Tray} from '../electron'

import {createSelector} from 'reselect'

import * as actions from '../actions'

let tray

function makeTray (store) {
  // cf. https://github.com/itchio/itch/issues/462
  // windows still displays a 16x16, whereas
  // some linux DEs don't know what to do with a @x2, etc.
  const iconName = os.platform() === 'linux' ? `${app.getName()}.png` : `${app.getName()}-small.png`
  const iconPath = path.resolve(`${__dirname}/../static/images/tray/${iconName}`)
  tray = new Tray(iconPath)
  tray.setToolTip('itch.io')
  tray.on('click', () => store.dispatch(actions.focusWindow({toggle: true})))
  tray.on('double-click', () => store.dispatch(actions.focusWindow()))
}

function setMenu (trayMenu, store) {
  if (os.platform() === 'darwin') {
    app.dock.setMenu(trayMenu)
  } else {
    if (!tray) {
      makeTray(store)
    }
    tray.setContextMenu(trayMenu)
  }
}

async function go (store, path) {
  store.dispatch(actions.focusWindow())
  store.dispatch(actions.navigate(path))
}

function refreshTray (store, i18n) {
  const t = localizer.getT(i18n.strings, i18n.lang)
  const menuTemplate = [
    {label: t('sidebar.owned'), click: () => go(store, 'library')},
    {label: t('sidebar.dashboard'), click: () => go(store, 'dashboard')}
  ]

  if (os.platform() !== 'darwin') {
    menuTemplate.push({type: 'separator'})
    menuTemplate.push({
      label: t('menu.file.quit'),
      click: () => store.dispatch(actions.quit())
    })
  }

  const trayMenu = Menu.buildFromTemplate(menuTemplate)
  setMenu(trayMenu, store)
}

// TODO: make the tray a lot more useful? that'd be good.
// (like: make it display recent stuff / maybe the last few tabs)

let traySelector
const makeTraySelector = (store) => createSelector(
  (state) => state.i18n,
  (i18n) => {
    setImmediate(() => {
      refreshTray(store, i18n)
    })
  }
)

let hasBooted = false

async function boot (store, action) {
  hasBooted = true
}

async function catchAll (store, action) {
  if (!hasBooted) return
  if (!traySelector) {
    traySelector = makeTraySelector(store)
  }
  traySelector(store.getState())
}

export function getTray () {
  return tray
}

export default {boot, catchAll}
<|endoftext|>