<commit_before>$(document).ready(function() {
    $('.charts-container').html('');

    $.each(window.chartData, function(index, chart) {
        chart.id = "chart-" + index;
        chart.options = $.extend({}, chart.options);

        $('.charts-container').append('

');
        var divider = $('.chart-labels').children().length > 0 ? " | " : "";
        $('.chart-labels').append(divider + '' + chart.label + '');

        var defaultOptions = {
            height: 240,
            width: $('.charts').width(),
            chartPadding: { left: 3 },
            axisY: {
                onlyInteger: true,
                position: 'end',
                scaleMinSpace: 15,
                labelInterpolationFnc: function(value) {
                    return value;
                },
                referenceValue: 0,
            },
            plugins: [
                Chartist.plugins.legend(),
                Chartist.plugins.tooltip({
                    tooltipFnc: function(meta, value){
                        return decodeEntities(meta);
                    }
                })
            ]
        };

        switch(chart.type) {
            case 'line':
                var options = {
                    axisX: {
                        type: Chartist.FixedScaleAxis,
                        divisor: chart.data.series[0].data.length > 15 ? 15 : chart.data.series[0].data.length,  // no of days
                        labelInterpolationFnc: function(value, index) {
                            var val = isNumber(value) ? new Date(value) : value;
                            return val.formatWithString(chart.options.dateFormat ? chart.options.dateFormat : "MMM 'YY");
                        }
                    },
                    lineSmooth: Chartist.Interpolation.none()
                };

                new Chartist.Line("#" + chart.id, chart.data,
                    $.extend({}, defaultOptions, options, chart.options)
                );
                break;
            case 'bar':
                var options = {
                    axisX: {
                        labelInterpolationFnc: function(value, index) {
                            if (chart.data.labels.length <= 25 || index % Math.ceil(chart.data.labels.length / 25) === 0) {
                                var val = isNumber(value) ? new Date(value) : value;
                                return val.formatWithString(chart.options.dateFormat ? chart.options.dateFormat : "MMM 'YY");
                            } else {
                                return null;
                            }
                        }
                    }
                };

                new Chartist.Bar("#" + chart.id, chart.data,
                    $.extend({}, defaultOptions, options, chart.options)
                );

                break;
            case 'treemap': {
                var colors = ['#990000', '#113861', '#777', '#1B9900', '#FF8800', '#59922b', '#0544d3', '#6b0392', '#f05b4f', '#dda458', '#eacf7d', '#86797d', '#b2c326', '#6188e2', '#a748ca'].reverse();
                var backupColor = '#949494';
                var colorsMap = {};

                var options =     {
                    backgroundColor: function ($box, node) {
                        if (colors.length == 0) { return backupColor; }
                        var parentName = node.accountName.split(':')[chart.options.treemapColorLevel ? chart.options.treemapColorLevel : 0];
                        if (!(parentName in colorsMap)) { colorsMap[parentName] = colors.pop(); }
                        return colorsMap[parentName];
                    },
                    smallestFontSize: 6,
                    startingFontSize: 15,
                    centerLabelVertically: true,
                    itemMargin: 3,
                    mouseenter: function (node, event) {
                        $(event.target).append('

');
                        var $popover = $('.treetable-popover');
                        $popover.html('

Account:

' + node.accountName + '

Balance:

' + node.balance + '

').hide().fadeIn(200);

                        var windowWidth = $(window.window).width();
                        var left = node.bounds.width / 2 - ($popover.width() / 2) - 10,
                        left = (left + $popover.width() > windowWidth) ? (windowWidth - $popover.width()) : left;
                        $popover.css({
                            left: left,
                            top:  node.bounds.height / 2 - $popover.height() - 30
                        });
                    },
                    mouseleave: function (node, event) {
                        $('.treetable-popover').remove();
                    }
                };

                $('div#' + chart.id).css('margin', '8px 0').append('

');
                $('div#' + chart.id + '-container').treemap(chart.data,
                    $.extend({}, options, chart.options)
                );
                break;
            }
            default:
                console.error('Chart-Type "' + chart.type + '" unknown.');
        }
    });

    // Toggle multiple charts
    $('.charts .chart-labels label').click(function() {
        $('.charts .ct-chart').addClass('hidden')
        $('.charts .ct-chart#' + $(this).prop('for')).removeClass('hidden');

        $('.charts .chart-labels label').removeClass('selected');
        $(this).addClass('selected');
    });
    $('.charts .chart-labels label:first-child').click();

    // Toggle chart by clicking on "Hide/Show chart"
    $('.charts input#toggle-chart').click(function() {
        $('.charts-container, .chart-labels').toggle($(this).hasClass('hide-charts'));
        $(this).toggleClass('hide-charts');

        var text = $(this).prop('value');
        $(this).prop('value', text === 'Hide chart' ? 'Show chart' : 'Hide chart');

        return false;
    });
});

<commit_msg>Fix treemap colors when there are more categories than colors (closes #49)<commit_after>$(document).ready(function() {
    $('.charts-container').html('');

    $.each(window.chartData, function(index, chart) {
        chart.id = "chart-" + index;
        chart.options = $.extend({}, chart.options);

        $('.charts-container').append('

');
        var divider = $('.chart-labels').children().length > 0 ? " | " : "";
        $('.chart-labels').append(divider + '' + chart.label + '');

        var defaultOptions = {
            height: 240,
            width: $('.charts').width(),
            chartPadding: { left: 3 },
            axisY: {
                onlyInteger: true,
                position: 'end',
                scaleMinSpace: 15,
                labelInterpolationFnc: function(value) {
                    return value;
                },
                referenceValue: 0,
            },
            plugins: [
                Chartist.plugins.legend(),
                Chartist.plugins.tooltip({
                    tooltipFnc: function(meta, value){
                        return decodeEntities(meta);
                    }
                })
            ]
        };

        switch(chart.type) {
            case 'line':
                var options = {
                    axisX: {
                        type: Chartist.FixedScaleAxis,
                        divisor: chart.data.series[0].data.length > 15 ? 15 : chart.data.series[0].data.length,  // no of days
                        labelInterpolationFnc: function(value, index) {
                            var val = isNumber(value) ? new Date(value) : value;
                            return val.formatWithString(chart.options.dateFormat ? chart.options.dateFormat : "MMM 'YY");
                        }
                    },
                    lineSmooth: Chartist.Interpolation.none()
                };

                new Chartist.Line("#" + chart.id, chart.data,
                    $.extend({}, defaultOptions, options, chart.options)
                );
                break;
            case 'bar':
                var options = {
                    axisX: {
                        labelInterpolationFnc: function(value, index) {
                            if (chart.data.labels.length <= 25 || index % Math.ceil(chart.data.labels.length / 25) === 0) {
                                var val = isNumber(value) ? new Date(value) : value;
                                return val.formatWithString(chart.options.dateFormat ? chart.options.dateFormat : "MMM 'YY");
                            } else {
                                return null;
                            }
                        }
                    }
                };

                new Chartist.Bar("#" + chart.id, chart.data,
                    $.extend({}, defaultOptions, options, chart.options)
                );

                break;
            case 'treemap': {
                var colors = ['#990000', '#113861', '#777', '#1B9900', '#FF8800', '#59922b', '#0544d3', '#6b0392', '#f05b4f', '#dda458', '#eacf7d', '#86797d', '#b2c326', '#6188e2', '#a748ca'].reverse();
                var backupColor = '#949494';
                var colorsMap = {};

                var options =     {
                    backgroundColor: function ($box, node) {
                        var parentName = node.accountName.split(':')[chart.options.treemapColorLevel ? chart.options.treemapColorLevel : 0];
                        if (!(parentName in colorsMap)) { colorsMap[parentName] = colors.pop(); }
                        if (colorsMap[parentName] == undefined) { return backupColor; }
                        return colorsMap[parentName];
                    },
                    smallestFontSize: 6,
                    startingFontSize: 15,
                    centerLabelVertically: true,
                    itemMargin: 3,
                    mouseenter: function (node, event) {
                        $(event.target).append('

');
                        var $popover = $('.treetable-popover');
                        $popover.html('

Account:

' + node.accountName + '

Balance:

' + node.balance + '

').hide().fadeIn(200);

                        var windowWidth = $(window.window).width();
                        var left = node.bounds.width / 2 - ($popover.width() / 2) - 10,
                        left = (left + $popover.width() > windowWidth) ? (windowWidth - $popover.width()) : left;
                        $popover.css({
                            left: left,
                            top:  node.bounds.height / 2 - $popover.height() - 30
                        });
                    },
                    mouseleave: function (node, event) {
                        $('.treetable-popover').remove();
                    }
                };

                $('div#' + chart.id).css('margin', '8px 0').append('

');
                $('div#' + chart.id + '-container').treemap(chart.data,
                    $.extend({}, options, chart.options)
                );
                break;
            }
            default:
                console.error('Chart-Type "' + chart.type + '" unknown.');
        }
    });

    // Toggle multiple charts
    $('.charts .chart-labels label').click(function() {
        $('.charts .ct-chart').addClass('hidden')
        $('.charts .ct-chart#' + $(this).prop('for')).removeClass('hidden');

        $('.charts .chart-labels label').removeClass('selected');
        $(this).addClass('selected');
    });
    $('.charts .chart-labels label:first-child').click();

    // Toggle chart by clicking on "Hide/Show chart"
    $('.charts input#toggle-chart').click(function() {
        $('.charts-container, .chart-labels').toggle($(this).hasClass('hide-charts'));
        $(this).toggleClass('hide-charts');

        var text = $(this).prop('value');
        $(this).prop('value', text === 'Hide chart' ? 'Show chart' : 'Hide chart');

        return false;
    });
});

<|endoftext|>


<commit_before>define(function(require) {

    'use strict';

    var PIXI       = require('pixi');
    var ClipperLib = require('clipper-lib');

    var PixiView  = require('common/v3/pixi/view');
    var Colors    = require('common/colors/colors');
    var Vector2   = require('common/math/vector2');
    var Rectangle = require('common/math/rectangle');

    var Constants = require('constants');

    var LaserBeamsView = PixiView.extend({

        initialize: function(options) {
            this.simulation = options.simulation;

            this.renderer = options.renderer;
            this.width = options.stageWidth;
            this.height = options.stageHeight;
            this.viewportRect = new Rectangle(0, 0, this.width, this.height);
            this.maxLineLength = (this.width > this.height) ? this.width : this.height;
            var m = 100;
            this.viewportClippingRect = new Rectangle(-m, -m, this.width + m * 2, this.height + m * 2);

            this.initGraphics();
            this.initWavesCanvas();
            this.initClipper();

            // Cached objects
            this._point0 = new Vector2();
            this._point1 = new Vector2();
            this._vector = new Vector2();
            this._checkVec = new Vector2();
            this._phaseOffset = new Vector2();

            this.updateMVT(options.mvt);
        },

        initGraphics: function() {
            //var renderer = PIXI.autoDetectRenderer(this.width, this.height, { transparent: true });
            this.raysRenderTexture = new PIXI.RenderTexture(this.renderer, this.width, this.height);
            this.raysSprite = new PIXI.Sprite(this.raysRenderTexture);
            this.raysGraphics = new PIXI.Graphics();

            this.wavesSprite = new PIXI.Sprite();

            this.displayObject.addChild(this.raysSprite);
            this.displayObject.addChild(this.wavesSprite);
        },

        initWavesCanvas: function() {
            this.wavesCanvas = document.createElement('canvas');
            this.wavesCanvas.width = this.width;
            this.wavesCanvas.height = this.height;
            this.wavesContext = this.wavesCanvas.getContext("2d");
        },

        initClipper: function() {
            this._beamVec = new Vector2();
            this._endExtension = new Vector2();

            this.beamCorner0 = new Vector2();
            this.beamCorner1 = new Vector2();
            this.beamCorner2 = new Vector2();
            this.beamCorner3 = new Vector2();
            this.beamSubjectPath = [];
            this.beamClipPath = [];
            this.beamSolutionPaths = [];

            // Both the subject path and clip path have 4 points
            for (var i = 0; i < 4; i++) {
                this.beamSubjectPath.push({ X: 0, Y: 0 });
                this.beamClipPath.push({ X: 0, Y: 0 });
            }

            this.beamClipper = new ClipperLib.Clipper();

        },

        draw: function() {
            if (this.simulation.laser.get('wave')) {
                this.wavesSprite.visible = true;
                this.raysSprite.visible = false;
                this.drawLightWaves();
            }
            else {
                this.wavesSprite.visible = false;
                this.raysSprite.visible = true;
                this.drawLightRays();
            }
        },

        drawLightRays: function() {
            var rays = this.simulation.rays;

            // Sort rays by zIndex so the lower z-indexes come first
            rays.sort(function(a, b) {
                return a.zIndex - b.zIndex;
            });

            var graphics = this.raysGraphics;
            graphics.clear();

            var beamWidth = LaserBeamsView.LASER_BEAM_WIDTH;

            // For each LightRay instance:
                // Set our line color to its color
                // Draw a line between its endpoints
            var point;
            for (var i = 0; i < rays.length; i++) {
                graphics.lineStyle(beamWidth, Constants.wavelengthToHex(rays[i].getLaserWavelength(), true), Math.sqrt(rays[i].getPowerFraction()));
                point = this.mvt.modelToView(rays[i].getTip());
                graphics.moveTo(point.x, point.y);
                point = this.mvt.modelToView(rays[i].getTail());
                graphics.lineTo(point.x, point.y);
            }

            this.raysRenderTexture.clear();
            this.raysRenderTexture.render(graphics);
        },

        drawLightWaves: function() {
            var rays = this.simulation.rays;

            // Sort rays by zIndex so the lower z-indexes come first
            rays.sort(function(a, b) {
                return a.zIndex - b.zIndex;
            });

            // The original Java version seems to have a gradient fill function that can repeat a
            //   linear gradient ("cyclic" option).  We don't have that, so we need to come up
            //   with another way of doing it.
            //
            // Options:
            //   1) We could break up each line into lines that are the length of one period and create
            //      separate gradients for each segments.
            //   2) We could create one big gradient where we calculate the size of the color stops
            //      relative to the total length of the line and then just draw one line.

            var ctx = this.wavesContext;
            var p0 = this._point0;
            var p1 = this._point1;
            var vector = this._vector;

            ctx.clearRect(0, 0, this.wavesCanvas.width, this.wavesCanvas.height);

            for (var i = 0; i < rays.length; i++) {
                // Get the endpoints of the ray in view coordinates
                this.getRayEndpoints(rays[i], p0, p1);

                // Get the vector from p0 to p1
                vector.set(p1).sub(p0);

                // Get the length of one period in view coordinates
                var wavelength = this.mvt.modelToViewDeltaX(rays[i].getWavelength());

                // Find the number of waves (periods) in the line
                var periods = vector.length() / wavelength;
                var wholePeriods = Math.ceil(periods);

                // Make vector extend past p1 to a length that would cover all periods fully
                vector.scale(wholePeriods / periods);

                // Create the gradient
                var beamColor = this.rgbaFromRay(rays[i]);
                var black = 'rgba(0, 0, 0, ' + this.alphaFromRay(rays[i]) + ')';

                // The problem with translating the code for phase offsets from the original
                //   is that they have the cyclical gradients which automatically handle
                //   repeating, so it doesn't matter where the offset is.  They use huge
                //   offsets that go off the screen, but we need to start at the right place.
                var partialPeriodOffset = (rays[i].getPhaseOffset() % (2 * Math.PI)) / (2 * Math.PI);
                var phaseOffset = this._phaseOffset
                    .set(vector)
                    .normalize()
                    .scale(this.mvt.modelToViewDeltaX((partialPeriodOffset - 1) * rays[i].getWavelength()));

                var x0 = p0.x + phaseOffset.x;
                var y0 = p0.y + phaseOffset.y;
                var gradient = ctx.createLinearGradient(x0, y0, x0 + vector.x, y0 + vector.y);

                var percentForOnePeriod = 1 / wholePeriods;
                for (var p = 0; p < wholePeriods; p++) {
                    gradient.addColorStop(percentForOnePeriod * p,         black);
                    gradient.addColorStop(percentForOnePeriod * (p + 0.5), beamColor);
                }
                gradient.addColorStop(1, black);

                ctx.fillStyle = gradient;

                // Calculate and apply the wave shape
                var paths = this.getWaveShape(rays[i], p0, p1);
                var polygon = paths[0];

                if (polygon && polygon.length) {
                    ctx.beginPath();
                    ctx.moveTo(polygon[0].X, polygon[0].Y);
                    for (var j = 1; j < polygon.length; j++) {
                        ctx.lineTo(polygon[j].X, polygon[j].Y);
                    }
                    ctx.closePath();
                    ctx.fill();
                }
            }

            // Render it to a texture to apply to the sprite
            var canvasTexture = PIXI.Texture.fromCanvas(this.wavesCanvas);
            this.wavesSprite.texture = canvasTexture;
            this.wavesSprite.texture.baseTexture.update();
        },

        rgbaFromRay: function(ray) {
            return Constants.wavelengthToRgba(
                ray.getLaserWavelength(),
                this.alphaFromRay(ray)
            );
        },

        alphaFromRay: function(ray) {
            return Math.sqrt(ray.getPowerFraction()).toFixed(4);
        },

        /**
         * Converts ray's endpoints to view space and restricts them to the viewport.
         *   Returns false if no endpoint is within the viewport (roughly).
         */
        getRayEndpoints: function(ray, p0, p1) {
            var vec = this._checkVec;

            var line = ray.extendBackwards ? ray.getExtendedLineBackwards() : ray.getExtendedLine();

            p0.set(this.mvt.modelToView(line.start));
            p1.set(this.mvt.modelToView(line.end));

            var points;

            // Clip any points that go off the screen
            if (!this.pointVisible(p0) && !this.pointVisible(p1)) {
                points = this.viewportClippingRect.lineIntersectionPoints(p0.x, p0.y, p1.x, p1.y);

                p0.set(points[1]);
                p1.set(points[0]);
            }
            else if (!this.pointVisible(p0)) {
                points = this.viewportClippingRect.lineIntersectionPoints(p0.x, p0.y, p1.x, p1.y);
                p0.set(points[0]);
            }
            else if (!this.pointVisible(p1)) {
                points = this.viewportClippingRect.lineIntersectionPoints(p0.x, p0.y, p1.x, p1.y);
                p1.set(points[0]);
            }
        },

        pointVisible: function(point) {
            return this.viewportRect.contains(point);
        },

        /**
         * Calculates the wave shape from a LightRay instance as a series
         *   of points that create a polygon that should be filled in.
         */
        getWaveShape: function(ray, p0, p1) {
            this.initBeamShape(ray, p0, p1);
            this.initClippingShape(ray);
            this.cutBeamShape(ray);

            return this.beamSolutionPaths;
        },

        setWavePathPoint: function(i, x, y) {
            this.beamSubjectPath[i].X = x;
            this.beamSubjectPath[i].Y = y;
        },

        setOppositeMediumPathPoint: function(i, x, y) {
            this.beamClipPath[i].X = this.mvt.modelToViewX(x);
            this.beamClipPath[i].Y = this.mvt.modelToViewY(y);
        },

        /**
         * Sets up the clip path with data from the oppositeMediumShape.
         */
        initClippingShape: function(ray) {
            // It's a rectangle, so just set the four corners
            var rect = ray.oppositeMediumShape;

            this.setOppositeMediumPathPoint(0, rect.left(),  rect.top());
            this.setOppositeMediumPathPoint(1, rect.right(), rect.top());
            this.setOppositeMediumPathPoint(2, rect.right(), rect.bottom());
            this.setOppositeMediumPathPoint(3, rect.left(),  rect.bottom());
        },

        /**
         * Sets up the subject path to represent the original beam.
         */
        initBeamShape: function(ray, p0, p1) {
            // We need to extend both ends a bit for drawing purposes
            var beamWidth = this.mvt.modelToViewDeltaX(ray.getWaveWidth());
            if (beamWidth > this.maxLineLength)
                beamWidth = this.maxLineLength;

            var beamVec = this._beamVec.set(p1).sub(p0);
            var beamLength = beamVec.length();
            var angle = beamVec.angle();

            // Set some points up as the corners of the rectangle that represents
            //   the beam as if it were at (0, 0) and pointing to the right.
            this.beamCorner0.set(0,          -beamWidth / 2);
            this.beamCorner1.set(beamLength, -beamWidth / 2);
            this.beamCorner2.set(beamLength,  beamWidth / 2);
            this.beamCorner3.set(0,           beamWidth / 2);

            // Rotate and then translate the points

            this.beamCorner0.rotate(angle).add(p0.x, p0.y);
            this.beamCorner1.rotate(angle).add(p0.x, p0.y);
            this.beamCorner2.rotate(angle).add(p0.x, p0.y);
            this.beamCorner3.rotate(angle).add(p0.x, p0.y);

            // Then put them in the clipper subject and convert to view coordinates
            this.setWavePathPoint(0, this.beamCorner0.x, this.beamCorner0.y);
            this.setWavePathPoint(1, this.beamCorner1.x, this.beamCorner1.y);
            this.setWavePathPoint(2, this.beamCorner2.x, this.beamCorner2.y);
            this.setWavePathPoint(3, this.beamCorner3.x, this.beamCorner3.y);
        },

        /**
         * The wave is wider than the ray and must be clipped against the opposite
         *   medium so it doesn't leak over.
         */
        cutBeamShape: function() {
            // ClipperLib only supports integers, so we need to scale everything up by a lot and then back down
            ClipperLib.JS.ScaleUpPath(this.beamSubjectPath, LaserBeamsView.CLIPPER_COORDINATE_SCALE);
            ClipperLib.JS.ScaleUpPath(this.beamClipPath,    LaserBeamsView.CLIPPER_COORDINATE_SCALE);

            this.beamClipper.Clear();
            this.beamClipper.AddPath(this.beamSubjectPath, ClipperLib.PolyType.ptSubject, true);
            this.beamClipper.AddPath(this.beamClipPath,    ClipperLib.PolyType.ptClip,    true);

            var succeeded = this.beamClipper.Execute(
                ClipperLib.ClipType.ctDifference,
                this.beamSolutionPaths,
                ClipperLib.PolyFillType.pftNonZero,
                ClipperLib.PolyFillType.pftNonZero
            );

            // Scale the solution back down
            ClipperLib.JS.ScaleDownPaths(this.beamSolutionPaths, LaserBeamsView.CLIPPER_COORDINATE_SCALE);
        },

        /**
         * Updates the model-view-transform and anything that
         *   relies on it.
         */
        updateMVT: function(mvt) {
            this.mvt = mvt;

            this.update();
        },

        updateBlendMode: function() {
            if (this.simulation.get('wavelength') === Constants.WHITE_LIGHT)
                this.raysGraphics.blendMode = PIXI.BLEND_MODES.ADD;
            else
                this.raysGraphics.blendMode = PIXI.BLEND_MODES.NORMAL;
        },

        update: function() {
            this.draw();
            this.updateBlendMode();
        }

    }, Constants.LaserBeamsView);

    return LaserBeamsView;
});<commit_msg>Fixed blend mode only getting updated after a draw instead of before.<commit_after>define(function(require) {

    'use strict';

    var PIXI       = require('pixi');
    var ClipperLib = require('clipper-lib');

    var PixiView  = require('common/v3/pixi/view');
    var Colors    = require('common/colors/colors');
    var Vector2   = require('common/math/vector2');
    var Rectangle = require('common/math/rectangle');

    var Constants = require('constants');

    var LaserBeamsView = PixiView.extend({

        initialize: function(options) {
            this.simulation = options.simulation;

            this.renderer = options.renderer;
            this.width = options.stageWidth;
            this.height = options.stageHeight;
            this.viewportRect = new Rectangle(0, 0, this.width, this.height);
            this.maxLineLength = (this.width > this.height) ? this.width : this.height;
            var m = 100;
            this.viewportClippingRect = new Rectangle(-m, -m, this.width + m * 2, this.height + m * 2);

            this.initGraphics();
            this.initWavesCanvas();
            this.initClipper();

            // Cached objects
            this._point0 = new Vector2();
            this._point1 = new Vector2();
            this._vector = new Vector2();
            this._checkVec = new Vector2();
            this._phaseOffset = new Vector2();

            this.updateMVT(options.mvt);
        },

        initGraphics: function() {
            //var renderer = PIXI.autoDetectRenderer(this.width, this.height, { transparent: true });
            this.raysRenderTexture = new PIXI.RenderTexture(this.renderer, this.width, this.height);
            this.raysSprite = new PIXI.Sprite(this.raysRenderTexture);
            this.raysGraphics = new PIXI.Graphics();

            this.wavesSprite = new PIXI.Sprite();

            this.displayObject.addChild(this.raysSprite);
            this.displayObject.addChild(this.wavesSprite);
        },

        initWavesCanvas: function() {
            this.wavesCanvas = document.createElement('canvas');
            this.wavesCanvas.width = this.width;
            this.wavesCanvas.height = this.height;
            this.wavesContext = this.wavesCanvas.getContext("2d");
        },

        initClipper: function() {
            this._beamVec = new Vector2();
            this._endExtension = new Vector2();

            this.beamCorner0 = new Vector2();
            this.beamCorner1 = new Vector2();
            this.beamCorner2 = new Vector2();
            this.beamCorner3 = new Vector2();
            this.beamSubjectPath = [];
            this.beamClipPath = [];
            this.beamSolutionPaths = [];

            // Both the subject path and clip path have 4 points
            for (var i = 0; i < 4; i++) {
                this.beamSubjectPath.push({ X: 0, Y: 0 });
                this.beamClipPath.push({ X: 0, Y: 0 });
            }

            this.beamClipper = new ClipperLib.Clipper();

        },

        draw: function() {
            if (this.simulation.laser.get('wave')) {
                this.wavesSprite.visible = true;
                this.raysSprite.visible = false;
                this.drawLightWaves();
            }
            else {
                this.wavesSprite.visible = false;
                this.raysSprite.visible = true;
                this.drawLightRays();
            }
        },

        drawLightRays: function() {
            var rays = this.simulation.rays;

            // Sort rays by zIndex so the lower z-indexes come first
            rays.sort(function(a, b) {
                return a.zIndex - b.zIndex;
            });

            var graphics = this.raysGraphics;
            graphics.clear();

            var beamWidth = LaserBeamsView.LASER_BEAM_WIDTH;

            // For each LightRay instance:
                // Set our line color to its color
                // Draw a line between its endpoints
            var point;
            for (var i = 0; i < rays.length; i++) {
                graphics.lineStyle(beamWidth, Constants.wavelengthToHex(rays[i].getLaserWavelength(), true), Math.sqrt(rays[i].getPowerFraction()));
                point = this.mvt.modelToView(rays[i].getTip());
                graphics.moveTo(point.x, point.y);
                point = this.mvt.modelToView(rays[i].getTail());
                graphics.lineTo(point.x, point.y);
            }

            this.raysRenderTexture.clear();
            this.raysRenderTexture.render(graphics);
        },

        drawLightWaves: function() {
            var rays = this.simulation.rays;

            // Sort rays by zIndex so the lower z-indexes come first
            rays.sort(function(a, b) {
                return a.zIndex - b.zIndex;
            });

            // The original Java version seems to have a gradient fill function that can repeat a
            //   linear gradient ("cyclic" option).  We don't have that, so we need to come up
            //   with another way of doing it.
            //
            // Options:
            //   1) We could break up each line into lines that are the length of one period and create
            //      separate gradients for each segments.
            //   2) We could create one big gradient where we calculate the size of the color stops
            //      relative to the total length of the line and then just draw one line.

            var ctx = this.wavesContext;
            var p0 = this._point0;
            var p1 = this._point1;
            var vector = this._vector;

            ctx.clearRect(0, 0, this.wavesCanvas.width, this.wavesCanvas.height);

            for (var i = 0; i < rays.length; i++) {
                // Get the endpoints of the ray in view coordinates
                this.getRayEndpoints(rays[i], p0, p1);

                // Get the vector from p0 to p1
                vector.set(p1).sub(p0);

                // Get the length of one period in view coordinates
                var wavelength = this.mvt.modelToViewDeltaX(rays[i].getWavelength());

                // Find the number of waves (periods) in the line
                var periods = vector.length() / wavelength;
                var wholePeriods = Math.ceil(periods);

                // Make vector extend past p1 to a length that would cover all periods fully
                vector.scale(wholePeriods / periods);

                // Create the gradient
                var beamColor = this.rgbaFromRay(rays[i]);
                var black = 'rgba(0, 0, 0, ' + this.alphaFromRay(rays[i]) + ')';

                // The problem with translating the code for phase offsets from the original
                //   is that they have the cyclical gradients which automatically handle
                //   repeating, so it doesn't matter where the offset is.  They use huge
                //   offsets that go off the screen, but we need to start at the right place.
                var partialPeriodOffset = (rays[i].getPhaseOffset() % (2 * Math.PI)) / (2 * Math.PI);
                var phaseOffset = this._phaseOffset
                    .set(vector)
                    .normalize()
                    .scale(this.mvt.modelToViewDeltaX((partialPeriodOffset - 1) * rays[i].getWavelength()));

                var x0 = p0.x + phaseOffset.x;
                var y0 = p0.y + phaseOffset.y;
                var gradient = ctx.createLinearGradient(x0, y0, x0 + vector.x, y0 + vector.y);

                var percentForOnePeriod = 1 / wholePeriods;
                for (var p = 0; p < wholePeriods; p++) {
                    gradient.addColorStop(percentForOnePeriod * p,         black);
                    gradient.addColorStop(percentForOnePeriod * (p + 0.5), beamColor);
                }
                gradient.addColorStop(1, black);

                ctx.fillStyle = gradient;

                // Calculate and apply the wave shape
                var paths = this.getWaveShape(rays[i], p0, p1);
                var polygon = paths[0];

                if (polygon && polygon.length) {
                    ctx.beginPath();
                    ctx.moveTo(polygon[0].X, polygon[0].Y);
                    for (var j = 1; j < polygon.length; j++) {
                        ctx.lineTo(polygon[j].X, polygon[j].Y);
                    }
                    ctx.closePath();
                    ctx.fill();
                }
            }

            // Render it to a texture to apply to the sprite
            var canvasTexture = PIXI.Texture.fromCanvas(this.wavesCanvas);
            this.wavesSprite.texture = canvasTexture;
            this.wavesSprite.texture.baseTexture.update();
        },

        rgbaFromRay: function(ray) {
            return Constants.wavelengthToRgba(
                ray.getLaserWavelength(),
                this.alphaFromRay(ray)
            );
        },

        alphaFromRay: function(ray) {
            return Math.sqrt(ray.getPowerFraction()).toFixed(4);
        },

        /**
         * Converts ray's endpoints to view space and restricts them to the viewport.
         *   Returns false if no endpoint is within the viewport (roughly).
         */
        getRayEndpoints: function(ray, p0, p1) {
            var vec = this._checkVec;

            var line = ray.extendBackwards ? ray.getExtendedLineBackwards() : ray.getExtendedLine();

            p0.set(this.mvt.modelToView(line.start));
            p1.set(this.mvt.modelToView(line.end));

            var points;

            // Clip any points that go off the screen
            if (!this.pointVisible(p0) && !this.pointVisible(p1)) {
                points = this.viewportClippingRect.lineIntersectionPoints(p0.x, p0.y, p1.x, p1.y);

                p0.set(points[1]);
                p1.set(points[0]);
            }
            else if (!this.pointVisible(p0)) {
                points = this.viewportClippingRect.lineIntersectionPoints(p0.x, p0.y, p1.x, p1.y);
                p0.set(points[0]);
            }
            else if (!this.pointVisible(p1)) {
                points = this.viewportClippingRect.lineIntersectionPoints(p0.x, p0.y, p1.x, p1.y);
                p1.set(points[0]);
            }
        },

        pointVisible: function(point) {
            return this.viewportRect.contains(point);
        },

        /**
         * Calculates the wave shape from a LightRay instance as a series
         *   of points that create a polygon that should be filled in.
         */
        getWaveShape: function(ray, p0, p1) {
            this.initBeamShape(ray, p0, p1);
            this.initClippingShape(ray);
            this.cutBeamShape(ray);

            return this.beamSolutionPaths;
        },

        setWavePathPoint: function(i, x, y) {
            this.beamSubjectPath[i].X = x;
            this.beamSubjectPath[i].Y = y;
        },

        setOppositeMediumPathPoint: function(i, x, y) {
            this.beamClipPath[i].X = this.mvt.modelToViewX(x);
            this.beamClipPath[i].Y = this.mvt.modelToViewY(y);
        },

        /**
         * Sets up the clip path with data from the oppositeMediumShape.
         */
        initClippingShape: function(ray) {
            // It's a rectangle, so just set the four corners
            var rect = ray.oppositeMediumShape;

            this.setOppositeMediumPathPoint(0, rect.left(),  rect.top());
            this.setOppositeMediumPathPoint(1, rect.right(), rect.top());
            this.setOppositeMediumPathPoint(2, rect.right(), rect.bottom());
            this.setOppositeMediumPathPoint(3, rect.left(),  rect.bottom());
        },

        /**
         * Sets up the subject path to represent the original beam.
         */
        initBeamShape: function(ray, p0, p1) {
            // We need to extend both ends a bit for drawing purposes
            var beamWidth = this.mvt.modelToViewDeltaX(ray.getWaveWidth());
            if (beamWidth > this.maxLineLength)
                beamWidth = this.maxLineLength;

            var beamVec = this._beamVec.set(p1).sub(p0);
            var beamLength = beamVec.length();
            var angle = beamVec.angle();

            // Set some points up as the corners of the rectangle that represents
            //   the beam as if it were at (0, 0) and pointing to the right.
            this.beamCorner0.set(0,          -beamWidth / 2);
            this.beamCorner1.set(beamLength, -beamWidth / 2);
            this.beamCorner2.set(beamLength,  beamWidth / 2);
            this.beamCorner3.set(0,           beamWidth / 2);

            // Rotate and then translate the points

            this.beamCorner0.rotate(angle).add(p0.x, p0.y);
            this.beamCorner1.rotate(angle).add(p0.x, p0.y);
            this.beamCorner2.rotate(angle).add(p0.x, p0.y);
            this.beamCorner3.rotate(angle).add(p0.x, p0.y);

            // Then put them in the clipper subject and convert to view coordinates
            this.setWavePathPoint(0, this.beamCorner0.x, this.beamCorner0.y);
            this.setWavePathPoint(1, this.beamCorner1.x, this.beamCorner1.y);
            this.setWavePathPoint(2, this.beamCorner2.x, this.beamCorner2.y);
            this.setWavePathPoint(3, this.beamCorner3.x, this.beamCorner3.y);
        },

        /**
         * The wave is wider than the ray and must be clipped against the opposite
         *   medium so it doesn't leak over.
         */
        cutBeamShape: function() {
            // ClipperLib only supports integers, so we need to scale everything up by a lot and then back down
            ClipperLib.JS.ScaleUpPath(this.beamSubjectPath, LaserBeamsView.CLIPPER_COORDINATE_SCALE);
            ClipperLib.JS.ScaleUpPath(this.beamClipPath,    LaserBeamsView.CLIPPER_COORDINATE_SCALE);

            this.beamClipper.Clear();
            this.beamClipper.AddPath(this.beamSubjectPath, ClipperLib.PolyType.ptSubject, true);
            this.beamClipper.AddPath(this.beamClipPath,    ClipperLib.PolyType.ptClip,    true);

            var succeeded = this.beamClipper.Execute(
                ClipperLib.ClipType.ctDifference,
                this.beamSolutionPaths,
                ClipperLib.PolyFillType.pftNonZero,
                ClipperLib.PolyFillType.pftNonZero
            );

            // Scale the solution back down
            ClipperLib.JS.ScaleDownPaths(this.beamSolutionPaths, LaserBeamsView.CLIPPER_COORDINATE_SCALE);
        },

        /**
         * Updates the model-view-transform and anything that
         *   relies on it.
         */
        updateMVT: function(mvt) {
            this.mvt = mvt;

            this.update();
        },

        updateBlendMode: function() {
            if (this.simulation.get('wavelength') === Constants.WHITE_LIGHT)
                this.raysGraphics.blendMode = PIXI.BLEND_MODES.ADD;
            else
                this.raysGraphics.blendMode = PIXI.BLEND_MODES.NORMAL;
        },

        update: function() {
            this.updateBlendMode();
            this.draw();
        }

    }, Constants.LaserBeamsView);

    return LaserBeamsView;
});<|endoftext|>


<commit_before>function applyQuirks(text,pattern) {

    // Case
    try {
        switch (pattern.case) {
            case "lower":
                text = text.toLowerCase();
                break;
            case "upper":
                text = text.toUpperCase();
                break;
            case "title":
                text = text.toLowerCase().replace(/\b\w/g, function(t) { return t.toUpperCase(); });
                break;
            case "inverted":
                var text = text.replace(/[a-zA-Z]/g, function(t) {
                    var out = t.toUpperCase();
                    if (out==t) {
                        return t.toLowerCase();
                    } else {
                        return out;
                    }
                });
                break;
            case "alt-lines":
                var buffer = text.toLowerCase().split('');
                for(var i=0; i<buffer.length; i+=2){
                    buffer[i] = buffer[i].toUpperCase();
                }
                text = buffer.join('');
                break;
        }
    } catch(e) {}

    // Replacements
    try {
        var replace = {};
        for (i=0; i<pattern.replacements.length; i++) {
            var replacement = pattern.replacements[i];
            replace[replacement[0]] = replacement[1];
        }
    } catch(e) {
        replace = {};
    }

    try {
        var regex = {};
        for (i=0; i<pattern.regexes.length; i++) {
            var re = pattern.regexes[i];
            regex[re[0]] = re[1];
        }
    } catch(e) {
        regex = {}
    }

    var empty = true;
    for(var key in replace) {
        empty = false;
        break;
    }
    for(var key in regex) {
        empty = false;
        break;
    }

    try {
        if (!empty) {
            try {
                var replacementStrings = Object.keys(replace);
            } catch(e) {
                var replacementStrings = [];
            }
            for (i=0;i<replacementStrings.length;i++) {
                replacementStrings[i] = replacementStrings[i].replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
            }
            try {
                var regexStrings = Object.keys(regex);
            } catch(e) {
                var regexStrings = [];
            }
            if (replacementStrings.length!=0 && regexStrings.length!=0) {
                var reg_from = new RegExp(replacementStrings.join("|")+"|"+regexStrings.join("|"), "g");
            } else if (replacementStrings.length!=0 && regexStrings.length==0) {
                var reg_from = new RegExp(replacementStrings.join("|"), "g");
            } else if (replacementStrings.length==0 && regexStrings.length!=0) {
                var reg_from = new RegExp(regexStrings.join("|"), "g");
            } else {
                var reg_from = new RegExp("", "g");
            }
            text = text.replace(reg_from, function($1) {
                if (replace[$1]) {
                    return replace[$1]
                } else {
                    for (var reg in regexStrings) {
                        var original_text = $1;
                        if (RegExp(regexStrings[reg],'g').test($1)) {
                            var insert_text = regex[regexStrings[reg]];
                            insert_text = insert_text.replace(/([^\\]|^)(\$1)/g, function($1,$2,$3) {
                                return $2+original_text;
                            });
                            insert_text = insert_text.replace(/\\\$1/g, "\$1");
                            return insert_text;
                        }
                    }
                }
                return "";
            });
        }
    } catch(e) {}

    // Prefix
    if (pattern.quirk_prefix) {
        text = pattern.quirk_prefix+' '+text;
    }

    // Suffix
    if (pattern.quirk_suffix) {
        text = text+' '+pattern.quirk_suffix;
    }

    return text
}

function depunct(txt) {
    return txt.replace(/[.,?!']/g, '');
}<commit_msg>casing quirk<commit_after>function applyQuirks(text,pattern) {

    // Case
    try {
        switch (pattern.case) {
            case "lower":
                text = text.toLowerCase();
                break;
            case "upper":
                text = text.toUpperCase();
                break;
            case "title":
                text = text.toLowerCase().replace(/\b\w/g, function(t) { return t.toUpperCase(); });
                break;
            case "inverted":
                var text = text.replace(/[a-zA-Z]/g, function(t) {
                    var out = t.toUpperCase();
                    if (out==t) {
                        return t.toLowerCase();
                    } else {
                        return out;
                    }
                });
                break;
            case "alt-lines":
                var buffer = text.toLowerCase().split('');
                for(var i=0; i<buffer.length; i+=2){
                    buffer[i] = buffer[i].toUpperCase();
                }
                text = buffer.join('');
                break;
        }
    } catch(e) {}

    // Replacements
    try {
        var replace = {};
        for (i=0; i<pattern.replacements.length; i++) {
            var replacement = pattern.replacements[i];
            replace[replacement[0]] = replacement[1];
        }
    } catch(e) {
        replace = {};
    }

    try {
        var regex = {};
        for (i=0; i<pattern.regexes.length; i++) {
            var re = pattern.regexes[i];
            regex[re[0]] = re[1];
        }
    } catch(e) {
        regex = {};
    }

    var empty = true;
    for(var key in replace) {
        empty = false;
        break;
    }
    for(var key in regex) {
        empty = false;
        break;
    }

    try {
        if (!empty) {
            try {
                var replacementStrings = Object.keys(replace);
            } catch(e) {
                var replacementStrings = [];
            }
            for (i=0;i<replacementStrings.length;i++) {
                replacementStrings[i] = replacementStrings[i].replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
            }
            try {
                var regexStrings = Object.keys(regex);
            } catch(e) {
                var regexStrings = [];
            }
            if (replacementStrings.length!=0 && regexStrings.length!=0) {
                var reg_from = new RegExp(replacementStrings.join("|")+"|"+regexStrings.join("|"), "g");
            } else if (replacementStrings.length!=0 && regexStrings.length==0) {
                var reg_from = new RegExp(replacementStrings.join("|"), "g");
            } else if (replacementStrings.length==0 && regexStrings.length!=0) {
                var reg_from = new RegExp(regexStrings.join("|"), "g");
            } else {
                var reg_from = new RegExp("", "g");
            }
            text = text.replace(reg_from, function($1) {
                if (replace[$1]) {
                    return replace[$1]
                } else {
                    for (var reg in regexStrings) {
                        var original_text = $1;
                        if (RegExp(regexStrings[reg],'g').test($1)) {
                            var insert_text = regex[regexStrings[reg]];
                            insert_text = insert_text.replace(/([^\\]|^)(\$1)/g, function($1,$2,$3) {
                                return $2+original_text;
                            });
                            insert_text = insert_text.replace(/\\\$1/g, "\$1");
                            return insert_text;
                        }
                    }
                }
                return "";
            });
        }
    } catch(e) {}

    // Prefix
    if (pattern.quirk_prefix) {
        text = pattern.quirk_prefix+' '+text;
    }

    // Suffix
    if (pattern.quirk_suffix) {
        text = text+' '+pattern.quirk_suffix;
    }

    return text
}

function depunct(txt) {
    return txt.replace(/[.,?!']/g, '');
}<|endoftext|>


<commit_before>'use strict';

var formatURL = require( '../utils/format-url' );

var linksView = {
  $schoolLinkText: $( '.school-link' ),

  init: function() {
    this.setSchoolLink();
  },

  setSchoolLink: function() {
    var schoolURL = formatURL( window.schoolData.url );
    if ( schoolURL ) {
      var $schoolLink = $( '<a>', {
        'href': schoolURL,
        'target': 'blank',
        'class': this.$schoolLinkText.attr( 'class' )
      } )
        .text( this.$schoolLinkText.text() );
      this.$schoolLinkText.replaceWith( $schoolLink );
    }
  }

};

module.exports = linksView;
<commit_msg>Fix target value<commit_after>'use strict';

var formatURL = require( '../utils/format-url' );

var linksView = {
  $schoolLinkText: $( '.school-link' ),

  init: function() {
    this.setSchoolLink();
  },

  setSchoolLink: function() {
    var schoolURL = formatURL( window.schoolData.url );
    if ( schoolURL ) {
      var $schoolLink = $( '<a>', {
        'href': schoolURL,
        'target': '_blank',
        'class': this.$schoolLinkText.attr( 'class' )
      } )
        .text( this.$schoolLinkText.text() );
      this.$schoolLinkText.replaceWith( $schoolLink );
    }
  }

};

module.exports = linksView;
<|endoftext|>


<commit_before>'use strict';

var formatURL = require( '../utils/format-url' );
var constructScorecardSearch = require( '../utils/construct-scorecard-search' );

var linksView = {
  $schoolLinkText: $( '.school-link' ),
  $scorecardLink: $( '.scorecard-link' ),

  init: function() {
    this.setSchoolLink();
    this.setScorecardSearch();
  },

  setSchoolLink: function() {
    var schoolURL = formatURL( window.schoolData.url );
    if ( schoolURL ) {
      var $schoolLink = $( '<a>', {
        'href': schoolURL,
        'target': '_blank',
        'class': this.$schoolLinkText.attr( 'class' )
      } )
        .text( this.$schoolLinkText.text() );
      this.$schoolLinkText.replaceWith( $schoolLink );
    }
  },

  setScorecardSearch: function() {
    var pcip = window.programData.cipCode.slice( 0, 2 ),
        zip = window.schoolData.zip5,
        // We're using a 50-mile radius, the most common Scorecard search
        radius = '50',
        scorecardURL = this.$scorecardLink.attr( 'href' ),
        scorecardQuery = constructScorecardSearch( pcip, zip, radius );
    this.$scorecardLink.attr( 'href', scorecardURL + scorecardQuery );
  }

};

module.exports = linksView;
<commit_msg>Better handle missing ZIP and PCIP data<commit_after>'use strict';

var formatURL = require( '../utils/format-url' );
var constructScorecardSearch = require( '../utils/construct-scorecard-search' );

var linksView = {
  $schoolLinkText: $( '.school-link' ),
  $scorecardLink: $( '.scorecard-link' ),

  init: function() {
    this.setSchoolLink();
    this.setScorecardSearch();
  },

  setSchoolLink: function() {
    var schoolURL = formatURL( window.schoolData.url );
    if ( schoolURL ) {
      var $schoolLink = $( '<a>', {
        'href': schoolURL,
        'target': '_blank',
        'class': this.$schoolLinkText.attr( 'class' )
      } )
        .text( this.$schoolLinkText.text() );
      this.$schoolLinkText.replaceWith( $schoolLink );
    }
  },

  setScorecardSearch: function() {
    var pcip = window.programData.cipCode ? window.programData.cipCode.slice( 0, 2 ) : '',
        zip = window.schoolData.zip5 || '',
        // We're using a 50-mile radius, the most common Scorecard search
        radius = '50',
        scorecardURL = this.$scorecardLink.attr( 'href' ),
        scorecardQuery = constructScorecardSearch( pcip, zip, radius );
    this.$scorecardLink.attr( 'href', scorecardURL + scorecardQuery );
  }

};

module.exports = linksView;
<|endoftext|>


<commit_before>/** global: browser */

if ( document.body !== undefined ) {
  var html = document.documentElement.outerHTML;

  if ( html.length > 50000 ) {
    html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length);
  }

  browser.runtime.sendMessage({
    id: 'analyze',
    subject: { html },
    source: 'content.js'
  });

  try {
    var container = document.createElement('wappalyzerData');

    container.setAttribute('id',    'wappalyzerData');
    container.setAttribute('style', 'display: none');

    var script = document.createElement('script');

    script.setAttribute('id', 'wappalyzerEnvDetection');
    script.setAttribute('src', browser.extension.getURL('js/inject.js'));

    container.addEventListener('wappalyzerEvent', (event => {
      var env = event.target.childNodes[0].nodeValue;

      document.documentElement.removeChild(container);
      document.documentElement.removeChild(script);

      env = env.split(' ').slice(0, 500);

      browser.runtime.sendMessage({
        id: 'analyze',
        subject: { env },
        source: 'content.js'
      });
    }), true);

    document.documentElement.appendChild(container);
    document.documentElement.appendChild(script);
  } catch(e) {
    log('Error: ' + e);
  }
}

function log(message) {
  browser.runtime.sendMessage({
    id: 'log',
    message,
    source: 'content.js'
  });
}
<commit_msg>Fix browser is undefined error in content.js<commit_after>/** global: browser */

if ( typeof browser !== 'undefined' && typeof document.body !== 'undefined' ) {
  var html = document.documentElement.outerHTML;

  if ( html.length > 50000 ) {
    html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length);
  }

  try {
    browser.runtime.sendMessage({
      id: 'analyze',
      subject: { html },
      source: 'content.js'
    });

    var container = document.createElement('wappalyzerData');

    container.setAttribute('id',    'wappalyzerData');
    container.setAttribute('style', 'display: none');

    var script = document.createElement('script');

    script.setAttribute('id', 'wappalyzerEnvDetection');
    script.setAttribute('src', browser.extension.getURL('js/inject.js'));

    container.addEventListener('wappalyzerEvent', (event => {
      var env = event.target.childNodes[0].nodeValue;

      document.documentElement.removeChild(container);
      document.documentElement.removeChild(script);

      env = env.split(' ').slice(0, 500);

      browser.runtime.sendMessage({
        id: 'analyze',
        subject: { env },
        source: 'content.js'
      });
    }), true);

    document.documentElement.appendChild(container);
    document.documentElement.appendChild(script);
  } catch(e) {
    log(e);
  }
}

function log(message) {
  browser.runtime.sendMessage({
    id: 'log',
    message,
    source: 'content.js'
  });
}
<|endoftext|>


<commit_before>/*

// opera.scopeSendToHost(service, message)

onload = function() {
function put(str)
{
	document.getElementById('info').innerHTML += str + "
";
}

function enable_service(str)
{
	put('Enabling service ' + str);
	opera.scopeEnableService('console-logger');
}

function host_connected(services)
{
	put('Host connected');
	services = services.split(',');
	put("Host has services: " + services);
	enable_service(services[0]);
}
function receive(service, msg)
{
	put('Got data on service ' + service +':
' + msg + '
');
}
function quit()
{
	put('Host quit');
}

opera.scopeAddClient(host_connected, receive, quit);
put('Ready.');
}

*/

var client = new function()
{
  var self = this;

  var services = [];
  var services_dict = {};
  var services_avaible = {};

  this.addService = function(service)
  {
    services[services.length] = service;
    services_dict[service.name] = service;
  }

  var host_connected = function(_services)
  {
    opera.postError('on host_connected: '+_services);
    services_avaible = eval("({\"" + _services.replace(/,/g, "\":1,\"") + "\":1})");
    var service = null, i = 0;
    for( ; service = services[i]; i++)
    {
      if (service.name in services_avaible)
      {
        opera.postError(service.name);
        opera.scopeEnableService(service.name);
      }
      else
      {
        alert
        (
           'Service is avaible: ' + service.name
        );

      }
    }

  }

  var receive = function(service, msg)
  {
    opera.postError(service+' '+msg);
    var xml_doc = ( new DOMParser() ).parseFromString(msg, "application/xml");
    if(xml_doc)
    {
      services_dict[service].onreceive(xml_doc);
    }
  }

  var quit = function()
  {
    alert('Session quited');
  }

  this.setup = function()
  {

    document.addEventListener('load', arguments.callee, false);

    var args = location.search, params = {}, arg = '', i = 0, ele = null;

    if( args )
    {
      args = args.slice(1).split(';');
      for( ; arg = args[i]; i++)
      {
        arg = arg.split('=');
        params[arg[0]] = arg[1] ? arg[1] : true;
      }
    }
    if( params.debug || params['event-flow'] )
    {
      Debug.init();
      if(params.debug) ini.debug = true;
      if(params['event-flow']) window.__debug_event_flow__ = true;
    }

    if( params['profiling'] )
    {
      Debug.init();
      window.__profiling__ = true;
      window.__times__ = [];
    }

    if( params['test'] )
    {
      Debug.init();
      window.__testing__ = true;
      window.__times_spotlight__ = [];
    }

    if( params['profile-dom'] )
    {
      Debug.init();
      window.__times_dom = [];
    }

    window[defaults.viewport] = document.getElementsByTagName(defaults.viewport_main_container)[0];

    if( viewport )
    {
      setupMarkup();

      opera.scopeAddClient(host_connected, receive, quit);
      opera.postError('opera.scopeAddClient');

    }
    else
    {
      opera.postError('missing viewport');
    }

  }

  var setupMarkup = function()
  {

    UIBase.copyCSS(resolve_map);

    // TODO clean up

    container = viewport.appendChild(document.createElement('div'));
    container.style.cssText = 'position:absolute;top:0;left:-1000px;';
    container.innerHTML = resolve_map_2.markup;

    var set = null, i = 0;

    for( ; set = resolve_map_2[i]; i++ )
    {
      defaults[set.target] = set.getValue();
    }
    viewport.removeChild(container);

    new CompositeView('console_new', 'Console', console_rough_layout);
    new CompositeView('js_new', 'Script', js_rough_layout);
    new CompositeView('dom_new', 'DOM', dom_rough_layout);

    window.topCell = new TopCell
    (
      main_layout,
      function()
      {
        this.top = 0;
        this.left = 0;
        this.width = innerWidth;
        this.height = innerHeight;
      },
      function()
      {
        this.setStartDimesions();
        this.update();
        this.setup();
      }
    );

    new SlideViews(document);
  }

  this.onquit = function()
  {
    messages.post('host-state', {state: 'inactive'});
  }

  this.post = function(service, msg)
  {
    opera.postError('command: '+service+' '+ msg)
    opera.scopeSendToHost(service, "<?xml version=\"1.0\"?>" + msg)
  }

  document.addEventListener('load', this.setup, false);

}

/* TODO take that out from the global scope */

new ToolbarConfig
(
  'main-view',
  [
    {
      handler: 'create-all-runtimes',
      title: 'Create all runtimes'
    }
  ]
)

var console_rough_layout =
{
  dir: 'v', width: 700, height: 700,
  children:
  [
    { height: 200, tabs: ['console'] }
  ]
}

var dom_rough_layout =
{
  dir: 'v', width: 700, height: 700,
  children:
  [
    { height: 150, tabs: ['runtimes'] },
    { width: 200, tabs: ['dom-markup-style', 'dom-tree-style'] }
  ]
}

var js_rough_layout =
{
  dir: 'h', width: 700, height: 700,
  children:
  [
    {
      width: 700,
      children:
      [
        { height: 150, tabs: ['runtimes'] },
        { width: 650, tabs: ['js_source']},
      ]
    },
    {
      width: 250,
      children:
      [
        { height: 250, tabs: ['callstack'] },
        { height: 1000, tabs: ['frame_inspection'] }
      ]
    }
  ]
}

var main_layout =
{
  id: 'main-view',
  tabs: ['console_new', 'js_new', 'dom_new']
}

var resolve_map_properties =
[
  {s_name: 'border-top-width'},
  {s_name: 'border-right-width'},
  {s_name: 'border-bottom-width'},
  {s_name: 'border-left-width'},
  {s_name: 'padding-top'},
  {s_name: 'padding-right'},
  {s_name: 'padding-bottom'},
  {s_name: 'padding-left'},
  {s_name: 'width'},
  {s_name: 'height'}
]

var resolve_map =
[
  {
    source: 'toolbar',
    target: Toolbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'tabs',
    target: Tabs.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'container',
    target: Container.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'top-tabs',
    target: TopTabs.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'top-container',
    target: TopContainer.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'top-statusbar',
    target: TopStatusbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'top-toolbar',
    target: TopToolbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'window-toolbar',
    target: WindowToolbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'window-header',
    target: defaults,
    properties:
    [
      {
        t_name: 'window_header_offsetHeight',
        setProp: function(source, decalaration)
        {
          return source.offsetHeight;
        }
      }
    ]
  },
  {
    source: 'window-container',
    target: WindowContainer.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'window-satusbar',
    target: WindowStatusbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'window-statusbar',
    target: defaults,
    properties:
    [
      {
        t_name: 'window_statusbar_offsetHeight',
        setProp: function(source, decalaration)
        {
          return source.offsetHeight;
        }
      }
    ]
  },
];

var resolve_map_2 =
[
  {
    id: 'test-line-height',
    property: 'lineHeight',
    target: 'js-source-line-height',
    getValue: function(){return parseInt(document.getElementById(this.id).currentStyle[this.property])}
  },
  {
    id: 'test-scrollbar-width',
    target: 'scrollbar-width',
    getValue: function(){return ( 100 - document.getElementById(this.id).offsetWidth )}
  }
];

resolve_map_2.markup =
  "
"+
    "
"+
      "
"+
        "
test
"+
        "
"+
          "

"+
        "
"
      "
"+
    "
"+
  "
";

<commit_msg>clean up for the first internal release<commit_after>/*

// opera.scopeSendToHost(service, message)

onload = function() {
function put(str)
{
	document.getElementById('info').innerHTML += str + "
";
}

function enable_service(str)
{
	put('Enabling service ' + str);
	opera.scopeEnableService('console-logger');
}

function host_connected(services)
{
	put('Host connected');
	services = services.split(',');
	put("Host has services: " + services);
	enable_service(services[0]);
}
function receive(service, msg)
{
	put('Got data on service ' + service +':
' + msg + '
');
}
function quit()
{
	put('Host quit');
}

opera.scopeAddClient(host_connected, receive, quit);
put('Ready.');
}

*/

var client = new function()
{
  var self = this;

  var services = [];
  var services_dict = {};
  var services_avaible = {};

  this.addService = function(service)
  {
    services[services.length] = service;
    services_dict[service.name] = service;
  }

  var host_connected = function(_services)
  {
    opera.postError('on host_connected: '+_services);
    services_avaible = eval("({\"" + _services.replace(/,/g, "\":1,\"") + "\":1})");
    var service = null, i = 0;
    for( ; service = services[i]; i++)
    {
      if (service.name in services_avaible)
      {
        opera.postError(service.name);
        opera.scopeEnableService(service.name);
      }
      else
      {
        alert
        (
           'Service is avaible: ' + service.name
        );

      }
    }

  }

  var receive = function(service, msg)
  {
    opera.postError(service+' '+msg);
    var xml_doc = ( new DOMParser() ).parseFromString(msg, "application/xml");
    if(xml_doc)
    {
      services_dict[service].onreceive(xml_doc);
    }
  }

  var quit = function()
  {
    alert('Session quited');
  }

  this.setup = function()
  {

    document.addEventListener('load', arguments.callee, false);

    var args = location.search, params = {}, arg = '', i = 0, ele = null;

    if( args )
    {
      args = args.slice(1).split(';');
      for( ; arg = args[i]; i++)
      {
        arg = arg.split('=');
        params[arg[0]] = arg[1] ? arg[1] : true;
      }
    }
    if( params.debug || params['event-flow'] )
    {
      Debug.init();
      if(params.debug) ini.debug = true;
      if(params['event-flow']) window.__debug_event_flow__ = true;
    }

    if( params['profiling'] )
    {
      Debug.init();
      window.__profiling__ = true;
      window.__times__ = [];
    }

    if( params['test'] )
    {
      Debug.init();
      window.__testing__ = true;
      window.__times_spotlight__ = [];
    }

    if( params['profile-dom'] )
    {
      Debug.init();
      window.__times_dom = [];
    }

    window[defaults.viewport] = document.getElementsByTagName(defaults.viewport_main_container)[0];

    if( viewport )
    {
      setupMarkup();

      opera.scopeAddClient(host_connected, receive, quit);
      opera.postError('opera.scopeAddClient');

    }
    else
    {
      opera.postError('missing viewport');
    }

  }

  var setupMarkup = function()
  {

    UIBase.copyCSS(resolve_map);

    // TODO clean up

    container = viewport.appendChild(document.createElement('div'));
    container.style.cssText = 'position:absolute;top:0;left:-1000px;';
    container.innerHTML = resolve_map_2.markup;

    var set = null, i = 0;

    for( ; set = resolve_map_2[i]; i++ )
    {
      defaults[set.target] = set.getValue();
    }
    viewport.removeChild(container);

    new CompositeView('console_new', 'Console', console_rough_layout);
    new CompositeView('js_new', 'Script', js_rough_layout);
    new CompositeView('dom_new', 'DOM', dom_rough_layout);

    window.topCell = new TopCell
    (
      main_layout,
      function()
      {
        this.top = 0;
        this.left = 0;
        this.width = innerWidth;
        this.height = innerHeight;
      },
      function()
      {
        this.setStartDimesions();
        this.update();
        this.setup();
      }
    );

    new SlideViews(document);
  }

  this.onquit = function()
  {
    messages.post('host-state', {state: 'inactive'});
  }

  this.post = function(service, msg)
  {
    opera.postError('command: '+service+' '+ msg)
    opera.scopeSendToHost(service, "<?xml version=\"1.0\"?>" + msg)
  }

  document.addEventListener('load', this.setup, false);

}

/* TODO take that out from the global scope */

new ToolbarConfig
(
  'main-view',
  [
    {
      handler: 'create-all-runtimes',
      title: 'Create all runtimes'
    }
  ]
)

var console_rough_layout =
{
  dir: 'v', width: 700, height: 700,
  children:
  [
    { height: 200, tabs: ['runtimes'] },
    { height: 200, tabs: ['console'] }
  ]
}

var dom_rough_layout =
{
  dir: 'h', width: 700, height: 700,
  children:
  [
    { width: 200, tabs: ['dom-markup-style', 'dom-tree-style'] }
  ]
}

var js_rough_layout =
{
  dir: 'h', width: 700, height: 700,
  children:
  [
    { width: 650, tabs: ['js_source']},
    {
      width: 300,
      children:
      [
        { height: 250, tabs: ['callstack'] },
        { height: 1000, tabs: ['frame_inspection'] }
      ]
    }
  ]
}

var main_layout =
{
  id: 'main-view',
  tabs: ['console_new', 'js_new', 'dom_new']
}

var resolve_map_properties =
[
  {s_name: 'border-top-width'},
  {s_name: 'border-right-width'},
  {s_name: 'border-bottom-width'},
  {s_name: 'border-left-width'},
  {s_name: 'padding-top'},
  {s_name: 'padding-right'},
  {s_name: 'padding-bottom'},
  {s_name: 'padding-left'},
  {s_name: 'width'},
  {s_name: 'height'}
]

var resolve_map =
[
  {
    source: 'toolbar',
    target: Toolbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'tabs',
    target: Tabs.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'container',
    target: Container.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'top-tabs',
    target: TopTabs.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'top-container',
    target: TopContainer.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'top-statusbar',
    target: TopStatusbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'top-toolbar',
    target: TopToolbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'window-toolbar',
    target: WindowToolbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'window-header',
    target: defaults,
    properties:
    [
      {
        t_name: 'window_header_offsetHeight',
        setProp: function(source, decalaration)
        {
          return source.offsetHeight;
        }
      }
    ]
  },
  {
    source: 'window-container',
    target: WindowContainer.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'window-satusbar',
    target: WindowStatusbar.prototype,
    properties: resolve_map_properties
  },
  {
    source: 'window-statusbar',
    target: defaults,
    properties:
    [
      {
        t_name: 'window_statusbar_offsetHeight',
        setProp: function(source, decalaration)
        {
          return source.offsetHeight;
        }
      }
    ]
  },
];

var resolve_map_2 =
[
  {
    id: 'test-line-height',
    property: 'lineHeight',
    target: 'js-source-line-height',
    getValue: function(){return parseInt(document.getElementById(this.id).currentStyle[this.property])}
  },
  {
    id: 'test-scrollbar-width',
    target: 'scrollbar-width',
    getValue: function(){return ( 100 - document.getElementById(this.id).offsetWidth )}
  }
];

resolve_map_2.markup =
  "
"+
    "
"+
      "
"+
        "
test
"+
        "
"+
          "

"+
        "
"
      "
"+
    "
"+
  "
";

<|endoftext|>


<commit_before>#!/usr/bin/env node

const AWS = require('aws-sdk');
const archiver = require('archiver');
const commander = require('commander');
const csvParse = require('csv-parse');
const csvStringify = require('csv-stringify');
const fs = require('fs');
const moment = require('moment');
const path = require('path');
const process = require('process');
const puppeteer = require('puppeteer');
const config = require('./pdf-generator-config');

const endpoint = '#seattle';
const parameters = 'report_active=true';
const outputCSVFilename = 'scorecards.csv';
const outputZipFilename = 'scorecards-pdfs.zip';

AWS.config.loadFromPath('./config.json');

function getBuildingUrl(buildingId, baseUrl, year) {
  return `${baseUrl}/${endpoint}/${year}?${parameters}&building=${buildingId}`;
}

async function pageToPdf(buildingId, baseUrl, outputDir, year) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  let status = 'success';

  page.on('pageerror', e => {
    console.log(`Page error for ${buildingId}: '${e}'`);
    status = 'page load error';
  });

  console.log(`Generating PDF for building ${buildingId}`);
  try {
    await page.goto(getBuildingUrl(buildingId, baseUrl, year), { waitUntil: 'networkidle0' });
    await page.pdf({
      path: path.join(outputDir, `${buildingId}.pdf`),
      preferCSSPageSize: true
    });
  } catch (e) {
    console.log(`Exception creating PDF for building ${buildingId}`);
    console.log(e);
    status = 'page load error';
  }
  await browser.close();
  return status;
}

async function openCSV(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(filename, (err, data) => {
      csvParse(data.toString(), { columns: true }, (err, output) => {
        resolve(output);
      });
    });
  });
}

async function writeCSV(filename, records) {
  return new Promise((resolve, reject) => {
    csvStringify(records, { header: true }, (err, output) => {
      fs.writeFile(filename, output, err => {
        resolve();
      });
    });
  });
}

async function writeZip(outputDir) {
  return new Promise((resolve, reject) => {
    const outputPath = path.join(outputDir, outputZipFilename);
    var output = fs.createWriteStream(outputPath);
    var archive = archiver('zip', {
      zlib: { level: 9 }
    });

    output.on('close', () => resolve(outputPath));
    archive.on('warning', err => reject(err));
    archive.on('error', err => reject(err));
    archive.pipe(output);

    archive.glob('*.pdf', { cwd: outputDir });
    archive.glob(outputCSVFilename, { cwd: outputDir });

    archive.finalize();
  });
}

async function downloadPdfs(records, batchSize, baseUrl, outputDir, year) {
  const outputRecords = [];

  let batch = 1;
  while (batch <= Math.ceil(records.length / batchSize)) {
    const batchRecords = records.slice((batch - 1) * batchSize, batch * batchSize);

    await Promise.all(batchRecords.map(async record => {
      const status = await pageToPdf(record.property_id, baseUrl, outputDir, year);
      outputRecords.push(Object.assign({}, record, {
        pdf_filename: `${record.property_id}.pdf`,
        pdf_status: status
      }));
    }));

    batch++;
  }

  return outputRecords;
}

async function uploadToS3(filename, bucket) {
  return new Promise((resolve, reject) => {
    const s3 = new AWS.S3({
      apiVersion: '2006-03-01',
      params: { Bucket: bucket }
    });

    const params = {
      Key: `${moment().format('YYYYMMDDHHmmss')}-generated-pdfs.zip`,
      Body: fs.createReadStream(filename)
    };

    s3.upload(params, {}, (err, data) => {
      if (err) return reject(err);
      return resolve(data);
    });
  });
}

async function sendEmail(email, url) {
  const body = `Your scorecard PDFs are ready: ${url}`;
  return new Promise((resolve, reject) => {
    var params = {
      Destination: {
        CcAddresses: config.email.cc,
        ToAddresses: [email]
      },
      Message: {
        Body: {
          Html: {
            Data: body
          },
          Text: {
            Data: body
          }
        },
        Subject: {
          Data: 'City Energy Project: Scorecards generated'
        }
      },
      ReplyToAddresses: config.email.replyTo,
      ReturnPath: config.email.returnPath,
      Source: config.email.from,
      Tags: [{ Name: 'project', Value: 'cityenergyproject-seattle' }]
    };

    const ses = new AWS.SES({ apiVersion: '2010-12-01' });
    ses.sendEmail(params, function(err, data) {
      if (err) return reject(err);
      return resolve(data);
    });
  });
}

(async () => {
  commander
    .option('-i --input-csv <input-csv>', 'The CSV to read on input')
    .option('-y --year [year]', 'The year to get data for', '2017')
    .option('-b --batch-size [batch-size]', 'Batch size when downloading PDFs', 5)
    .option('-e --email [email]', 'Email address to notify on completion')
    .option('--base-url [base-url]', 'Base URL to get scorecards from', 'http://www.seattle.gov/energybenchmarkingmap')
    .option('-o --output-dir [output-dir]', 'The directory to put generated files in', '.')
    .option('--s3-bucket [s3-bucket]', 'S3 bucket to upload zip file to')
    .option('--upload-to-s3', 'Upload to S3')
    .parse(process.argv);

  if (!commander.inputCsv) {
    commander.help();
  }

  const records = await openCSV(commander.inputCsv);
  const outputRecords = await downloadPdfs(records, commander.batchSize, commander.baseUrl, commander.outputDir, commander.year);
  await writeCSV(path.join(commander.outputDir, outputCSVFilename), outputRecords);

  const zipPath = await writeZip(commander.outputDir);

  if (commander.uploadToS3 && config.environment === 'production' && commander.s3Bucket) {
    const s3data = await uploadToS3(zipPath, s3Bucket);
    if (commander.email) {
      await sendEmail(commander.email, s3data.Location);
    }
  }
})();
<commit_msg>Only load credentials config in development<commit_after>#!/usr/bin/env node

const AWS = require('aws-sdk');
const archiver = require('archiver');
const commander = require('commander');
const csvParse = require('csv-parse');
const csvStringify = require('csv-stringify');
const fs = require('fs');
const moment = require('moment');
const path = require('path');
const process = require('process');
const puppeteer = require('puppeteer');
const config = require('./pdf-generator-config');

const endpoint = '#seattle';
const parameters = 'report_active=true';
const outputCSVFilename = 'scorecards.csv';
const outputZipFilename = 'scorecards-pdfs.zip';

function getBuildingUrl(buildingId, baseUrl, year) {
  return `${baseUrl}/${endpoint}/${year}?${parameters}&building=${buildingId}`;
}

async function pageToPdf(buildingId, baseUrl, outputDir, year) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  let status = 'success';

  page.on('pageerror', e => {
    console.log(`Page error for ${buildingId}: '${e}'`);
    status = 'page load error';
  });

  console.log(`Generating PDF for building ${buildingId}`);
  try {
    await page.goto(getBuildingUrl(buildingId, baseUrl, year), { waitUntil: 'networkidle0' });
    await page.pdf({
      path: path.join(outputDir, `${buildingId}.pdf`),
      preferCSSPageSize: true
    });
  } catch (e) {
    console.log(`Exception creating PDF for building ${buildingId}`);
    console.log(e);
    status = 'page load error';
  }
  await browser.close();
  return status;
}

async function openCSV(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(filename, (err, data) => {
      csvParse(data.toString(), { columns: true }, (err, output) => {
        resolve(output);
      });
    });
  });
}

async function writeCSV(filename, records) {
  return new Promise((resolve, reject) => {
    csvStringify(records, { header: true }, (err, output) => {
      fs.writeFile(filename, output, err => {
        resolve();
      });
    });
  });
}

async function writeZip(outputDir) {
  return new Promise((resolve, reject) => {
    const outputPath = path.join(outputDir, outputZipFilename);
    var output = fs.createWriteStream(outputPath);
    var archive = archiver('zip', {
      zlib: { level: 9 }
    });

    output.on('close', () => resolve(outputPath));
    archive.on('warning', err => reject(err));
    archive.on('error', err => reject(err));
    archive.pipe(output);

    archive.glob('*.pdf', { cwd: outputDir });
    archive.glob(outputCSVFilename, { cwd: outputDir });

    archive.finalize();
  });
}

async function downloadPdfs(records, batchSize, baseUrl, outputDir, year) {
  const outputRecords = [];

  let batch = 1;
  while (batch <= Math.ceil(records.length / batchSize)) {
    const batchRecords = records.slice((batch - 1) * batchSize, batch * batchSize);

    await Promise.all(batchRecords.map(async record => {
      const status = await pageToPdf(record.property_id, baseUrl, outputDir, year);
      outputRecords.push(Object.assign({}, record, {
        pdf_filename: `${record.property_id}.pdf`,
        pdf_status: status
      }));
    }));

    batch++;
  }

  return outputRecords;
}

async function uploadToS3(filename, bucket) {
  return new Promise((resolve, reject) => {
    const s3 = new AWS.S3({
      apiVersion: '2006-03-01',
      params: { Bucket: bucket }
    });

    const params = {
      Key: `${moment().format('YYYYMMDDHHmmss')}-generated-pdfs.zip`,
      Body: fs.createReadStream(filename)
    };

    s3.upload(params, {}, (err, data) => {
      if (err) return reject(err);
      return resolve(data);
    });
  });
}

async function sendEmail(email, url) {
  const body = `Your scorecard PDFs are ready: ${url}`;
  return new Promise((resolve, reject) => {
    var params = {
      Destination: {
        CcAddresses: config.email.cc,
        ToAddresses: [email]
      },
      Message: {
        Body: {
          Html: {
            Data: body
          },
          Text: {
            Data: body
          }
        },
        Subject: {
          Data: 'City Energy Project: Scorecards generated'
        }
      },
      ReplyToAddresses: config.email.replyTo,
      ReturnPath: config.email.returnPath,
      Source: config.email.from,
      Tags: [{ Name: 'project', Value: 'cityenergyproject-seattle' }]
    };

    const ses = new AWS.SES({ apiVersion: '2010-12-01' });
    ses.sendEmail(params, function(err, data) {
      if (err) return reject(err);
      return resolve(data);
    });
  });
}

(async () => {
  commander
    .option('-i --input-csv <input-csv>', 'The CSV to read on input')
    .option('-y --year [year]', 'The year to get data for', '2017')
    .option('-b --batch-size [batch-size]', 'Batch size when downloading PDFs', 5)
    .option('-e --email [email]', 'Email address to notify on completion')
    .option('--base-url [base-url]', 'Base URL to get scorecards from', 'http://www.seattle.gov/energybenchmarkingmap')
    .option('-o --output-dir [output-dir]', 'The directory to put generated files in', '.')
    .option('--s3-bucket [s3-bucket]', 'S3 bucket to upload zip file to')
    .option('--upload-to-s3', 'Upload to S3')
    .parse(process.argv);

  if (!commander.inputCsv) {
    commander.help();
  }

  const records = await openCSV(commander.inputCsv);
  const outputRecords = await downloadPdfs(records, commander.batchSize, commander.baseUrl, commander.outputDir, commander.year);
  await writeCSV(path.join(commander.outputDir, outputCSVFilename), outputRecords);

  const zipPath = await writeZip(commander.outputDir);

  if (config.environment !== 'production') {
    AWS.config.loadFromPath('./config.json');
  }

  if (commander.uploadToS3 && config.environment === 'production' && commander.s3Bucket) {
    const s3data = await uploadToS3(zipPath, commander.s3Bucket);
    if (commander.email) {
      await sendEmail(commander.email, s3data.Location);
    }
  }
})();
<|endoftext|>


<commit_before>var user1, user2, currentSession, startTime, endTime, currentInterval,pairingDurationMs, timer, view, currentView;
var sessions = [];
var totalTimeWorking = 0;

function PairingSession(user1, user2){
  this.drive = user1;
  this.navigate = user2;
  this.timeWorked = 0;
  this.timePaused = 0;
}

function TotalSessionInfo(activeTime, pauseTime){
  this.totalTime = pauseTime + activeTime;
  this.activeTime = activeTime;
  this.pauseTime = pauseTime;
  this.user1 = user1;
  this.user2 = user2;
}

function getPopup(){
  var views = chrome.extension.getViews({ type: "popup" });
  view = views[0];
}

function setInterval(interval){
  currentInterval = interval;
  pairingDurationMs = currentInterval * 60000;
}
function pause(){
  if(timer){
    stopTimer();
  } else {
    console.log("unpause");
    startTimerCount();
  }
}

function endPairingSession(){
   if(currentSession.timeWorked === 0){
    endSession();
  }
  var activeTime = 0;
  var inActiveTime = 0;
  sessions.forEach(function(pairSession){
    activeTime += pairSession.timeWorked;
    inActiveTime += pairSession.timePaused;
  })
  stopTimer();
  sessions.push(new TotalSessionInfo(activeTime, inActiveTime));
  currentView = null;
}

function startPairing(){
  var date = new Date();
  startTime = date.getTime();
  currentSession = new PairingSession(user1, user2);
  console.log(currentSession);
  sessions.push(currentSession);
  console.log(sessions);
  startTimerCount();
  updateTimerCountdown();
}

function startTimerCount(){
  timer = setTimeout(function(){
    totalTimeWorking ++;
    currentInterval --;
    updateTimerCountdown();
    checkDuration();
    clearTimeout(timer);
    startTimerCount();
  }, 60000);
  console.log(timer);
}

function stopTimer(){
  console.log("timer", timer);
  clearInterval(timer);
  timer = null;
}

function checkDuration(){
  if(currentInterval === 0){
    endSession();
    alert('Switch it ^');
    startPairing();
  }
}

function endSession(){
  var date = new Date();
  endTime = date.getTime();
  currentInterval = pairingDurationMs / 60000;
  currentSession.timeWorked = currentInterval;
  currentSession.timePaused =  (endTime - startTime - (totalTimeWorking * 60000)) / 60000;
  if(currentSession.timePaused == null || currentSession.timePaused < .1){
    currentSession.timePaused = 0;
  }
  totalTimeWorking = 0;
}

function updateTimerCountdown(){
  view.document.getElementsByClassName('countdown').innerHTML =currentInterval;
}
<commit_msg>Add ajax to send data up to database<commit_after>var user1, user2, currentSession, startTime, endTime, currentInterval,pairingDurationMs, timer, view, currentView;
var sessions = [];
var totalTimeWorking = 0;

function PairingSession(user1, user2){
  this.drive = user1;
  this.navigate = user2;
  this.timeWorked = 0;
  this.timePaused = 0;
}

function TotalSessionInfo(activeTime, pauseTime){
  this.totalTime = pauseTime + activeTime;
  this.activeTime = activeTime;
  this.pauseTime = pauseTime;
  this.user1 = user1;
  this.user2 = user2;
}

function getPopup(){
  var views = chrome.extension.getViews({ type: "popup" });
  view = views[0];
}

function setInterval(interval){
  currentInterval = interval;
  pairingDurationMs = currentInterval * 60000;
}
function pause(){
  if(timer){
    stopTimer();
  } else {
    console.log("unpause");
    startTimerCount();
  }
}

function endPairingSession(){
   if(currentSession.timeWorked === 0){
    endSession();
  }
  var activeTime = 0;
  var inActiveTime = 0;
  sessions.forEach(function(pairSession){
    activeTime += pairSession.timeWorked;
    inActiveTime += pairSession.timePaused;
  })
  stopTimer();
  sessions.push(new TotalSessionInfo(activeTime, inActiveTime));
  currentView = null;
}

function startPairing(){
  var date = new Date();
  startTime = date.getTime();
  currentSession = new PairingSession(user1, user2);
  console.log(currentSession);
  sessions.push(currentSession);
  console.log(sessions);
  startTimerCount();
  updateTimerCountdown();
}

function startTimerCount(){
  timer = setTimeout(function(){
    totalTimeWorking ++;
    currentInterval --;
    updateTimerCountdown();
    checkDuration();
    clearTimeout(timer);
    startTimerCount();
  }, 60000);
  console.log(timer);
}

function stopTimer(){
  console.log("timer", timer);
  clearInterval(timer);
  timer = null;
}

function checkDuration(){
  if(currentInterval === 0){
    endSession();
    alert('Switch it ^');
    startPairing();
  }
}

function endSession(){
  var date = new Date();
  endTime = date.getTime();
  currentInterval = pairingDurationMs / 60000;
  currentSession.timeWorked = currentInterval;
  currentSession.timePaused =  (endTime - startTime - (totalTimeWorking * 60000)) / 60000;
  if(currentSession.timePaused == null || currentSession.timePaused < .1){
    currentSession.timePaused = 0;
  }
  totalTimeWorking = 0;
}

function updateTimerCountdown(){
  view.document.getElementsByClassName('countdown').innerHTML =currentInterval;
}

function sendInfoToDatabase(){
  $.ajax({
    url: 'http://localhost:9393/sessions/data',
    type: 'post',
    data: {sessions}
  })
  .done(function(data){
    console.log(data);
  })
}
<|endoftext|>