<commit_before>(function () {

    var defaults = {
        chart: {
            gridlines: 'none',
            padding: {
                top: 6,
                bottom: 25,
                left: 0,
                right: 5
            }
        },

        xAxis: {
            rangePadding: 0,
            innerTickSize: 0,
            outerTickSize: 0,
            tickPadding: 6,
            titlePadding: 4,
            firstAndLast: true,
            orient: 'bottom',
            labels: {
                // format: 'd'
            },
            linearDomain: false,     // specify if a time domain should be treated linearly or ....
        },

        yAxis: {
            min: 0,
            max: undefined,
            smartAxis: true,
            innerTickSize: 6,
            outerTickSize: 6,
            tickPadding: 4,
            titlePadding: 4,
            orient: 'left',
            labels: {
                align: 'middle',
                format: 's' // d3 formats
                // formatter

            }
        }
    };

    /*
    * Provides a cartesian frame to the Narwhal instance
    *
    * Example:
    *
    *     new Narwhal(options)
    *           .cartesian();
    *
    * Now new visualizations have accecss to the cartesian frame functionality
    * @name .cartesian()
    */
    var cartesian = {
        dataSrc: [],

        init: function (options) {

            this.options = _.merge({}, defaults, options);

            if (!this.options.xAxis.firstAndLast) {
                this.options.chart.padding.right += 15;
            }

            return this;
        },

        adjustPadding: function () {
            var options = this.options.yAxis;
            var yLabels = this.yDomain.slice().concat([_.nw.niceRound(this.yDomain[1])]);
            var format = d3.format(options.labels.format);
            var yAxisText = _.map(yLabels, format).join('
');
            var yLabelBounds = _.nw.textBounds(yAxisText, '.y.axis');
            var xLabelBounds = _.nw.textBounds('jgitlhHJKQWE', '.x.axis');
            var maxTickSize = function (options) { return Math.max(options.outerTickSize || 0, options.innerTickSize || 0); };

            this.options.chart.padding.left = maxTickSize(this.options.yAxis) + (this.options.yAxis.tickPadding || 0) + yLabelBounds.width;
            this.options.chart.padding.bottom = maxTickSize(this.options.xAxis) + (this.options.xAxis.tickPadding || 0) + xLabelBounds.height;
        },

        adjustTitlePadding: function () {
            var titleBounds;
            if (this.options.xAxis.title || this.options.yAxis.title) {
                if(this.options.xAxis.title) {
                    titleBounds = _.nw.textBounds(this.options.xAxis.title, '.x.axis-title');
                    this.options.chart.padding.bottom += titleBounds.height + this.options.xAxis.titlePadding;
                }

                if(this.options.yAxis.title) {
                    titleBounds = _.nw.textBounds(this.options.yAxis.title, '.y.axis-title');
                    this.options.chart.padding.left += titleBounds.height + this.options.yAxis.titlePadding;
                }
            }
        },

        computeXScale: function () {
            if (!this.xDomain) throw new Error('You are trying to render without setting data (xDomain).');

            this.xScaleGenerator = _.nw.xScaleFactory(this.dataSrc, this.options);
            this.xScale = this.xScaleGenerator.scale(this.xDomain);
            this.rangeBand = this.xScaleGenerator.rangeBand();
        },

        computeYScale: function () {
            if (!this.yDomain) throw new Error('You are trying to render without setting data (yDomain).');

            var yScaleDomain = _.nw.extractScaleDomain(this.yDomain, this.options.yAxis.min, this.options.yAxis.max);
            this.yScaleGenerator = _.nw.yScaleFactory(this.dataSrc, this.options, this.yMin, this.yMax);
            this.yScale = this.yScaleGenerator.scale(yScaleDomain);
        },

        /*
        * Provides a scaling function based on the xAxis values.
        *
        * Example:
        *
        *     var scaledValue = this.xScale(100);
        *
        * @function this.xScale()
        * @param {Number|String} value The value to be scaled
        * @return {Number} The scaled value according to the current xAxis settings
        */
        xScale: function(val) { return val; },

        /*
        * Provides a scaling function based on the xAxis values.
        *
        * Example:
        *
        *     var scaledValue = this.xScale(100);
        *
        * @function this.yScale()
        * @param {Number} value The value to be scaled
        * @return {Number} The scaled value according to the current yAxis settings
        */
        yScale: function(val) { return val; },

        /*
        * Modifies the domain for the y axis.
        *
        * Example:
        *
        *     this.setYDomain([100, 200]);
        *
        * @param {Array} domain The domain array represeting the min and max values of to be visible in the y Axis
        */
        setYDomain: function (domain) {
            this.yScaleGenerator.setDomain(domain);
        },

        /*
        * Redraws the yAxis with the new settings and domain
        *
        * Example:
        *
        *     this.redrawYAxis(;
        *
        */
        redrawYAxis: function () {
            this.svg.select(".y.axis").call(this.yAxis());
            this.renderGridlines();
        },

        computeScales: function () {
            this.computeXScale();
            this.computeYScale();

            return this;
        },

        xAxis: function () {
            return this.xScaleGenerator.axis().orient(this.options.xAxis.orient);
        },

        yAxis: function () {
            return this.yScaleGenerator.axis().orient(this.options.yAxis.orient);
        },

        renderXAxis: function () {
            var xAxis = this.xAxis();
            var y = this.options.chart.plotHeight + this.options.chart.padding.top;
            this._xAxisGroup = this.svg
                .append('g')
                .attr('class', 'x axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + y + ')')
                .call(xAxis);

            this.xScaleGenerator.postProcessAxis(this._xAxisGroup);

            return this;
        },

        renderYAxis: function () {
            var options = this.options.yAxis;
            var alignmentOffset = { top: '.8em', middle: '.35em', bottom: '0' };
            var x = this.options.chart.padding.left;
            var y = this.options.chart.padding.top;
            var yAxis = this.yAxis();

            this._yAxisGroup = this.svg
                .append('g')
                .attr('class', 'y axis')
                .attr('transform', 'translate(' + x + ',' + y + ')')
                .call(yAxis);

            this._yAxisGroup
                .selectAll('text')
                    .attr('dy', alignmentOffset[options.labels.align]);

            return this;
        },

        renderAxisLabels: function () {
            var lineHeightAdjustment = this.titleOneEm * 0.25; // add 25% of font-size for a complete line-height
            var adjustFactor = 40/46.609; // this factor is to account for the difference between the actual svg size and what we get from the DOM
            var bounds, x, y;

            if (this.options.xAxis.title) {
                bounds = _.nw.textBounds(this.options.xAxis.title, '.x.axis-title');
                y = this.options.chart.padding.bottom;
                x = 0;
                this._xAxisGroup.append('text')
                    .attr('class', 'x axis-title')
                    .attr('x', x)
                    .attr('y', y)
                    .attr('dx', (this.options.chart.plotWidth - bounds.width) / 2)
                    .attr('dy', -2) // just because
                    .text(this.options.xAxis.title);
            }

            if (this.options.yAxis.title) {
                bounds = _.nw.textBounds(this.options.yAxis.title, '.y.axis-title');
                y = -this.options.chart.padding.left + bounds.height * adjustFactor;
                x = 0;
                this._yAxisGroup.append('text')
                    .attr('class', 'y axis-title')
                    .attr('transform', 'rotate(-90)')
                    .attr('x', x)
                    .attr('y', y)
                    .attr('dx', -(this.options.chart.plotHeight + bounds.width) / 2)
                    .attr('dy', 0)
                    .text(this.options.yAxis.title);
            }
        },

        renderGridlines: function () {
            var option = this.options.chart.gridlines;
            var horizontal = option === 'horizontal' || option === 'both';
            var vertical = option === 'vertical' || option === 'both';

            function getYTicks(axis, smart) {
                var tickValues = axis.tickValues();

                if(!tickValues) {
                    var numTicks = axis.ticks()[0];
                    return axis.scale().ticks(numTicks).slice(1);
                }

                smart && tickValues.pop();

                return tickValues.slice(1);
            }

            function getXTicks(axis) {
                return axis.tickValues() || (axis.scale().ticks ? axis.scale().ticks().slice(1) : axis.scale().domain());
            }

            var ticks, gr;
            var x = this.xScale;
            var y = this.yScale;

            if(horizontal) {

                // remove previous lines (TODO: we need a better way)
                this._yAxisGroup.select('g.grid-lines').remove();
                gr = this._yAxisGroup
                    .append('svg:g')
                    .attr('class', 'grid-lines');

                ticks = getYTicks(this.yAxis(), this.options.yAxis.smartAxis);
                var w = this.options.chart.plotWidth;

                gr.selectAll('.grid-line')
                    .data(ticks)
                    .enter().append('line')
                        .attr('class', 'grid-line')
                        .attr('x1', 0)
                        .attr('x2', w)
                        .attr('y1', y)
                        .attr('y2', y);
            }

            if(vertical) {
                // remove previous lines (TODO: we need a better way)
                this._xAxisGroup.select('g.grid-lines').remove();
                gr = this._xAxisGroup.append('svg:g').attr('class', 'grid-lines');
                ticks = getXTicks(this.xAxis());
                var offset = this.rangeBand / 2;
                var h = this.options.chart.plotHeight;

                gr.selectAll('.grid-line')
                    .data(ticks)
                    .enter().append('line')
                        .attr('class', 'grid-line')
                        .attr('x1', function (d) { return x(d) + offset; })
                        .attr('x2', function (d) { return x(d) + offset; })
                        .attr('y1', -h)
                        .attr('y2', 0);
            }

            return this;
        },

        render: function () {
            this.composeOptions();

            this.adjustDomain();

            this.calcMetrics();

            this.computeScales();

            this.baseRender();

            this.renderXAxis()
                .renderYAxis()
                .renderGridlines()
                .renderAxisLabels();

            this.renderVisualizations();

            return this;
        },

        datum: function (d, index) {
            if(_.isObject(d) && _.isArray(d.data))
                return _.map(d.data, _.bind(this.datum, this));

            return {
                y: _.isObject(d) ? d.y : d,
                x: _.isObject(d) ? d.x : this.options.xAxis.categories ? this.options.xAxis.categories[index] : index
            };
        },

        data: function (series) {

            if (series instanceof Array && !series.length) {
                return this;
            } else if (series instanceof Array && !series[0].data) {
                this.dataSrc = _.map(series, _.bind(this.datum, this));
                this.xDomain = this.extractXDomain(this.dataSrc);
                this.yDomain = this.options.chart.stacked ? this.extractYStackedDomain(this.dataSrc) : this.extractYDomain(this.dataSrc);
                this.yMax = this.getYAxisDataMax(this.dataSrc);
                this.yMin = this.getYAxisDataMin(this.dataSrc);
            } else if (series instanceof Array && series[0].data) {
                // we have an array of series
                _.each(series, function (set) { this.data(set.data); }, this);
            }

            return this;
        },

        getYAxisDataMax: function (data) {
            return Math.max(this.yMax || 0, _.max(_.pluck(data, 'y')));
        },

        getYAxisDataMin: function (data) {
            return Math.min(this.yMin || 0, _.min(_.pluck(data, 'y')));
        },

        extractXDomain: function(data) {
            return  this.options.xAxis.categories ? this.options.xAxis.categories : _.pluck(data, 'x');
        },

        extractYDomain: function (data) {
            var max = this.yDomain ? Math.max(this.yDomain[1], _.max(_.pluck(data, 'y'))) : _.max(_.pluck(data, 'y'));
            var min = this.yDomain ? Math.min(this.yDomain[0], _.min(_.pluck(data, 'y'))) : _.min(_.pluck(data, 'y'));

            return [min, max];
        },

        extractYStackedDomain: function (data) {
            var dataSets = _.pluck(data, 'data');
            var maxLength = _.max(dataSets, function (d) { return d.length; });
            var stackY = [];

            for (var j=0; j<maxLength; j++) {
                _.each(dataSets, function (datum) {
                    stackY[j] = datum && datum.y ? (stackY[j] || 0) + datum.y : (stackY[j] || 0);
                });
            }

            return [_.min(stackY), _.max(stackY)];
        },

        adjustDomain: function () {
            this.yDomain = this.yDomain ? this.yDomain : [0, 10];
            this.xDomain = this.xDomain ? this.xDomain : [];
        }
    };

    Narwhal.expose('cartesian', cartesian);

})();
<commit_msg>Consider min and max options for yAxis padding<commit_after>(function () {

    var defaults = {
        chart: {
            gridlines: 'none',
            padding: {
                top: 6,
                bottom: 25,
                left: 0,
                right: 5
            }
        },

        xAxis: {
            rangePadding: 0,
            innerTickSize: 0,
            outerTickSize: 0,
            tickPadding: 6,
            titlePadding: 4,
            firstAndLast: true,
            orient: 'bottom',
            labels: {
                // format: 'd'
            },
            linearDomain: false,     // specify if a time domain should be treated linearly or ....
        },

        yAxis: {
            min: 0,
            max: undefined,
            smartAxis: true,
            innerTickSize: 6,
            outerTickSize: 6,
            tickPadding: 4,
            titlePadding: 4,
            orient: 'left',
            labels: {
                align: 'middle',
                format: 's' // d3 formats
                // formatter

            }
        }
    };

    /*
    * Provides a cartesian frame to the Narwhal instance
    *
    * Example:
    *
    *     new Narwhal(options)
    *           .cartesian();
    *
    * Now new visualizations have accecss to the cartesian frame functionality
    * @name .cartesian()
    */
    var cartesian = {
        dataSrc: [],

        init: function (options) {

            this.options = _.merge({}, defaults, options);

            if (!this.options.xAxis.firstAndLast) {
                this.options.chart.padding.right += 15;
            }

            return this;
        },

        adjustPadding: function () {
            var options = this.options.yAxis;
            var yLabels = _.nw.extractScaleDomain(this.yDomain.slice().concat([_.nw.niceRound(this.yDomain[1])]), options.min, options.max);
            var format = options.labels.formatter || d3.format(options.labels.format || ',.0f');
            var yAxisText = _.map(yLabels, format).join('
');
            var yLabelBounds = _.nw.textBounds(yAxisText, '.y.axis');
            var xLabelBounds = _.nw.textBounds('jgitlhHJKQWE', '.x.axis');
            var maxTickSize = function (options) { return Math.max(options.outerTickSize || 0, options.innerTickSize || 0); };

            this.options.chart.padding.left = maxTickSize(this.options.yAxis) + (this.options.yAxis.tickPadding || 0) + yLabelBounds.width;
            this.options.chart.padding.bottom = maxTickSize(this.options.xAxis) + (this.options.xAxis.tickPadding || 0) + xLabelBounds.height;
        },

        adjustTitlePadding: function () {
            var titleBounds;
            if (this.options.xAxis.title || this.options.yAxis.title) {
                if(this.options.xAxis.title) {
                    titleBounds = _.nw.textBounds(this.options.xAxis.title, '.x.axis-title');
                    this.options.chart.padding.bottom += titleBounds.height + this.options.xAxis.titlePadding;
                }

                if(this.options.yAxis.title) {
                    titleBounds = _.nw.textBounds(this.options.yAxis.title, '.y.axis-title');
                    this.options.chart.padding.left += titleBounds.height + this.options.yAxis.titlePadding;
                }
            }
        },

        computeXScale: function () {
            if (!this.xDomain) throw new Error('You are trying to render without setting data (xDomain).');

            this.xScaleGenerator = _.nw.xScaleFactory(this.dataSrc, this.options);
            this.xScale = this.xScaleGenerator.scale(this.xDomain);
            this.rangeBand = this.xScaleGenerator.rangeBand();
        },

        computeYScale: function () {
            if (!this.yDomain) throw new Error('You are trying to render without setting data (yDomain).');

            var yScaleDomain = _.nw.extractScaleDomain(this.yDomain, this.options.yAxis.min, this.options.yAxis.max);
            this.yScaleGenerator = _.nw.yScaleFactory(this.dataSrc, this.options, this.yMin, this.yMax);
            this.yScale = this.yScaleGenerator.scale(yScaleDomain);
        },

        /*
        * Provides a scaling function based on the xAxis values.
        *
        * Example:
        *
        *     var scaledValue = this.xScale(100);
        *
        * @function this.xScale()
        * @param {Number|String} value The value to be scaled
        * @return {Number} The scaled value according to the current xAxis settings
        */
        xScale: function(val) { return val; },

        /*
        * Provides a scaling function based on the xAxis values.
        *
        * Example:
        *
        *     var scaledValue = this.xScale(100);
        *
        * @function this.yScale()
        * @param {Number} value The value to be scaled
        * @return {Number} The scaled value according to the current yAxis settings
        */
        yScale: function(val) { return val; },

        /*
        * Modifies the domain for the y axis.
        *
        * Example:
        *
        *     this.setYDomain([100, 200]);
        *
        * @param {Array} domain The domain array represeting the min and max values of to be visible in the y Axis
        */
        setYDomain: function (domain) {
            this.yScaleGenerator.setDomain(domain);
        },

        /*
        * Redraws the yAxis with the new settings and domain
        *
        * Example:
        *
        *     this.redrawYAxis(;
        *
        */
        redrawYAxis: function () {
            this.svg.select(".y.axis").call(this.yAxis());
            this.renderGridlines();
        },

        computeScales: function () {
            this.computeXScale();
            this.computeYScale();

            return this;
        },

        xAxis: function () {
            return this.xScaleGenerator.axis().orient(this.options.xAxis.orient);
        },

        yAxis: function () {
            return this.yScaleGenerator.axis().orient(this.options.yAxis.orient);
        },

        renderXAxis: function () {
            var xAxis = this.xAxis();
            var y = this.options.chart.plotHeight + this.options.chart.padding.top;
            this._xAxisGroup = this.svg
                .append('g')
                .attr('class', 'x axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + y + ')')
                .call(xAxis);

            this.xScaleGenerator.postProcessAxis(this._xAxisGroup);

            return this;
        },

        renderYAxis: function () {
            var options = this.options.yAxis;
            var alignmentOffset = { top: '.8em', middle: '.35em', bottom: '0' };
            var x = this.options.chart.padding.left;
            var y = this.options.chart.padding.top;
            var yAxis = this.yAxis();

            this._yAxisGroup = this.svg
                .append('g')
                .attr('class', 'y axis')
                .attr('transform', 'translate(' + x + ',' + y + ')')
                .call(yAxis);

            this._yAxisGroup
                .selectAll('text')
                    .attr('dy', alignmentOffset[options.labels.align]);

            return this;
        },

        renderAxisLabels: function () {
            var lineHeightAdjustment = this.titleOneEm * 0.25; // add 25% of font-size for a complete line-height
            var adjustFactor = 40/46.609; // this factor is to account for the difference between the actual svg size and what we get from the DOM
            var bounds, x, y;

            if (this.options.xAxis.title) {
                bounds = _.nw.textBounds(this.options.xAxis.title, '.x.axis-title');
                y = this.options.chart.padding.bottom;
                x = 0;
                this._xAxisGroup.append('text')
                    .attr('class', 'x axis-title')
                    .attr('x', x)
                    .attr('y', y)
                    .attr('dx', (this.options.chart.plotWidth - bounds.width) / 2)
                    .attr('dy', -2) // just because
                    .text(this.options.xAxis.title);
            }

            if (this.options.yAxis.title) {
                bounds = _.nw.textBounds(this.options.yAxis.title, '.y.axis-title');
                y = -this.options.chart.padding.left + bounds.height * adjustFactor;
                x = 0;
                this._yAxisGroup.append('text')
                    .attr('class', 'y axis-title')
                    .attr('transform', 'rotate(-90)')
                    .attr('x', x)
                    .attr('y', y)
                    .attr('dx', -(this.options.chart.plotHeight + bounds.width) / 2)
                    .attr('dy', 0)
                    .text(this.options.yAxis.title);
            }
        },

        renderGridlines: function () {
            var option = this.options.chart.gridlines;
            var horizontal = option === 'horizontal' || option === 'both';
            var vertical = option === 'vertical' || option === 'both';

            function getYTicks(axis, smart) {
                var tickValues = axis.tickValues();

                if(!tickValues) {
                    var numTicks = axis.ticks()[0];
                    return axis.scale().ticks(numTicks).slice(1);
                }

                smart && tickValues.pop();

                return tickValues.slice(1);
            }

            function getXTicks(axis) {
                return axis.tickValues() || (axis.scale().ticks ? axis.scale().ticks().slice(1) : axis.scale().domain());
            }

            var ticks, gr;
            var x = this.xScale;
            var y = this.yScale;

            if(horizontal) {

                // remove previous lines (TODO: we need a better way)
                this._yAxisGroup.select('g.grid-lines').remove();
                gr = this._yAxisGroup
                    .append('svg:g')
                    .attr('class', 'grid-lines');

                ticks = getYTicks(this.yAxis(), this.options.yAxis.smartAxis);
                var w = this.options.chart.plotWidth;

                gr.selectAll('.grid-line')
                    .data(ticks)
                    .enter().append('line')
                        .attr('class', 'grid-line')
                        .attr('x1', 0)
                        .attr('x2', w)
                        .attr('y1', y)
                        .attr('y2', y);
            }

            if(vertical) {
                // remove previous lines (TODO: we need a better way)
                this._xAxisGroup.select('g.grid-lines').remove();
                gr = this._xAxisGroup.append('svg:g').attr('class', 'grid-lines');
                ticks = getXTicks(this.xAxis());
                var offset = this.rangeBand / 2;
                var h = this.options.chart.plotHeight;

                gr.selectAll('.grid-line')
                    .data(ticks)
                    .enter().append('line')
                        .attr('class', 'grid-line')
                        .attr('x1', function (d) { return x(d) + offset; })
                        .attr('x2', function (d) { return x(d) + offset; })
                        .attr('y1', -h)
                        .attr('y2', 0);
            }

            return this;
        },

        render: function () {
            this.composeOptions();

            this.adjustDomain();

            this.calcMetrics();

            this.computeScales();

            this.baseRender();

            this.renderXAxis()
                .renderYAxis()
                .renderGridlines()
                .renderAxisLabels();

            this.renderVisualizations();

            return this;
        },

        datum: function (d, index) {
            if(_.isObject(d) && _.isArray(d.data))
                return _.map(d.data, _.bind(this.datum, this));

            return {
                y: _.isObject(d) ? d.y : d,
                x: _.isObject(d) ? d.x : this.options.xAxis.categories ? this.options.xAxis.categories[index] : index
            };
        },

        data: function (series) {

            if (series instanceof Array && !series.length) {
                return this;
            } else if (series instanceof Array && !series[0].data) {
                this.dataSrc = _.map(series, _.bind(this.datum, this));
                this.xDomain = this.extractXDomain(this.dataSrc);
                this.yDomain = this.options.chart.stacked ? this.extractYStackedDomain(this.dataSrc) : this.extractYDomain(this.dataSrc);
                this.yMax = this.getYAxisDataMax(this.dataSrc);
                this.yMin = this.getYAxisDataMin(this.dataSrc);
            } else if (series instanceof Array && series[0].data) {
                // we have an array of series
                _.each(series, function (set) { this.data(set.data); }, this);
            }

            return this;
        },

        getYAxisDataMax: function (data) {
            return Math.max(this.yMax || 0, _.max(_.pluck(data, 'y')));
        },

        getYAxisDataMin: function (data) {
            return Math.min(this.yMin || 0, _.min(_.pluck(data, 'y')));
        },

        extractXDomain: function(data) {
            return  this.options.xAxis.categories ? this.options.xAxis.categories : _.pluck(data, 'x');
        },

        extractYDomain: function (data) {
            var max = this.yDomain ? Math.max(this.yDomain[1], _.max(_.pluck(data, 'y'))) : _.max(_.pluck(data, 'y'));
            var min = this.yDomain ? Math.min(this.yDomain[0], _.min(_.pluck(data, 'y'))) : _.min(_.pluck(data, 'y'));

            return [min, max];
        },

        extractYStackedDomain: function (data) {
            var dataSets = _.pluck(data, 'data');
            var maxLength = _.max(dataSets, function (d) { return d.length; });
            var stackY = [];

            for (var j=0; j<maxLength; j++) {
                _.each(dataSets, function (datum) {
                    stackY[j] = datum && datum.y ? (stackY[j] || 0) + datum.y : (stackY[j] || 0);
                });
            }

            return [_.min(stackY), _.max(stackY)];
        },

        adjustDomain: function () {
            this.yDomain = this.yDomain ? this.yDomain : [0, 10];
            this.xDomain = this.xDomain ? this.xDomain : [];
        }
    };

    Narwhal.expose('cartesian', cartesian);

})();
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'

import { cachedResource } from '../services/resources'
import { fetchResource } from '../actions/resources'
import { LocationMap } from '../components'
import { numberToSlotSizeStr } from '../utils/slots'
import { translate } from '../services/i18n'

class PlacesShow extends Component {
  static propTypes = {
    fetchResource: PropTypes.func.isRequired,
    isLoading: PropTypes.bool.isRequired,
    resourceSlug: PropTypes.string.isRequired,
    resourceData: PropTypes.object.isRequired,
  }

  componentWillMount() {
    this.props.fetchResource('places', this.props.resourceSlug)
  }

  renderPrivacy() {
    if (this.props.resourceData.isPublic) {
      return null
    }
    return (

        { translate('components.placeListItem.isPrivatePlace') }

    )
  }

  renderSlotSize() {
    return (

        Slot-size (hh:mm)

{ numberToSlotSizeStr(this.props.resourceData.slotSize) }

    )
  }

  renderAddress() {
    const {
      city,
      cityCode,
      country,
      latitude,
      longitude,
      mode,
      street,
    } = this.props.resourceData

    if (mode === 'gps') {
      return (

          Location

@ { latitude }, { longitude }

          <LocationMap latitude={latitude} longitude={longitude} />

      )
    } else if (mode === 'address') {
      return (

          Location

            { street }

            { `${cityCode} ${city}` }

            { country }

      )
    }
    return (

        Location

{ translate('components.placeListItem.virtualLocation') }

    )
  }

  renderDescription() {
    return (

        Description

    )
  }

  renderOwner() {
    const { name, slug } = this.props.resourceData.animal

    return (

        { translate('views.places.owner') }

        <Link to={`animals/${slug}`}>
          { name }
        </Link>

    )
  }

  renderContent() {
    if (this.props.isLoading) {
      return
{ translate('components.common.loading') }

    }

    return (

        { this.renderOwner() }
        { this.renderPrivacy() }

        { this.renderAddress() }

        { this.renderSlotSize() }
        { this.renderDescription() }

    )
  }

  renderTitle() {
    if (this.props.isLoading || !this.props.resourceData) {
      return
{ translate('views.places.titlePlaceholder') }

    }
    return
{ this.props.resourceData.title }

  }

  render() {
    return (

        { this.renderTitle() }
        <Link className="button" to="/places">
          { translate('views.places.backToOverview') }
        </Link>

        { this.renderContent() }

    )
  }

  constructor(props) {
    super(props)
  }
}

function mapStateToProps(state, ownProps) {
  const resourceSlug = ownProps.match.params.slug
  const resource = cachedResource('places', resourceSlug)
  const { isLoading, object } = resource

  return {
    isLoading,
    resourceData: object,
    resourceSlug,
  }
}

export default connect(
  mapStateToProps, {
    fetchResource,
  }
)(PlacesShow)
<commit_msg>Fix animal link<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'

import { cachedResource } from '../services/resources'
import { fetchResource } from '../actions/resources'
import { LocationMap } from '../components'
import { numberToSlotSizeStr } from '../utils/slots'
import { translate } from '../services/i18n'

class PlacesShow extends Component {
  static propTypes = {
    fetchResource: PropTypes.func.isRequired,
    isLoading: PropTypes.bool.isRequired,
    resourceSlug: PropTypes.string.isRequired,
    resourceData: PropTypes.object.isRequired,
  }

  componentWillMount() {
    this.props.fetchResource('places', this.props.resourceSlug)
  }

  renderPrivacy() {
    if (this.props.resourceData.isPublic) {
      return null
    }
    return (

        { translate('components.placeListItem.isPrivatePlace') }

    )
  }

  renderSlotSize() {
    return (

        Slot-size (hh:mm)

{ numberToSlotSizeStr(this.props.resourceData.slotSize) }

    )
  }

  renderAddress() {
    const {
      city,
      cityCode,
      country,
      latitude,
      longitude,
      mode,
      street,
    } = this.props.resourceData

    if (mode === 'gps') {
      return (

          Location

@ { latitude }, { longitude }

          <LocationMap latitude={latitude} longitude={longitude} />

      )
    } else if (mode === 'address') {
      return (

          Location

            { street }

            { `${cityCode} ${city}` }

            { country }

      )
    }
    return (

        Location

{ translate('components.placeListItem.virtualLocation') }

    )
  }

  renderDescription() {
    return (

        Description

    )
  }

  renderOwner() {
    const { name, slug } = this.props.resourceData.animal

    return (

        { translate('views.places.owner') }

        <Link to={`/animals/${slug}`}>
          { name }
        </Link>

    )
  }

  renderContent() {
    if (this.props.isLoading) {
      return
{ translate('components.common.loading') }

    }

    return (

        { this.renderOwner() }
        { this.renderPrivacy() }

        { this.renderAddress() }

        { this.renderSlotSize() }
        { this.renderDescription() }

    )
  }

  renderTitle() {
    if (this.props.isLoading || !this.props.resourceData) {
      return
{ translate('views.places.titlePlaceholder') }

    }
    return
{ this.props.resourceData.title }

  }

  render() {
    return (

        { this.renderTitle() }
        <Link className="button" to="/places">
          { translate('views.places.backToOverview') }
        </Link>

        { this.renderContent() }

    )
  }

  constructor(props) {
    super(props)
  }
}

function mapStateToProps(state, ownProps) {
  const resourceSlug = ownProps.match.params.slug
  const resource = cachedResource('places', resourceSlug)
  const { isLoading, object } = resource

  return {
    isLoading,
    resourceData: object,
    resourceSlug,
  }
}

export default connect(
  mapStateToProps, {
    fetchResource,
  }
)(PlacesShow)
<|endoftext|>


<commit_before>import { alias } from '@ember/object/computed';
import Service, { inject as service } from '@ember/service';

import { task, waitForEvent } from 'ember-concurrency';
import window from 'ember-window-mock';

import ajax from '../utils/ajax';
import * as localStorage from '../utils/local-storage';

export default class SessionService extends Service {
  @service store;
  @service notifications;
  @service router;

  savedTransition = null;

  @alias('loadUserTask.last.value.currentUser') currentUser;
  @alias('loadUserTask.last.value.ownedCrates') ownedCrates;

  get isLoggedIn() {
    return localStorage.getItem('isLoggedIn') === '1';
  }

  set isLoggedIn(value) {
    if (value) {
      localStorage.setItem('isLoggedIn', '1');
    } else {
      localStorage.removeItem('isLoggedIn');
    }
  }

  /**
   * This task will open a popup window directed at the `github-login` route.
   * After the window has opened it will wait for the window to send a message
   * back and then evaluate whether the OAuth flow was successful.
   *
   * @see `github-authorize` route
   */
  @task(function* () {
    let windowDimensions = [
      'width=1000',
      'height=450',
      'toolbar=0',
      'scrollbars=1',
      'status=1',
      'resizable=1',
      'location=1',
      'menuBar=0',
    ].join(',');

    let win = window.open('/github_login', 'Authorization', windowDimensions);
    if (!win) {
      return;
    }

    let event = yield waitForEvent(window, 'message');
    if (event.origin !== window.location.origin || !event.data) {
      return;
    }

    let { data } = event.data;
    if (data && data.errors) {
      this.notifications.error(`Failed to log in: ${data.errors[0].detail}`);
      return;
    } else if (!event.data.ok) {
      this.notifications.error('Failed to log in');
      return;
    }

    this.login();
  })
  loginTask;

  login() {
    this.isLoggedIn = true;

    // just trigger the task, but don't wait for the result here
    this.loadUserTask.perform();

    // perform the originally saved transition, if it exists
    let transition = this.savedTransition;
    if (transition) {
      transition.retry();
    }
  }

  @task(function* () {
    yield ajax(`/api/private/session`, { method: 'DELETE' });

    this.savedTransition = null;
    this.isLoggedIn = false;

    this.loadUserTask.cancelAll({ resetState: true });

    this.router.transitionTo('index');
  })
  logoutTask;

  @(task(function* () {
    if (!this.isLoggedIn) return {};

    let response;
    try {
      response = yield ajax('/api/v1/me');
    } catch (error) {
      return {};
    }

    let currentUser = this.store.push(this.store.normalize('user', response.user));
    let ownedCrates = response.owned_crates.map(c => this.store.push(this.store.normalize('owned-crate', c)));

    return { currentUser, ownedCrates };
  }).drop())
  loadUserTask;
}
<commit_msg>services/session: Inline `login()` method<commit_after>import { alias } from '@ember/object/computed';
import Service, { inject as service } from '@ember/service';

import { task, waitForEvent } from 'ember-concurrency';
import window from 'ember-window-mock';

import ajax from '../utils/ajax';
import * as localStorage from '../utils/local-storage';

export default class SessionService extends Service {
  @service store;
  @service notifications;
  @service router;

  savedTransition = null;

  @alias('loadUserTask.last.value.currentUser') currentUser;
  @alias('loadUserTask.last.value.ownedCrates') ownedCrates;

  get isLoggedIn() {
    return localStorage.getItem('isLoggedIn') === '1';
  }

  set isLoggedIn(value) {
    if (value) {
      localStorage.setItem('isLoggedIn', '1');
    } else {
      localStorage.removeItem('isLoggedIn');
    }
  }

  /**
   * This task will open a popup window directed at the `github-login` route.
   * After the window has opened it will wait for the window to send a message
   * back and then evaluate whether the OAuth flow was successful.
   *
   * @see `github-authorize` route
   */
  @task(function* () {
    let windowDimensions = [
      'width=1000',
      'height=450',
      'toolbar=0',
      'scrollbars=1',
      'status=1',
      'resizable=1',
      'location=1',
      'menuBar=0',
    ].join(',');

    let win = window.open('/github_login', 'Authorization', windowDimensions);
    if (!win) {
      return;
    }

    let event = yield waitForEvent(window, 'message');
    if (event.origin !== window.location.origin || !event.data) {
      return;
    }

    let { data } = event.data;
    if (data && data.errors) {
      this.notifications.error(`Failed to log in: ${data.errors[0].detail}`);
      return;
    } else if (!event.data.ok) {
      this.notifications.error('Failed to log in');
      return;
    }

    this.isLoggedIn = true;

    // just trigger the task, but don't wait for the result here
    this.loadUserTask.perform();

    // perform the originally saved transition, if it exists
    let transition = this.savedTransition;
    if (transition) {
      transition.retry();
    }
  })
  loginTask;

  @task(function* () {
    yield ajax(`/api/private/session`, { method: 'DELETE' });

    this.savedTransition = null;
    this.isLoggedIn = false;

    this.loadUserTask.cancelAll({ resetState: true });

    this.router.transitionTo('index');
  })
  logoutTask;

  @(task(function* () {
    if (!this.isLoggedIn) return {};

    let response;
    try {
      response = yield ajax('/api/v1/me');
    } catch (error) {
      return {};
    }

    let currentUser = this.store.push(this.store.normalize('user', response.user));
    let ownedCrates = response.owned_crates.map(c => this.store.push(this.store.normalize('owned-crate', c)));

    return { currentUser, ownedCrates };
  }).drop())
  loadUserTask;
}
<|endoftext|>


<commit_before>  // Single var pattern of gulp (require) stuff in full effect!!!

var gulp = require("gulp"), // Bring in gulp
    exec = require('child_process').exec, // Run CLI commands via node
    Q = require('q'), // Manage promises
    jade = require("gulp-jade"), // Jade task
    uncss = require("gulp-uncss"), // Remove unused css selectors
    minifyCSS = require("gulp-minify-css"), // Minify CSS
    autoprefixer = require('gulp-autoprefixer'), // Vendor prefixes
    csslint = require("gulp-csslint"), // Lint CSS
    concatCss = require('gulp-concat-css'), // Concatenating .css files
    imagemin = require('gulp-imagemin'), // Minifying images
    watch = require("gulp-watch"), // Watch files changes
    connect = require("gulp-connect"); // Livereload on port 8080

// End single var pattern

// Needed to run grunt tasks through gulp
require("gulp-grunt")(gulp);

/*
 *  ===================================================================
 *  | STORE PREPROCESSOR FILE REFERENCES IN VARIABLES |
 *  ===================================================================
 */
var jadeFiles = ["jade/index.jade", "jade/**/*.jade"], // Jade
    lessFiles = ["css-build/*.less", "css-build/**/*.less"], // LESS
    coffeeFiles = ["coffee/*.coffee"]; // Coffeescript

    /*
     * IGNORE ARRAY
     * ============
     * Selectors NOT to be removed when "gulp uncss" task runs.
     * Try to list them alphabetically and in the following order:
     *
     * 1. Page elements first (<nav>, <aside>, etc.)
     * 2. IDs second
     * 3. Classes third
     */
    ignoreArray = [
      "nav",
      "ol",
      "ul",
      ".all-player-info",
      ".btn",
      ".btn-info",
      ".cycle-slide",
      ".player-info",
      ".player-name",
      ".scroll-nav",
      ".scroll-nav__item",
      ".scroll-nav__link",
      ".scroll-nav__list",
      ".scroll-nav__section",
      ".team-copy-popup"
    ];

/*
 *  ===================================================================
 *  | JADE TASK |
 *  ===================================================================
 */
// Output Jade Files to build "build/index.html"
gulp.task("jade", function () {
  return gulp.src("jade/index.jade")
  .pipe(jade({
    pretty: true
    }))
  .pipe(gulp.dest("build"))
  .pipe(connect.reload());
  });

/*
 *  ===================================================================
 *  | CSS BUILD TASK |
 *
 * Running "gulp buildcss" performs the following sequence of tasks...
 *
 * 1. "gulp less" task
 * 2. "gulp concat" task that takes "gulp less" as a hint(*)
 * 3. "gulp outputCss" task that takes "gulp concat" as a hint(*)
 * 4. "gulp outputCss" removes unused CSS, minifies, lints, &
      auto-prefixes it
 * 5. "gulp jade" which rebuilds/resets build/index.html
 * 6. "gulp critical" to extract critical path CSS to build/index.html
 *
 * Some tasks return q promises so all files are ready for a given task
 *
 * (*) gulp "hints" are cool...read more about them at:
 * https://github.com/gulpjs/gulp/blob/master/docs/API.md
 *  ===================================================================
 */

// The core task for building out the site's CSS
gulp.task('buildcss', ['outputCss', 'jade', 'critical']);

// "gulp less" task
// ================
// Process .less files using node exec
// Returns a promise with q
gulp.task("less", function () {
  var deferred = Q.defer();

  setTimeout(function() {
    exec("lessc css-build/styles.less > css-build/styles.css");
    return deferred.promise;
  }, 1000);
});

// "gulp concat" task
// ==================
// Concatenate "css-build/styles.css" and "css-build/bootstrap.css"
// Takes "less" as a gulp hint
// Returns a promise with q
gulp.task('concat', ['less'], function() {
  var deferred = Q.defer();
  setTimeout(function() {
    return gulp.src(['css-build/bootstrap.css', 'css-build/styles.css'])
    .pipe(concatCss('styles.min.css'))
    .pipe(gulp.dest('build/css/'));
    return deferred.promise;
  }, 1000);
});

// "gulp outputCss" task
// =====================
// Concatenate "css-build/styles.css" and "css-build/bootstrap.css"
// Takes "concat" as a gulp hint
// Returns a promise with q
gulp.task("outputCss", ['concat'],function () {

 // Set this up so "outputCss" returns a promise
 var deferred = Q.defer();

  setTimeout(function() {
    gulp.src('build/css/styles.min.css')
    .pipe(uncss({
      html: ["build/index.html"],
      ignore: ignoreArray
    }))
    .pipe(autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
    }))
    .pipe(minifyCSS())
    .pipe(gulp.dest("build/css/"))
    .pipe(csslint({
      "important": false,
      "duplicate-background-images": false,
      "ids": false,
      "text-indent": false
    }))
    .pipe(csslint.reporter())
    .pipe(connect.reload())
   }, 4000);
  return deferred.promise;
});

// "gulp critical" task
// =====================
// Extract critical path css to "build/index.html" with a grunt task
// Returns a promise with q
gulp.task("critical", function () {
  var deferred = Q.defer();
  setTimeout(function() {
    gulp.run("grunt-critical");
      return deferred.promise;
  }, 6000);
});

/*
 *  ===================================================================
 *  | END CSS BUILD TASK |
 *  ===================================================================
 */

/*
 *  ===================================================================
 *  | IMAGE MINIFICATION TASK |
 *
 *  Take all images in "imagemin/" & minify them out to "build/img/"
 *  ===================================================================
 */

gulp.task('images', function () {
  return gulp.src('image-min/*')
  .pipe(imagemin({
    progressive: true,
    svgoPlugins: [{removeViewBox: false}]
    }))
  .pipe(gulp.dest('build/img'));
});

/*
 *  ===================================================================
 *  | "gulp-grunt" TASKS...RUN GRUNT TASKS VIA GULP!!!! |
 *  ===================================================================
 */

// Run the "grunt coffee" task
gulp.task("coffee", function () {
  gulp.run("grunt-coffee");
});

// BOWERCOPY TASKS
// Copy over ALL the Bower Components!!!
gulp.task("bowercopy", function () {
  gulp.run("grunt-bowercopy");
});

// Copy over Bootstrap core .css only
gulp.task("bowerbscss", function () {
  gulp.run("grunt-bowercopy:bscss");
});

// Copy over jQuery Cycle2 plugin only
gulp.task("bowercycle", function () {
  gulp.run("grunt-bowercopy:cycle2");
});

// Copy over jQuery v.1.11.1 only
gulp.task("bowerjq", function () {
  gulp.run("grunt-bowercopy:jq");
});

// Copy over scrollNav only
gulp.task("bowerscroll", function () {
  gulp.run("grunt-bowercopy:scroll");
  });

// Copy over enquire.js only
gulp.task("boweren", function () {
  gulp.run("grunt-bowercopy:enquire");
});

// Copy over matchMedia.js only
gulp.task("bowermm", function () {
  gulp.run("grunt-bowercopy:matchmedia");
});

// Start a server from "build/" and run it on port 8080
gulp.task("connect", function () {
  connect.server({
    root: "build/",
    livereload: true
  });
});

/*
 *  ===================================================================
 *  | STOP "gulp-grunt" TASKS |
 *  ===================================================================
 */

/*
 *  ===================================================================
 *  | START WATCH TASK |
 *
 *  If either pre-processor or "build/" files change and the site
 *  is currently running in the browser, refresh it via
 *  livereload.
 *
 *  Be careful of watching a lot of files because that may eat up
 *  computer memory...at least, it does in Grunt.
 *  ===================================================================
 */

 gulp.task("watch", function () {

  gulp.watch(jadeFiles, ["jade"]);
  gulp.watch(lessFiles, ["buildcss"]);
  gulp.watch(coffeeFiles, ["coffee"]);
  gulp.watch(["build/index.html", "build/css/*.css", "build/js/*.js"]);
});

// Simultaneously run "gulp connect" and "gulp spy"
gulp.task('spy', ['connect', 'watch']);
<commit_msg>update comments<commit_after>  // Single var pattern of gulp (require) stuff in full effect!!!

var gulp = require("gulp"), // Bring in gulp
    exec = require('child_process').exec, // Run CLI commands via node
    Q = require('q'), // Manage promises
    jade = require("gulp-jade"), // Jade task
    uncss = require("gulp-uncss"), // Remove unused css selectors
    minifyCSS = require("gulp-minify-css"), // Minify CSS
    autoprefixer = require('gulp-autoprefixer'), // Vendor prefixes
    csslint = require("gulp-csslint"), // Lint CSS
    concatCss = require('gulp-concat-css'), // Concatenating .css files
    imagemin = require('gulp-imagemin'), // Minifying images
    watch = require("gulp-watch"), // Watch files changes
    connect = require("gulp-connect"); // Livereload on port 8080

// End single var pattern

// Needed to run grunt tasks through gulp
require("gulp-grunt")(gulp);

/*
 *  ===================================================================
 *  | STORE PREPROCESSOR FILE REFERENCES IN VARIABLES |
 *  ===================================================================
 */
var jadeFiles = ["jade/index.jade", "jade/**/*.jade"], // Jade
    lessFiles = ["css-build/*.less", "css-build/**/*.less"], // LESS
    coffeeFiles = ["coffee/*.coffee"]; // Coffeescript

    /*
     * IGNORE ARRAY
     * ============
     * Selectors NOT to be removed when "gulp uncss" task runs.
     * Try to list them alphabetically and in the following order:
     *
     * 1. Page elements first (<nav>, <aside>, etc.)
     * 2. IDs second
     * 3. Classes third
     */
    ignoreArray = [
      "nav",
      "ol",
      "ul",
      ".all-player-info",
      ".btn",
      ".btn-info",
      ".cycle-slide",
      ".player-info",
      ".player-name",
      ".scroll-nav",
      ".scroll-nav__item",
      ".scroll-nav__link",
      ".scroll-nav__list",
      ".scroll-nav__section",
      ".team-copy-popup"
    ];

/*
 *  ===================================================================
 *  | JADE TASK |
 *  ===================================================================
 */
// Output Jade Files to build "build/index.html"
gulp.task("jade", function () {
  return gulp.src("jade/index.jade")
  .pipe(jade({
    pretty: true
    }))
  .pipe(gulp.dest("build"))
  .pipe(connect.reload());
  });

/*
 *  ===================================================================
 *  | CSS BUILD TASK |
 *
 * Running "gulp buildcss" performs the following sequence of tasks...
 *
 * 1. "gulp less" task
 * 2. "gulp concat" task that takes "gulp less" as a hint(*)
 * 3. "gulp outputCss" task that takes "gulp concat" as a hint(*)
 * 4. "gulp outputCss" removes unused CSS, minifies, lints, &
      auto-prefixes it
 * 5. "gulp jade" which rebuilds/resets build/index.html
 * 6. "gulp critical" to extract critical path CSS to build/index.html
 *
 * Some tasks return q promises so all files are ready for a given task
 *
 * (*) gulp "hints" are cool...read more about them at:
 * https://github.com/gulpjs/gulp/blob/master/docs/API.md
 *  ===================================================================
 */

// The core task for building out the site's CSS
gulp.task('buildcss', ['outputCss', 'jade', 'critical']);

// "gulp less" task
// ================
// Process .less files using node exec
// Returns a promise with q
gulp.task("less", function () {
  var deferred = Q.defer();

  setTimeout(function() {
    exec("lessc css-build/styles.less > css-build/styles.css");
    return deferred.promise;
  }, 1000);
});

// "gulp concat" task
// ==================
// Concatenate "css-build/styles.css" and "css-build/bootstrap.css"
// Takes "less" as a gulp hint
// Returns a promise with q
gulp.task('concat', ['less'], function() {
  var deferred = Q.defer();
  setTimeout(function() {
    return gulp.src(['css-build/bootstrap.css', 'css-build/styles.css'])
    .pipe(concatCss('styles.min.css'))
    .pipe(gulp.dest('build/css/'));
    return deferred.promise;
  }, 1000);
});

// "gulp outputCss" task
// =====================
// Concatenate "css-build/styles.css" and "css-build/bootstrap.css"
// Takes "concat" as a gulp hint
// Returns a promise with q
gulp.task("outputCss", ['concat'],function () {

 // Set this up so "outputCss" returns a promise
 var deferred = Q.defer();

  setTimeout(function() {
    gulp.src('build/css/styles.min.css')
    .pipe(uncss({
      html: ["build/index.html"],
      ignore: ignoreArray
    }))
    .pipe(autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
    }))
    .pipe(minifyCSS())
    .pipe(gulp.dest("build/css/"))
    .pipe(csslint({
      "important": false,
      "duplicate-background-images": false,
      "ids": false,
      "text-indent": false
    }))
    .pipe(csslint.reporter())
    .pipe(connect.reload())
   }, 4000);
  return deferred.promise;
});

// "gulp critical" task
// =====================
// Extract critical path css to "build/index.html" with a grunt task
// Returns a promise with q
gulp.task("critical", function () {
  var deferred = Q.defer();
  setTimeout(function() {
    gulp.run("grunt-critical");
      return deferred.promise;
  }, 6000);
});

/*
 *  ===================================================================
 *  | END CSS BUILD TASK |
 *  ===================================================================
 */

/*
 *  ===================================================================
 *  | IMAGE MINIFICATION TASK |
 *
 *  Take all images in "imagemin/" & minify them out to "build/img/"
 *  ===================================================================
 */

gulp.task('images', function () {
  return gulp.src('image-min/*')
  .pipe(imagemin({
    progressive: true,
    svgoPlugins: [{removeViewBox: false}]
    }))
  .pipe(gulp.dest('build/img'));
});

/*
 *  ===================================================================
 *  | "gulp-grunt" TASKS...RUN GRUNT TASKS VIA GULP!!!! |
 *  ===================================================================
 */

// Run the "grunt coffee" task
gulp.task("coffee", function () {
  gulp.run("grunt-coffee");
});

// BOWERCOPY TASKS
// Copy over ALL the Bower Components!!!
gulp.task("bowercopy", function () {
  gulp.run("grunt-bowercopy");
});

// Copy over Bootstrap core .css only
gulp.task("bowerbscss", function () {
  gulp.run("grunt-bowercopy:bscss");
});

// Copy over jQuery Cycle2 plugin only
gulp.task("bowercycle", function () {
  gulp.run("grunt-bowercopy:cycle2");
});

// Copy over jQuery v.1.11.1 only
gulp.task("bowerjq", function () {
  gulp.run("grunt-bowercopy:jq");
});

// Copy over scrollNav only
gulp.task("bowerscroll", function () {
  gulp.run("grunt-bowercopy:scroll");
  });

// Copy over enquire.js only
gulp.task("boweren", function () {
  gulp.run("grunt-bowercopy:enquire");
});

// Copy over matchMedia.js only
gulp.task("bowermm", function () {
  gulp.run("grunt-bowercopy:matchmedia");
});

// Serve a website from "build/" and run it on port 8080
gulp.task("connect", function () {
  connect.server({
    root: "build/",
    livereload: true
  });
});

/*
 *  ===================================================================
 *  | STOP "gulp-grunt" TASKS |
 *  ===================================================================
 */

/*
 *  ===================================================================
 *  | START WATCH TASK |
 *
 *  If either pre-processor or "build/" files change and the site
 *  is currently running in the browser, refresh it via
 *  livereload.
 *
 *  Be careful of watching a lot of files because that may eat up
 *  computer memory...at least, it does in Grunt.
 *  ===================================================================
 */

 gulp.task("watch", function () {

  gulp.watch(jadeFiles, ["jade"]);
  gulp.watch(lessFiles, ["buildcss"]);
  gulp.watch(coffeeFiles, ["coffee"]);
  gulp.watch(["build/index.html", "build/css/*.css", "build/js/*.js"]);
});

// Simultaneously run "gulp connect" and "gulp spy"
gulp.task('spy', ['connect', 'watch']);
<|endoftext|>


<commit_before>"use strict";

module.exports = function (grunt) {
    grunt.registerTask("plato", "exec:plato");
};
<commit_msg>Created aliases for common tasks<commit_after>"use strict";

module.exports = function (grunt) {
    grunt.registerTask("plato", "exec:plato");
    grunt.registerTask("wscript", "exec:testWscriptVerbose");
    grunt.registerTask("rhino", "exec:testRhinoVerbose");
    configuration.selenium.forEach(function (browser) {
        grunt.registerTask(browser, "exec:test" + browser.charAt(0).toUpperCase() + browser.substr(1) + "Verbose");
    });
};
<|endoftext|>


<commit_before>(function (ns, d3, _, $, undefined) {

    var defaults = {
        chart: {
            padding: {
                top: 3,
                bottom: 25,
                left: 30,
                right: 3
            }
        },

        xAxis: {
            rangePadding: 0,
            innerTickSize: 0,
            outerTickSize: 0,
            tickPadding: 6,
            titlePadding: 0,
            firstAndLast: true
        },

        yAxis: {
            min: undefined,
            max: undefined,
            innerTickSize: 6,
            outerTickSize: 6,
            tickPadding: 4,
            titlePadding: 0,
            labels: {
                format: '.0f' // d3 formats
            }
        }
    };

    function merge(array1, array2) {
        if(typeof(array1) === 'number') array1 = [array1];
        if(typeof(array2) === 'number') array2 = [array2];
        if(!array1 || !array1.length) return array2;
        if(!array2 || !array2.length) return array1;

        return [].concat(array1, array2).sort(function (a,b) { return a-b; });
    }

    function roundToNearest(number, multiple){
        return Math.ceil(number / multiple) * multiple;
    }

    function niceRound(val) {
        var digits = Math.floor(Math.log(val) / Math.LN10) + 1;
        var fac = Math.pow(10, digits);

        if(val < 1) return roundToNearest(val, 1);

        if(val < fac / 2) return roundToNearest(val, fac / 2);

        return roundToNearest(val, fac);
    }

    function extractScaleDomain(domain, min, max) {
        var dataMax = _.max(domain);
        var dataMin = _.min(domain);

        if (min === undefined && max === undefined) {
            return [dataMin, dataMax];
        }

        if (min === undefined) {
            return [Math.min(dataMin, max), max];
        }

        if (max === undefined) {
            return [min, Math.max(min, dataMax)];
        }

        return [min, max];
    }

    var cartesian = {
        dataSrc: [],

        init: function (options) {

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

            if(this.options.xAxis.title || this.options.yAxis.title) {
                this.titleOneEm = Narwhal.utils.textBounds('ABCD', 'axis-title').height;
                if(this.options.xAxis.title) {
                    this.options.chart.padding.bottom += this.titleOneEm; // should be 1em
                }

                if(this.options.yAxis.title) {
                    this.options.chart.padding.left += this.titleOneEm; // should be 1em
                }
            }

            return this;
        },

        computeXScale: function () {
            if (!this.xDomain) throw new Error('You are trying to render without setting data (xDomain).');

            this.scaleGenerator = _.nw.xScaleFactory(this.dataSrc, this.options);
            this.xScale = this.scaleGenerator.scale(this.xDomain);
            this.rangeBand = this.scaleGenerator.rangeBand();
        },

        computeYScale: function () {
            if (!this.yDomain) throw new Error('You are trying to render without setting data (yDomain).');
            var yScaleDomain = extractScaleDomain(this.yDomain, this.options.yAxis.min, this.options.yAxis.max);

            this.yScale = d3.scale.linear()
                .domain(yScaleDomain)
                .range([this.options.chart.plotHeight, 0]);
        },

        computeScales: function () {
            this.adjustDomain();
            this.computeXScale();
            this.computeYScale();

            return this;
        },

        xAxis: function () {
            var y = this.options.chart.plotHeight + this.options.chart.padding.top;
            var xAxis = this.scaleGenerator.axis().orient('bottom');

            this._xAxisGroup = this.svg
                .append('g')
                .attr('class', 'x axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + y + ')')
                .call(xAxis);

            this.scaleGenerator.postProcessAxis(this._xAxisGroup);

            return this;
        },

        yAxis: function () {
            var options = this.options.yAxis;
            var tickValues = this._extractTickValues(this.yDomain, options.min, options.max);
            var format = d3.format(options.labels.format);
            var yAxis = d3.svg.axis()
                .scale(this.yScale)
                .tickValues(tickValues)
                .tickFormat(format)
                .tickSize(options.innerTickSize, options.outerTickSize)
                .tickPadding(options.tickPadding)
                .orient('left');

            this._yAxisGroup = this.svg.append('g')
                .attr('class', 'y axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + this.options.chart.padding.top + ')');

            this._yAxisGroup.call(yAxis)
                .selectAll('text')
                    .attr('dy', '.9em');

            return this;
        },

        axisLabels: function () {
            var lineHeightAdjustment = this.titleOneEm * 0.25; // add 25% of font-size for a complete line-height

            if (this.options.xAxis.title) {
                this._xAxisGroup.append('text')
                    .attr('class', 'x axis-title')
                    .attr('text-anchor', 'end')
                    .attr('y', this.options.chart.padding.bottom - lineHeightAdjustment)
                    .attr('dx', this.options.chart.plotWidth)
                    .attr('dy', -this.options.xAxis.titlePadding)
                    .text(this.options.xAxis.title);
            }

            if (this.options.yAxis.title) {
                this._yAxisGroup.append('text')
                    .attr('class', 'y axis-title')
                    .attr('text-anchor', 'end')
                    .attr('transform', 'rotate(-90)')
                    .attr('y', -this.options.chart.padding.left + this.titleOneEm - lineHeightAdjustment)
                    .attr('dx', 0)
                    .attr('dy', this.options.yAxis.titlePadding)
                    .text(this.options.yAxis.title);
            }

        },

        render: function () {
            this.composeOptions();

            this.calcMetrics();

            this.computeScales();

            this.baseRender();

            this.xAxis()
                .yAxis()
                .axisLabels();

            this.renderVisualizations();

            return this;
        },

        datum: function (d, index) {
            return {
                y: _.isObject(d) ? d.y : d,
                x: _.isObject(d) ? d.x : this.options.xAxis.categories ? this.options.xAxis.categories[index] : index
            };
        },

        data: function (series) {

            if (series instanceof Array && !series.length) {
                return this;
            } else if (series instanceof Array && !series[0].data) {
                this.dataSrc = _.map(series, _.bind(this.datum, this));
                this.xDomain = this.extractXDomain(this.dataSrc);
                this.yDomain = this.extractYDomain(this.dataSrc);
                this.yMax = this.getYAxisDataMax(this.dataSrc);
                this.yMin = this.getYAxisDataMin(this.dataSrc);
            } else if (series instanceof Array && series[0].data) {
                // we have an array of series
                _.each(series, function (set) { this.data(set.data); }, this);
            }

            return this;
        },

        getYAxisDataMax: function (datums) {
            return Math.max(this.yMax || 0, _.max(_.pluck(datums, 'y')));
        },

        getYAxisDataMin: function (datums) {
            return Math.min(this.yMin || 0, _.min(_.pluck(datums, 'y')));
        },

        extractXDomain: function(datums) {
            return  this.options.xAxis.categories ? this.options.xAxis.categories : _.pluck(datums, 'x');
        },

        extractYDomain: function (datums) {
            var max = this.yDomain ? Math.max(this.yDomain[1], _.max(_.pluck(datums, 'y'))) : _.max(_.pluck(datums, 'y'));
            var min = this.yDomain ? Math.max(this.yDomain[0], _.min(_.pluck(datums, 'y'))) : _.min(_.pluck(datums, 'y'));

            return [min, max];
        },

        adjustDomain: function () {
            this.yDomain = this.yDomain ? [this.yDomain[0], niceRound(this.yDomain[1])] : [0, 10];
            this.xDomain = this.xDomain ? this.xDomain : [];
        },

        _extractTickValues: function (domain, min, max) {
            var adjustedDomain = merge(domain, this.yMax);

            if (min === undefined && max === undefined)
                return adjustedDomain;

            if (min === undefined) {
                return max > this.yMin ? merge([max], adjustedDomain) : [max];
            }

            if (max === undefined) {
                if (min >= this.yMax) return [min];
                adjustedDomain[0] = min;

                return adjustedDomain;
            }

            return merge([min, max], adjustedDomain);
        }

    };

    window[ns].prototype.expose('cartesian', cartesian);

})('Narwhal', window.d3, window._, window.jQuery);
<commit_msg>Change behavior of niceRound to be 10% above the max<commit_after>(function (ns, d3, _, $, undefined) {

    var defaults = {
        chart: {
            padding: {
                top: 3,
                bottom: 25,
                left: 30,
                right: 3
            }
        },

        xAxis: {
            rangePadding: 0,
            innerTickSize: 0,
            outerTickSize: 0,
            tickPadding: 6,
            titlePadding: 0,
            firstAndLast: true
        },

        yAxis: {
            min: undefined,
            max: undefined,
            innerTickSize: 6,
            outerTickSize: 6,
            tickPadding: 4,
            titlePadding: 0,
            labels: {
                format: '.0f' // d3 formats
            }
        }
    };

    function merge(array1, array2) {
        if(typeof(array1) === 'number') array1 = [array1];
        if(typeof(array2) === 'number') array2 = [array2];
        if(!array1 || !array1.length) return array2;
        if(!array2 || !array2.length) return array1;

        return [].concat(array1, array2).sort(function (a,b) { return a-b; });
    }

    function roundToNearest(number, multiple){
        return Math.ceil(number / multiple) * multiple;
    }

    function niceRound(val) {
        return Math.ceil(val * 1.10);
        var digits = Math.floor(Math.log(val) / Math.LN10) + 1;
        var fac = Math.pow(10, digits);

        if(val < 1) return roundToNearest(val, 1);

        if(val < fac / 2) return roundToNearest(val, fac / 2);

        return roundToNearest(val, fac);
    }

    function extractScaleDomain(domain, min, max) {
        var dataMax = _.max(domain);
        var dataMin = _.min(domain);

        if (min === undefined && max === undefined) {
            return [dataMin, dataMax];
        }

        if (min === undefined) {
            return [Math.min(dataMin, max), max];
        }

        if (max === undefined) {
            return [min, Math.max(min, dataMax)];
        }

        return [min, max];
    }

    var cartesian = {
        dataSrc: [],

        init: function (options) {

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

            if(this.options.xAxis.title || this.options.yAxis.title) {
                this.titleOneEm = Narwhal.utils.textBounds('ABCD', 'axis-title').height;
                if(this.options.xAxis.title) {
                    this.options.chart.padding.bottom += this.titleOneEm; // should be 1em
                }

                if(this.options.yAxis.title) {
                    this.options.chart.padding.left += this.titleOneEm; // should be 1em
                }
            }

            return this;
        },

        computeXScale: function () {
            if (!this.xDomain) throw new Error('You are trying to render without setting data (xDomain).');

            this.scaleGenerator = _.nw.xScaleFactory(this.dataSrc, this.options);
            this.xScale = this.scaleGenerator.scale(this.xDomain);
            this.rangeBand = this.scaleGenerator.rangeBand();
        },

        computeYScale: function () {
            if (!this.yDomain) throw new Error('You are trying to render without setting data (yDomain).');
            var yScaleDomain = extractScaleDomain(this.yDomain, this.options.yAxis.min, this.options.yAxis.max);

            this.yScale = d3.scale.linear()
                .domain(yScaleDomain)
                .range([this.options.chart.plotHeight, 0]);
        },

        computeScales: function () {
            this.adjustDomain();
            this.computeXScale();
            this.computeYScale();

            return this;
        },

        xAxis: function () {
            var y = this.options.chart.plotHeight + this.options.chart.padding.top;
            var xAxis = this.scaleGenerator.axis().orient('bottom');

            this._xAxisGroup = this.svg
                .append('g')
                .attr('class', 'x axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + y + ')')
                .call(xAxis);

            this.scaleGenerator.postProcessAxis(this._xAxisGroup);

            return this;
        },

        yAxis: function () {
            var options = this.options.yAxis;
            var tickValues = this._extractTickValues(this.yDomain, options.min, options.max);
            var format = d3.format(options.labels.format);
            var yAxis = d3.svg.axis()
                .scale(this.yScale)
                .tickValues(tickValues)
                .tickFormat(format)
                .tickSize(options.innerTickSize, options.outerTickSize)
                .tickPadding(options.tickPadding)
                .orient('left');

            this._yAxisGroup = this.svg.append('g')
                .attr('class', 'y axis')
                .attr('transform', 'translate(' + this.options.chart.padding.left + ',' + this.options.chart.padding.top + ')');

            this._yAxisGroup.call(yAxis)
                .selectAll('text')
                    .attr('dy', '.9em');

            return this;
        },

        axisLabels: function () {
            var lineHeightAdjustment = this.titleOneEm * 0.25; // add 25% of font-size for a complete line-height

            if (this.options.xAxis.title) {
                this._xAxisGroup.append('text')
                    .attr('class', 'x axis-title')
                    .attr('text-anchor', 'end')
                    .attr('y', this.options.chart.padding.bottom - lineHeightAdjustment)
                    .attr('dx', this.options.chart.plotWidth)
                    .attr('dy', -this.options.xAxis.titlePadding)
                    .text(this.options.xAxis.title);
            }

            if (this.options.yAxis.title) {
                this._yAxisGroup.append('text')
                    .attr('class', 'y axis-title')
                    .attr('text-anchor', 'end')
                    .attr('transform', 'rotate(-90)')
                    .attr('y', -this.options.chart.padding.left + this.titleOneEm - lineHeightAdjustment)
                    .attr('dx', 0)
                    .attr('dy', this.options.yAxis.titlePadding)
                    .text(this.options.yAxis.title);
            }

        },

        render: function () {
            this.composeOptions();

            this.calcMetrics();

            this.computeScales();

            this.baseRender();

            this.xAxis()
                .yAxis()
                .axisLabels();

            this.renderVisualizations();

            return this;
        },

        datum: function (d, index) {
            return {
                y: _.isObject(d) ? d.y : d,
                x: _.isObject(d) ? d.x : this.options.xAxis.categories ? this.options.xAxis.categories[index] : index
            };
        },

        data: function (series) {

            if (series instanceof Array && !series.length) {
                return this;
            } else if (series instanceof Array && !series[0].data) {
                this.dataSrc = _.map(series, _.bind(this.datum, this));
                this.xDomain = this.extractXDomain(this.dataSrc);
                this.yDomain = this.extractYDomain(this.dataSrc);
                this.yMax = this.getYAxisDataMax(this.dataSrc);
                this.yMin = this.getYAxisDataMin(this.dataSrc);
            } else if (series instanceof Array && series[0].data) {
                // we have an array of series
                _.each(series, function (set) { this.data(set.data); }, this);
            }

            return this;
        },

        getYAxisDataMax: function (datums) {
            return Math.max(this.yMax || 0, _.max(_.pluck(datums, 'y')));
        },

        getYAxisDataMin: function (datums) {
            return Math.min(this.yMin || 0, _.min(_.pluck(datums, 'y')));
        },

        extractXDomain: function(datums) {
            return  this.options.xAxis.categories ? this.options.xAxis.categories : _.pluck(datums, 'x');
        },

        extractYDomain: function (datums) {
            var max = this.yDomain ? Math.max(this.yDomain[1], _.max(_.pluck(datums, 'y'))) : _.max(_.pluck(datums, 'y'));
            var min = this.yDomain ? Math.max(this.yDomain[0], _.min(_.pluck(datums, 'y'))) : _.min(_.pluck(datums, 'y'));

            return [min, max];
        },

        adjustDomain: function () {
            this.yDomain = this.yDomain ? [this.yDomain[0], niceRound(this.yDomain[1])] : [0, 10];
            this.xDomain = this.xDomain ? this.xDomain : [];
        },

        _extractTickValues: function (domain, min, max) {
            var adjustedDomain = merge(domain, this.yMax);

            if (min === undefined && max === undefined)
                return adjustedDomain;

            if (min === undefined) {
                return max > this.yMin ? merge([max], adjustedDomain) : [max];
            }

            if (max === undefined) {
                if (min >= this.yMax) return [min];
                adjustedDomain[0] = min;

                return adjustedDomain;
            }

            return merge([min, max], adjustedDomain);
        }

    };

    window[ns].prototype.expose('cartesian', cartesian);

})('Narwhal', window.d3, window._, window.jQuery);
<|endoftext|>


<commit_before>/*global require*/
var gulp = require('gulp');
var args = require('yargs').argv;
var config = require('./gulp.config')();
var del = require('del');
var $ = require('gulp-load-plugins')({lazy: true});

//var jshint = require('gulp-jshint');
//var jscs = require('gulp-jscs');
//var util = require('gulp-util');
//var gulpprint = require('gulp-print');
//var gulpif = require('gulp-if');

function log(msg) {
    'use strict';
    var item;
    if (typeof (msg) === 'object') {
        for (item in msg) {
            if (msg.hasOwnProperty(item)) {
                $.util.log($.util.colors.blue(msg[item]));
            }
        }
    } else {
        $.util.log($.util.colors.green(msg));
    }
}
//gulp vet --verbose
gulp.task('vet', function () {
    'use strict';
    log('Analyzing source with JSHint and JSCS');

    return gulp
        .src(config.alljs)
        .pipe($.if(args.verbose, $.print()))
        .pipe($.jscs())
        .pipe($.jshint())
        .pipe($.jshint.reporter('jshint-stylish', {verbose: true}))
        .pipe($.jshint.reporter('fail'));
});

gulp.task('styles', ['clean-styles'], function () {
    'use strict';
    log('Compile Less --->CSS');

    return gulp
        .src(config.less) //TODO
        .pipe($.plumber())
        .pipe($.less())
        .pipe($.autoprefixer({browsers: ['last 2 version', '> 5%']}))
        .pipe(gulp.dest(config.temp));
});

function clean(path, done) {
    'use strict';
    log('Cleaning: ' + $.util.colors.blue(path));
    del(path).then(done());
}

gulp.task('clean-styles', function (done) {
    'use strict';
    log('Clean files');
    var files = config.temp + '**/*.css';
    clean(files, done);
});

gulp.task('less-watcher', function () {
    'use strict';
    gulp.watch([config.less], ['styles']);
});
<commit_msg>remove empty space<commit_after>/*global require*/
var gulp = require('gulp');
var args = require('yargs').argv;
var config = require('./gulp.config')();
var del = require('del');
var $ = require('gulp-load-plugins')({lazy: true});

//var jshint = require('gulp-jshint');
//var jscs = require('gulp-jscs');
//var util = require('gulp-util');
//var gulpprint = require('gulp-print');
//var gulpif = require('gulp-if');

function log(msg) {
    'use strict';
    var item;
    if (typeof (msg) === 'object') {
        for (item in msg) {
            if (msg.hasOwnProperty(item)) {
                $.util.log($.util.colors.blue(msg[item]));
            }
        }
    } else {
        $.util.log($.util.colors.green(msg));
    }
}
//gulp vet --verbose
gulp.task('vet', function () {
    'use strict';
    log('Analyzing source with JSHint and JSCS');
    return gulp
        .src(config.alljs)
        .pipe($.if(args.verbose, $.print()))
        .pipe($.jscs())
        .pipe($.jshint())
        .pipe($.jshint.reporter('jshint-stylish', {verbose: true}))
        .pipe($.jshint.reporter('fail'));
});

gulp.task('styles', ['clean-styles'], function () {
    'use strict';
    log('Compile Less --->CSS');
    return gulp
        .src(config.less) //TODO
        .pipe($.plumber())
        .pipe($.less())
        .pipe($.autoprefixer({browsers: ['last 2 version', '> 5%']}))
        .pipe(gulp.dest(config.temp));
});

function clean(path, done) {
    'use strict';
    log('Cleaning: ' + $.util.colors.blue(path));
    del(path).then(done());
}

gulp.task('clean-styles', function (done) {
    'use strict';
    log('Clean files');
    var files = config.temp + '**/*.css';
    clean(files, done);
});

gulp.task('less-watcher', function () {
    'use strict';
    gulp.watch([config.less], ['styles']);
});
<|endoftext|>


<commit_before>'use strict'

import { hasToken } from './../auth/middleware/token';
import { accessToTournament } from './../auth/middleware/tournament-access';
import { findTournament } from './../auth/middleware/tournament';
import Tournament from './../models/sql-models/tournament';

import slug from './../helpers/slug';

module.exports = app => {

  const minifyJs = app.get('minifyJs')
  const statsBaseUrl = app.get('STATS_BASE_URL');

  app.get('/', (req, res) => {
      if (req.cookies.nfToken) {
          res.redirect('/tournaments');
      } else {
          res.render("index/index", { statsBaseUrl });
      }
  });

  app.get("/tournaments", (req, res, next) => {
      if (!req.cookies.nfToken) {
          res.redirect('/');
      } else {
          res.render('tournament/alltournaments', {minifyJs});
      }
  });

  app.get('/t/:tid/stats', findTournament, (req, res) => {
    res.redirect(`/t/${req.params.tid}/${slug(req.tournament.name)}/stats`);
  });

  app.get('/t/:tid/:slug/stats', (req, res) => {
    const redirectUrl = statsBaseUrl + `/t/${req.params.tid}/${req.params.slug}/stats`
    res.redirect(redirectUrl);
  });

  app.get('/t/:tid', hasToken, accessToTournament, (req, res, next) => {
    res.render("tournament/tournament-view", { statsBaseUrl })
  })

  app.get("/t/:tid/:slug", hasToken, accessToTournament, (req, res, next) => {
    res.render("tournament/tournament-view", { statsBaseUrl })
  });

  app.get('/t/:tid/stats/:page(team|player|teamfull|playerfull|roundreport)/', (req, res) => {
    const url = req.url;
    res.redirect(`https://v1.neg5.org${url}`);
  });
};
<commit_msg>redirect to new route for old tournaments<commit_after>'use strict'

import { hasToken } from './../auth/middleware/token';
import { accessToTournament } from './../auth/middleware/tournament-access';
import { findTournament } from './../auth/middleware/tournament';
import Tournament from './../models/sql-models/tournament';

import slug from './../helpers/slug';

module.exports = app => {

  const minifyJs = app.get('minifyJs')
  const statsBaseUrl = app.get('STATS_BASE_URL');

  app.get('/', (req, res) => {
      if (req.cookies.nfToken) {
          res.redirect('/tournaments');
      } else {
          res.render("index/index", { statsBaseUrl });
      }
  });

  app.get("/tournaments", (req, res, next) => {
      if (!req.cookies.nfToken) {
          res.redirect('/');
      } else {
          res.render('tournament/alltournaments', {minifyJs});
      }
  });

  app.get('/t/:tid/stats', findTournament, (req, res) => {
    res.redirect(`/t/${req.params.tid}/${slug(req.tournament.name)}/stats`);
  });

  app.get('/t/:tid/:slug/stats', (req, res) => {
    const redirectUrl = statsBaseUrl + `/t/${req.params.tid}/${req.params.slug}/stats`
    res.redirect(redirectUrl);
  });

  app.get('/t/:tid', hasToken, accessToTournament, (req, res, next) => {
    res.render("tournament/tournament-view", { statsBaseUrl })
  })

  app.get('/t/:tid/stats/:page(team|player|teamfull|playerfull|roundreport)/', (req, res) => {
    res.redirect(`/t/${req.params.tid}/stats`);
  });

  app.get("/t/:tid/:slug", hasToken, accessToTournament, (req, res, next) => {
    res.render("tournament/tournament-view", { statsBaseUrl })
  });
};
<|endoftext|>