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

/**
 * @param {*}        value
 * @param {Object}   validatorsConfig
 * @param {String[]} path
 * @param {Object}   originalObject
 *
 * @returns {Promise}
 */
module.exports = function validateValue(value, validatorsConfig, path, originalObject) {
    var validatorName, validator, validatorConfig;
    var validatorResult, validatorResultPromise, validatorsResultsPromises = [], validatorsErrors = [];

    validatorsConfig = utils.result(validatorsConfig, value, path, originalObject);

    for (validatorName in validatorsConfig) {
        validator = validators.get(validatorName);
        if (!validator) {
            return Promise.reject(new Error('Unknown validator ' + validatorName));
        }

        validatorConfig = utils.result(validatorsConfig[validatorName], value, path, originalObject);
        validatorResult = validator.call(validator, value, validatorConfig, originalObject);

        if (validatorResult && validatorResult.then instanceof Function) {
            validatorResultPromise = validatorResult;
        } else if (validatorResult === undefined) {
            validatorResultPromise = Promise.resolve();
        } else {
            validatorResultPromise = Promise.reject(validatorResult);
        }

        validatorResultPromise.catch(function(err) {
            validatorsErrors = validatorsErrors.concat(err);
        });

        validatorsResultsPromises.push(validatorResultPromise);
    }

    return utils.waitForAllPromises(validatorsResultsPromises).catch(function(err) {
        if (err instanceof Error) {
            return Promise.reject(err);
        }

        return Promise.reject(validatorsErrors);
    });
};<commit_msg>check validatorConfig<commit_after>var utils      = require('../../utils');
var validators = require('./validators');

/**
 * @param {*}        value
 * @param {Object}   validatorsConfig
 * @param {String[]} path
 * @param {Object}   originalObject
 *
 * @returns {Promise}
 */
module.exports = function validateValue(value, validatorsConfig, path, originalObject) {
    var validatorName, validator, validatorConfig;
    var validatorResult, validatorResultPromise, validatorsResultsPromises = [], validatorsErrors = [];

    validatorsConfig = utils.result(validatorsConfig, value, path, originalObject);

    for (validatorName in validatorsConfig) {
        validator = validators.get(validatorName);
        if (!validator) {
            return Promise.reject(new Error('Unknown validator ' + validatorName));
        }

        validatorConfig = utils.result(validatorsConfig[validatorName], value, path, originalObject);
        if (!validatorConfig) {
            continue;
        }

        validatorResult = validator.call(validator, value, validatorConfig, originalObject);

        if (validatorResult && validatorResult.then instanceof Function) {
            validatorResultPromise = validatorResult;
        } else if (validatorResult === undefined) {
            validatorResultPromise = Promise.resolve();
        } else {
            validatorResultPromise = Promise.reject(validatorResult);
        }

        validatorResultPromise.catch(function(err) {
            validatorsErrors = validatorsErrors.concat(err);
        });

        validatorsResultsPromises.push(validatorResultPromise);
    }

    return utils.waitForAllPromises(validatorsResultsPromises).catch(function(err) {
        if (err instanceof Error) {
            return Promise.reject(err);
        }

        return Promise.reject(validatorsErrors);
    });
};<|endoftext|>


<commit_before>import Utils from '../utils/utils';
import Geo from '../geo';

import parseCSSColor from 'csscolorparser';
import log from 'loglevel';

export var StyleParser = {};

// Style macros

StyleParser.expandMacros = function expandMacros (obj) {
    for (var p in obj) {
        var val = obj[p];

        // Loop through object properties
        if (typeof val === 'object') {
            obj[p] = expandMacros(val);
        }
        // Convert strings back into functions
        else if (typeof val === 'string') {
            for (var m in StyleParser.macros) {
                if (val.match(StyleParser.macros[m])) {
                    var f;
                    try {
                        /*jshint ignore:start */
                        eval('f = ' + val);
                        /*jshint ignore:end */
                        obj[p] = f;
                        log.trace(`expanded macro ${val} to ${f}`);
                        break;
                    }
                    catch (e) {
                        // fall-back to original value if parsing failed
                        obj[p] = val;
                        log.trace(`failed to expand macro ${val}`);
                    }
                }
            }
        }
    }

    return obj;
};

// List of macros
StyleParser.macros = [
    'Style.color.pseudoRandomColor',
    'Style.color.randomColor'
];

var Style = {};

Style.color = {
    // pseudo-random grayscale by geometry id
    pseudoRandomGrayscale() {
        var func = `function() {
            var c = Math.max((parseInt(feature.id, 16) % 100) / 100, 0.4);
            return [0.7 * c, 0.7 * c, 0.7 * c];
        }`;
        return func;
    },

    // pseudo-random color by geometry id
    pseudoRandomColor() {
        var func = `function() {
            return [
                0.7 * (parseInt(feature.id, 16) / 100 % 1),
                0.7 * (parseInt(feature.id, 16) / 10000 % 1),
                0.7 * (parseInt(feature.id, 16) / 1000000 % 1)
            ];
        }`;
        return func;
        // return `function() { return [0.7 * (parseInt(feature.id, 16) / 100 % 1), 0.7 * (parseInt(feature.id, 16) / 10000 % 1), 0.7 * (parseInt(feature.id, 16) / 1000000 % 1)]; }`;
    },

    // random color
    randomColor() {
        var func = `function() {
            return [0.7 * Math.random(), 0.7 * Math.random(), 0.7 * Math.random()];
        }`;
        return func;
    }
};

// Wraps style functions and provides a scope of commonly accessible data:
// - feature: the 'properties' of the feature, e.g. accessed as 'feature.name'
// - $zoom: the current map zoom level
// - $geometry: the type of geometry, 'point', 'line', or 'polygon'
// - $meters_per_pixel: conversion for meters/pixels at current map zoom
// - properties: user-defined properties on the style-rule object in the stylesheet
StyleParser.wrapFunction = function (func) {
    var f = `function(context) {
                var feature = context.feature.properties;
                var $zoom = context.zoom;
                var $layer = context.layer;
                var $geometry = context.geometry;
                var $meters_per_pixel = context.meters_per_pixel;
                var properties = context.properties;

                var val = (${func}());

                if (typeof val === 'number' && isNaN(val)) {
                    val = null; // convert NaNs to nulls
                }

                return val;
            }`;
    return f;
};

// Style parsing

// Style defaults
StyleParser.defaults = {
    color: [1, 1, 1, 1],
    width: 1,
    size: 1,
    extrude: false,
    height: 20,
    min_height: 0,
    order: 0,
    z: 0,
    material: {
        ambient: 1,
        diffuse: 1
    }
};

// A context object that is passed to style parsing functions to provide a scope of commonly used values
StyleParser.getFeatureParseContext = function (feature, tile) {
    return {
        feature,
        tile,
        zoom: tile.style_zoom,
        geometry: Geo.geometryType(feature.geometry.type),
        meters_per_pixel: Geo.metersPerPixel(tile.coords.z),
        units_per_meter: Geo.units_per_meter[tile.coords.z]
    };
};

StyleParser.convertUnits = function(val, context, convert = 'meters') {
    if (typeof val === 'string') {
        var units = val.match(/([0-9.-]+)([a-z]+)/);
        if (units && units.length === 3) {
            val = parseFloat(units[1]);
            units = units[2];
        }

        // Convert to meters
        if (convert === 'meters') {
            // Convert from pixels
            if (units === 'px') {
                val *= Geo.metersPerPixel(context.zoom);
            }
            // Convert from kilometers
            else if (units === 'km') {
                val *= 1000;
            }
        }
    }
    else if (Array.isArray(val)) {
        // Array of arrays, e.g. zoom-interpolated stops
        if (Array.isArray(val[0])) {
            return val.map(v => { return [v[0], StyleParser.convertUnits(v[1], context, convert)]; });
        }
        // Array of values
        else {
            return val.map(v => { return StyleParser.convertUnits(v, context, convert); });
        }
    }
    return val;
};

// Takes a distance cache object and returns a distance value for this zoom
// (caching the result for future use)
// { value: original, zoom: { z: meters }, dynamic: function(){...} }
StyleParser.cacheDistance = function(val, context, convert = 'meters') {
    if (val.dynamic) {
        let v = val.dynamic(context);
        return v;
    }
    else if (val.zoom && val.zoom[convert] && val.zoom[convert][context.zoom]) {
        return val.zoom[convert][context.zoom];
    }
    else {
        // Dynamic function-based
        if (typeof val.value === 'function') {
            val.dynamic = val.value;
            let v = val.dynamic(context);
            return v;
        }
        // Array of zoom-interpolated stops, e.g. [zoom, value] pairs
        else {
            // Calculate value for current zoom
            val.zoom = val.zoom || {};
            let zunits = val.zoom[convert] = val.zoom[convert] || {};

            zunits[context.zoom] = StyleParser.convertUnits(val.value, context,
                convert === 'meters' && 'meters'); // convert to meters
            zunits[context.zoom] = Utils.interpolate(context.zoom, zunits[context.zoom]);

            return zunits[context.zoom];
        }
    }
};

// Cache previously parsed color strings
StyleParser.string_colors = {};
StyleParser.colorForString = function(string) {
    // Cached
    if (StyleParser.string_colors[string]) {
        return StyleParser.string_colors[string];
    }

    // Calculate and cache
    let color = parseCSSColor.parseCSSColor(string);
    if (color && color.length === 4) {
        color[0] /= 255;
        color[1] /= 255;
        color[2] /= 255;
        color[3] = 1;
    }
    else {
        color = StyleParser.defaults.color;
    }
    StyleParser.string_colors[string] = color;
    return color;
};

// Takes a color cache object and returns a color value for this zoom
// (caching the result for future use)
// { value: original, static: [r,g,b,a], zoom: { z: [r,g,b,a] }, dynamic: function(){...} }
StyleParser.cacheColor = function(val, context = {}) {
    if (val.dynamic) {
        let v = val.dynamic(context);
        if (v && v[3] == null) {
            v[3] = 1; // default alpha
        }
        return v;
    }
    else if (val.static) {
        return val.static;
    }
    else if (val.zoom && val.zoom[context.zoom]) {
        return val.zoom[context.zoom];
    }
    else {
        // Dynamic function-based color
        if (typeof val.value === 'function') {
            val.dynamic = val.value;
            let v = val.dynamic(context);
            if (v && v[3] == null) {
                v[3] = 1; // default alpha
            }
            return v;
        }
        // Single string color
        else if (typeof val.value === 'string') {
            val.static = StyleParser.colorForString(val.value);
            return val.static;
        }
        // Array of zoom-interpolated stops, e.g. [zoom, color] pairs
        else if (Array.isArray(val.value) && Array.isArray(val.value[0])) {
            if (!val.zoom) {
                val.zoom = {};
                // Parse any string colors inside stops
                for (let i=0; i < val.value.length; i++) {
                    let v = val.value[i];
                    if (v && typeof v[1] === 'string') {
                        v[1] = StyleParser.colorForString(v[1]);
                    }
                }
            }

            // Calculate color for current zoom
            val.zoom[context.zoom] = Utils.interpolate(context.zoom, val.value);
            val.zoom[context.zoom][3] = val.zoom[context.zoom][3] || 1; // default alpha
            return val.zoom[context.zoom];
        }
        // Single array color
        else {
            val.static = val.value;
            if (val.static && val.static[3] == null) {
                val.static[3] = 1; // default alpha
            }
            return val.static;
        }
    }
};

StyleParser.parseColor = function(val, context = {}) {
    if (typeof val === 'function') {
        val = val(context);
    }

    // Parse CSS-style colors
    // TODO: change all colors to use 0-255 range internally to avoid dividing and then re-multiplying in geom builder
    if (typeof val === 'string') {
        val = parseCSSColor.parseCSSColor(val);
        if (val && val.length === 4) {
            val[0] /= 255;
            val[1] /= 255;
            val[2] /= 255;
        }
        else {
            val = null;
        }
    }
    else if (Array.isArray(val) && Array.isArray(val[0])) {
        // Array of zoom-interpolated stops, e.g. [zoom, color] pairs
        for (let i=0; i < val.length; i++) {
            let v = val[i];
            if (typeof v[1] === 'string') {
                var vc = parseCSSColor.parseCSSColor(v[1]);
                if (vc && vc.length === 4) {
                    vc[0] /= 255;
                    vc[1] /= 255;
                    vc[2] /= 255;
                    v[1] = vc;
                }
            }
        }
    }

    if (context.zoom) {
        val = Utils.interpolate(context.zoom, val);
    }

    // Defaults
    if (val) {
        // alpha
        if (val[3] == null) {
            val[3] = 1;
        }
    }
    else {
        val = [0, 0, 0, 1];
    }

    return val;
};

StyleParser.calculateOrder = function(order, context) {
    // Computed order
    if (typeof order === 'function') {
        order = order(context);
    }
    else if (typeof order === 'string') {
        // Order tied to feature property
        if (context.feature.properties[order]) {
            order = context.feature.properties[order];
        }
        // Explicit order value
        else {
            order = parseFloat(order);
        }
    }

    return order;
};
<commit_msg>fix string color parsing for non-cached colors<commit_after>import Utils from '../utils/utils';
import Geo from '../geo';

import parseCSSColor from 'csscolorparser';
import log from 'loglevel';

export var StyleParser = {};

// Style macros

StyleParser.expandMacros = function expandMacros (obj) {
    for (var p in obj) {
        var val = obj[p];

        // Loop through object properties
        if (typeof val === 'object') {
            obj[p] = expandMacros(val);
        }
        // Convert strings back into functions
        else if (typeof val === 'string') {
            for (var m in StyleParser.macros) {
                if (val.match(StyleParser.macros[m])) {
                    var f;
                    try {
                        /*jshint ignore:start */
                        eval('f = ' + val);
                        /*jshint ignore:end */
                        obj[p] = f;
                        log.trace(`expanded macro ${val} to ${f}`);
                        break;
                    }
                    catch (e) {
                        // fall-back to original value if parsing failed
                        obj[p] = val;
                        log.trace(`failed to expand macro ${val}`);
                    }
                }
            }
        }
    }

    return obj;
};

// List of macros
StyleParser.macros = [
    'Style.color.pseudoRandomColor',
    'Style.color.randomColor'
];

var Style = {};

Style.color = {
    // pseudo-random grayscale by geometry id
    pseudoRandomGrayscale() {
        var func = `function() {
            var c = Math.max((parseInt(feature.id, 16) % 100) / 100, 0.4);
            return [0.7 * c, 0.7 * c, 0.7 * c];
        }`;
        return func;
    },

    // pseudo-random color by geometry id
    pseudoRandomColor() {
        var func = `function() {
            return [
                0.7 * (parseInt(feature.id, 16) / 100 % 1),
                0.7 * (parseInt(feature.id, 16) / 10000 % 1),
                0.7 * (parseInt(feature.id, 16) / 1000000 % 1)
            ];
        }`;
        return func;
        // return `function() { return [0.7 * (parseInt(feature.id, 16) / 100 % 1), 0.7 * (parseInt(feature.id, 16) / 10000 % 1), 0.7 * (parseInt(feature.id, 16) / 1000000 % 1)]; }`;
    },

    // random color
    randomColor() {
        var func = `function() {
            return [0.7 * Math.random(), 0.7 * Math.random(), 0.7 * Math.random()];
        }`;
        return func;
    }
};

// Wraps style functions and provides a scope of commonly accessible data:
// - feature: the 'properties' of the feature, e.g. accessed as 'feature.name'
// - $zoom: the current map zoom level
// - $geometry: the type of geometry, 'point', 'line', or 'polygon'
// - $meters_per_pixel: conversion for meters/pixels at current map zoom
// - properties: user-defined properties on the style-rule object in the stylesheet
StyleParser.wrapFunction = function (func) {
    var f = `function(context) {
                var feature = context.feature.properties;
                var $zoom = context.zoom;
                var $layer = context.layer;
                var $geometry = context.geometry;
                var $meters_per_pixel = context.meters_per_pixel;
                var properties = context.properties;

                var val = (${func}());

                if (typeof val === 'number' && isNaN(val)) {
                    val = null; // convert NaNs to nulls
                }

                return val;
            }`;
    return f;
};

// Style parsing

// Style defaults
StyleParser.defaults = {
    color: [1, 1, 1, 1],
    width: 1,
    size: 1,
    extrude: false,
    height: 20,
    min_height: 0,
    order: 0,
    z: 0,
    material: {
        ambient: 1,
        diffuse: 1
    }
};

// A context object that is passed to style parsing functions to provide a scope of commonly used values
StyleParser.getFeatureParseContext = function (feature, tile) {
    return {
        feature,
        tile,
        zoom: tile.style_zoom,
        geometry: Geo.geometryType(feature.geometry.type),
        meters_per_pixel: Geo.metersPerPixel(tile.coords.z),
        units_per_meter: Geo.units_per_meter[tile.coords.z]
    };
};

StyleParser.convertUnits = function(val, context, convert = 'meters') {
    if (typeof val === 'string') {
        var units = val.match(/([0-9.-]+)([a-z]+)/);
        if (units && units.length === 3) {
            val = parseFloat(units[1]);
            units = units[2];
        }

        // Convert to meters
        if (convert === 'meters') {
            // Convert from pixels
            if (units === 'px') {
                val *= Geo.metersPerPixel(context.zoom);
            }
            // Convert from kilometers
            else if (units === 'km') {
                val *= 1000;
            }
        }
    }
    else if (Array.isArray(val)) {
        // Array of arrays, e.g. zoom-interpolated stops
        if (Array.isArray(val[0])) {
            return val.map(v => { return [v[0], StyleParser.convertUnits(v[1], context, convert)]; });
        }
        // Array of values
        else {
            return val.map(v => { return StyleParser.convertUnits(v, context, convert); });
        }
    }
    return val;
};

// Takes a distance cache object and returns a distance value for this zoom
// (caching the result for future use)
// { value: original, zoom: { z: meters }, dynamic: function(){...} }
StyleParser.cacheDistance = function(val, context, convert = 'meters') {
    if (val.dynamic) {
        let v = val.dynamic(context);
        return v;
    }
    else if (val.zoom && val.zoom[convert] && val.zoom[convert][context.zoom]) {
        return val.zoom[convert][context.zoom];
    }
    else {
        // Dynamic function-based
        if (typeof val.value === 'function') {
            val.dynamic = val.value;
            let v = val.dynamic(context);
            return v;
        }
        // Array of zoom-interpolated stops, e.g. [zoom, value] pairs
        else {
            // Calculate value for current zoom
            val.zoom = val.zoom || {};
            let zunits = val.zoom[convert] = val.zoom[convert] || {};

            zunits[context.zoom] = StyleParser.convertUnits(val.value, context,
                convert === 'meters' && 'meters'); // convert to meters
            zunits[context.zoom] = Utils.interpolate(context.zoom, zunits[context.zoom]);

            return zunits[context.zoom];
        }
    }
};

// Cache previously parsed color strings
StyleParser.string_colors = {};
StyleParser.colorForString = function(string) {
    // Cached
    if (StyleParser.string_colors[string]) {
        return StyleParser.string_colors[string];
    }

    // Calculate and cache
    let color = parseCSSColor.parseCSSColor(string);
    if (color && color.length === 4) {
        color[0] /= 255;
        color[1] /= 255;
        color[2] /= 255;
        color[3] = 1;
    }
    else {
        color = StyleParser.defaults.color;
    }
    StyleParser.string_colors[string] = color;
    return color;
};

// Takes a color cache object and returns a color value for this zoom
// (caching the result for future use)
// { value: original, static: [r,g,b,a], zoom: { z: [r,g,b,a] }, dynamic: function(){...} }
StyleParser.cacheColor = function(val, context = {}) {
    if (val.dynamic) {
        let v = val.dynamic(context);
        if (v && v[3] == null) {
            v[3] = 1; // default alpha
        }
        return v;
    }
    else if (val.static) {
        return val.static;
    }
    else if (val.zoom && val.zoom[context.zoom]) {
        return val.zoom[context.zoom];
    }
    else {
        // Dynamic function-based color
        if (typeof val.value === 'function') {
            val.dynamic = val.value;
            let v = val.dynamic(context);
            if (v && v[3] == null) {
                v[3] = 1; // default alpha
            }
            return v;
        }
        // Single string color
        else if (typeof val.value === 'string') {
            val.static = StyleParser.colorForString(val.value);
            return val.static;
        }
        // Array of zoom-interpolated stops, e.g. [zoom, color] pairs
        else if (Array.isArray(val.value) && Array.isArray(val.value[0])) {
            if (!val.zoom) {
                val.zoom = {};
                // Parse any string colors inside stops
                for (let i=0; i < val.value.length; i++) {
                    let v = val.value[i];
                    if (v && typeof v[1] === 'string') {
                        v[1] = StyleParser.colorForString(v[1]);
                    }
                }
            }

            // Calculate color for current zoom
            val.zoom[context.zoom] = Utils.interpolate(context.zoom, val.value);
            val.zoom[context.zoom][3] = val.zoom[context.zoom][3] || 1; // default alpha
            return val.zoom[context.zoom];
        }
        // Single array color
        else {
            val.static = val.value;
            if (val.static && val.static[3] == null) {
                val.static[3] = 1; // default alpha
            }
            return val.static;
        }
    }
};

StyleParser.parseColor = function(val, context = {}) {
    if (typeof val === 'function') {
        val = val(context);
    }

    // Parse CSS-style colors
    // TODO: change all colors to use 0-255 range internally to avoid dividing and then re-multiplying in geom builder
    if (typeof val === 'string') {
        val = StyleParser.colorForString(val);
    }
    else if (Array.isArray(val) && Array.isArray(val[0])) {
        // Array of zoom-interpolated stops, e.g. [zoom, color] pairs
        for (let i=0; i < val.length; i++) {
            let v = val[i];
            if (typeof v[1] === 'string') {
                v[1] = StyleParser.colorForString(v[1]);
            }
        }

        if (context.zoom) {
            val = Utils.interpolate(context.zoom, val);
        }
    }

    // Defaults
    if (val) {
        // alpha
        if (val[3] == null) {
            val[3] = 1;
        }
    }
    else {
        val = [0, 0, 0, 1];
    }

    return val;
};

StyleParser.calculateOrder = function(order, context) {
    // Computed order
    if (typeof order === 'function') {
        order = order(context);
    }
    else if (typeof order === 'string') {
        // Order tied to feature property
        if (context.feature.properties[order]) {
            order = context.feature.properties[order];
        }
        // Explicit order value
        else {
            order = parseFloat(order);
        }
    }

    return order;
};
<|endoftext|>


<commit_before>/**
 * Disallows multiple spaces between identifiers, keywords, and any other token
 *
 * Type: `Boolean`
 *
 * Value: `true`
 *
 * #### Example
 *
 * ```js
 * "disallowMultipleSpaces": true
 * ```
 *
 * ##### Valid
 * ```js
 * var x = "hello";
 * function y() {}
 * ```
 *
 * ##### Invalid
 * ```js
 * var x  = "hello";
 * function  y() {}
 * ```
 */

var assert = require('assert');

module.exports = function() {};

module.exports.prototype = {

    configure: function(options) {
        assert(
            options === true ||
            typeof options === 'object' &&
            options.allowEOLComments === true,
            'options option requires true value ' +
            'or an object with `allowEOLComments` property'
        );

        this.allowEOLComments = options.allowEOLComments;
    },

    getOptionName: function() {
        return 'disallowMultipleSpaces';
    },

    check: function(file, errors) {
        // Iterate over all tokens (including comments)
        var _this = this;
        file.getTokens().forEach(function(token, index, tokens) {
            // If there are no trailing tokens, exit early
            var nextToken = tokens[index + 1];
            if (!nextToken) {
                return;
            }

            // If we are allowing EOL comments and the next token is an EOL comment skip it
            // We don't need to check the current token since EOL comments must be on separate lines from the next one
            if (_this.allowEOLComments && nextToken.type === 'Line') {
                return;
            }

            // Verify we have at most 1 space between this token and the next (won't fail for different lines)
            errors.assert.spacesBetween({
                token: token,
                nextToken: nextToken,
                atMost: 1,
            });
        });
    }

};
<commit_msg>Docs: clarify that disallowMultipleSpaces matches tabs and spaces<commit_after>/**
 * Disallows multiple indentation characters (tabs or spaces) between identifiers, keywords, and any other token
 *
 * Type: `Boolean`
 *
 * Value: `true`
 *
 * #### Example
 *
 * ```js
 * "disallowMultipleSpaces": true
 * ```
 *
 * ##### Valid
 * ```js
 * var x = "hello";
 * function y() {}
 * ```
 *
 * ##### Invalid
 * ```js
 * var x  = "hello";
 * function  y() {}
 * ```
 */

var assert = require('assert');

module.exports = function() {};

module.exports.prototype = {

    configure: function(options) {
        assert(
            options === true ||
            typeof options === 'object' &&
            options.allowEOLComments === true,
            'options option requires true value ' +
            'or an object with `allowEOLComments` property'
        );

        this.allowEOLComments = options.allowEOLComments;
    },

    getOptionName: function() {
        return 'disallowMultipleSpaces';
    },

    check: function(file, errors) {
        // Iterate over all tokens (including comments)
        var _this = this;
        file.getTokens().forEach(function(token, index, tokens) {
            // If there are no trailing tokens, exit early
            var nextToken = tokens[index + 1];
            if (!nextToken) {
                return;
            }

            // If we are allowing EOL comments and the next token is an EOL comment skip it
            // We don't need to check the current token since EOL comments must be on separate lines from the next one
            if (_this.allowEOLComments && nextToken.type === 'Line') {
                return;
            }

            // Verify we have at most 1 space between this token and the next (won't fail for different lines)
            errors.assert.spacesBetween({
                token: token,
                nextToken: nextToken,
                atMost: 1,
            });
        });
    }

};
<|endoftext|>


<commit_before>'use strict';

var helpers = require('../helpers');
var properties = require('known-css-properties').all;
var PROP_DIVIDER = '-';

/**
 * Combine the valid property array and the array of extras into a new array
 *
 * @param {Array} props - The list of default valid properties
 * @param {Array} extras - The user specified list of valid properties
 * @returns {Array} Combined list
 */
var getCombinedList = function (props, extras) {
  return props.concat(extras);
};

/**
 * Recursive function to build up an array of property names when encountering multiline properties
 *
 * @param {Object} valBlock - The current block node from within our value
 * @param {Object} currentProperty - The current base property name i.e. border-
 * @param {Number} propsCounted - The number of properties encountered in our multiline so far
 * @returns {Array} Array of objects containing our property and line/col info etc
 */
var buildPartialProperty = function (valBlock, currentProperty, propsCounted) {
  var propList = [];
  var propsEncountered = propsCounted;
  if (valBlock.contains('declaration')) {
    valBlock.forEach('declaration', function (node) {
      var prop = node.first('property');
      var value = node.first('value');
      propsEncountered++;

      if (prop.first().is('ident')) {
        if (value.contains('block')) {
          propList = propList.concat(
            buildPartialProperty(value.first('block'),
              {
                name: currentProperty.name + PROP_DIVIDER + prop.first('ident').content
              },
              propsEncountered
            )
          );
        }
        else {
          propList.push({
            name: currentProperty.name + PROP_DIVIDER + prop.first('ident').content,
            line: prop.first().start.line,
            col: prop.first().start.column,
            propsEncountered: propsEncountered
          });
        }
      }
    });
  }
  return propList;
};

module.exports = {
  'name': 'no-misspelled-properties',
  'defaults': {
    'extra-properties': []
  },
  'detect': function (ast, parser) {
    var result = [];
    var toSkip = 0;
    var propertyList = getCombinedList(properties, parser.options['extra-properties']);

    ast.traverseByType('declaration', function (node) {
      var prop = node.first('property');
      var containsInterp = prop.contains('interpolation');
      // If we've already checked declarations in a multiline we can skip those decs here
      if (toSkip) {
        toSkip--;
        return !toSkip;
      }
      // make sure our first node within our property is an ident
      if (!prop.first() || !prop.first().is('ident')) {
        return false;
      }

      var curProperty = prop.first() && prop.first().is('ident') && prop.first('ident').content;
      var value = node.first('value');
      var fullProperties = [];

      if (value.contains('block')) {
        // encountered a multiline property, we should build the property list here
        fullProperties = buildPartialProperty(
          value.first('block'),
          {
            name: curProperty,
            line: prop.first('ident').start.line,
            col: prop.first('ident').start.column
          },
          0
        );
      }
      // If we have multiline properties
      if (fullProperties.length) {
        fullProperties.forEach(function (constrProp) {
          // Add the number of propertiy declarations we've already checked here so we can skip them
          toSkip += constrProp.propsEncountered;
          // Check if the property exists in our list
          if (propertyList.indexOf(constrProp.name) === -1) {
            result = helpers.addUnique(result, {
              'ruleId': parser.rule.name,
              'line': constrProp.line,
              'column': constrProp.col,
              'message': 'Property `' + constrProp.name + '` appears to be spelled incorrectly',
              'severity': parser.severity
            });
          }
        });
      }
      else {
        if (curProperty && curProperty.length > 0) {
          /*
           * If our property name contains interpolation we need to make a best guess by using a
           * partial string match as we can't be 100% on the context
           */
          if (containsInterp) {
            if (!helpers.isPartialStringMatch(curProperty, propertyList)) {
              result = helpers.addUnique(result, {
                'ruleId': parser.rule.name,
                'line': node.start.line,
                'column': node.start.column,
                'message': 'Property `' + curProperty + '` appears to be spelled incorrectly',
                'severity': parser.severity
              });
            }
          }
          // Otherwise it's just a normal string property name, lets check it here
          else if (propertyList.indexOf(curProperty) === -1) {
            result = helpers.addUnique(result, {
              'ruleId': parser.rule.name,
              'line': node.start.line,
              'column': node.start.column,
              'message': 'Property `' + curProperty + '` appears to be spelled incorrectly',
              'severity': parser.severity
            });
          }
        }
      }
      return false;
    });

    return result;
  }
};
<commit_msg>:art: tidy up refactor<commit_after>'use strict';

var helpers = require('../helpers');
var properties = require('known-css-properties').all;

/**
 * Combine the valid property array and the array of extras into a new array
 *
 * @param {Array} props - The list of default valid properties
 * @param {Array} extras - The user specified list of valid properties
 * @returns {Array} Combined list
 */
var getCombinedList = function (props, extras) {
  return props.concat(extras);
};

/**
 * Combines the base property name with the current property name to correct a full property name
 *
 * @param {String} baseName - The base property name
 * @param {String} name - The property name to append to our base property name
 * @returns {String} The constructed property name
 */
var generateName = function (baseName, name) {
  return baseName + '-' + name;
};

/**
 * Recursive function to build up an array of property names when encountering multiline properties
 *
 * @param {Object} valBlock - The current block node from within our value
 * @param {Object} currentProperty - The current base property name i.e. border-
 * @param {Number} propsCounted - The number of properties encountered in our multiline so far
 * @returns {Array} Array of objects containing our property and line/col info etc
 */
var buildPartialProperty = function (valBlock, currentProperty, propsCounted) {
  var propList = [];
  var propsEncountered = propsCounted;
  if (valBlock.contains('declaration')) {
    valBlock.forEach('declaration', function (node) {
      var prop = node.first('property');
      var value = node.first('value');
      propsEncountered++;

      if (prop.first().is('ident')) {
        if (value.contains('block')) {
          propList = propList.concat(
            buildPartialProperty(value.first('block'),
              {
                name: generateName(currentProperty.name, prop.first('ident').content)
              },
              propsEncountered
            )
          );
        }
        else {
          propList.push({
            name: generateName(currentProperty.name, prop.first('ident').content),
            line: prop.first().start.line,
            col: prop.first().start.column,
            propsEncountered: propsEncountered
          });
        }
      }
    });
  }
  return propList;
};

module.exports = {
  'name': 'no-misspelled-properties',
  'defaults': {
    'extra-properties': []
  },
  'detect': function (ast, parser) {
    var result = [];
    var toSkip = 0;
    var propertyList = getCombinedList(properties, parser.options['extra-properties']);

    ast.traverseByType('declaration', function (node) {
      var prop = node.first('property');
      var containsInterp = prop.contains('interpolation');
      // If we've already checked declarations in a multiline we can skip those decs here
      if (toSkip) {
        toSkip--;
        return !toSkip;
      }
      // make sure our first node within our property is an ident
      if (!prop.first() || !prop.first().is('ident')) {
        return false;
      }

      var curProperty = prop.first() && prop.first().is('ident') && prop.first('ident').content;
      var value = node.first('value');
      var fullProperties = [];

      if (value.contains('block')) {
        // encountered a multiline property, we should build the property list here
        fullProperties = buildPartialProperty(
          value.first('block'),
          {
            name: curProperty,
            line: prop.first('ident').start.line,
            col: prop.first('ident').start.column
          },
          0
        );
      }
      // If we have multiline properties
      if (fullProperties.length) {
        fullProperties.forEach(function (constrProp) {
          // Add the number of property declarations we've already checked here so we can skip them
          toSkip += constrProp.propsEncountered;
          // Check if the property exists in our list
          if (propertyList.indexOf(constrProp.name) === -1) {
            result = helpers.addUnique(result, {
              'ruleId': parser.rule.name,
              'line': constrProp.line,
              'column': constrProp.col,
              'message': 'Property `' + constrProp.name + '` appears to be spelled incorrectly',
              'severity': parser.severity
            });
          }
        });
      }
      else if (curProperty && curProperty.length) {
        /*
         * If our property name contains interpolation we need to make a best guess by using a
         * partial string match as we can't be 100% on the context
         */
        if (containsInterp) {
          if (!helpers.isPartialStringMatch(curProperty, propertyList)) {
            result = helpers.addUnique(result, {
              'ruleId': parser.rule.name,
              'line': node.start.line,
              'column': node.start.column,
              'message': 'Property `' + curProperty + '` appears to be spelled incorrectly',
              'severity': parser.severity
            });
          }
        }
        // Otherwise it's just a normal string property name, lets check it here
        else if (propertyList.indexOf(curProperty) === -1) {
          result = helpers.addUnique(result, {
            'ruleId': parser.rule.name,
            'line': node.start.line,
            'column': node.start.column,
            'message': 'Property `' + curProperty + '` appears to be spelled incorrectly',
            'severity': parser.severity
          });
        }
      }
      return false;
    });

    return result;
  }
};
<|endoftext|>


<commit_before>// Copyright © 2011 Aapo Laitinen <aapo.laitinen@iki.fi> unless otherwise noted

define(['engine/util/gllib', 'engine/util/mathlib', 'engine/world/Actor', 'engine/world/Player', 'tanks/world/Projectile', 'engine/util/JointedMesh!tanks/models/tank2.json!Tank,Turret'], function (gllib, mathlib, Actor, Player, Projectile, vehicleMesh) {

	var tempModelToWorld = gllib.Mat4.identity();
	var tempJointMatrices = [
		gllib.Mat4.identity(),
		gllib.Mat4.identity()
	];

	register('Vehicle', Vehicle);
	inherits(Vehicle, Actor);
	function Vehicle(opt /* id, player, x, y */) {
		Actor.call(this, opt);
		this.defaults(opt, {
			id: Number,
			player: Player,
			angle: 0,
			rotationSpeed: 3,
			speed: 30 * 1024,
			targetX: null,
			targetY: null,
			firingRadius: 300 * 1024,
			collisionRadius: 15 * 1024,
			reloadMsecs: 4000,
			projectileSpeed: 90 * 1024,
			currentReloadMsecs: [0, 0, 0, 0],
			reloadingCount: 0
		});
		this.dflAngle = 0;
	}

	Vehicle.prototype.setPosition = function (x, y) {
		this.dflX += x - this.x;
		this.dflY += y - this.y;
		this.x = x;
		this.y = y;
	};

	Vehicle.prototype.tick = function () {
		this.dflAngle = 0;
		this.dflX = 0;
		this.dflY = 0;
		if (this.targetX && this.targetY) {
			// Orient towards a waypoint if we have not reached it yet
			var angle = mathlib.angle(this.x, this.y, this.targetX, this.targetY);
			var angleDelta = mathlib.angleDelta(this.angle, angle);
			var rotationPerTick = this.rotationSpeed / this.game.ticksPerSecond;
			if (Math.abs(angleDelta) > rotationPerTick) {
				var lastAngle = this.angle;
				this.angle = mathlib.normalizeAngle(this.angle + angleDelta / Math.abs(angleDelta) * rotationPerTick);
				this.dflAngle = mathlib.angleDelta(lastAngle, this.angle);
			} else {
				this.dflAngle = mathlib.angleDelta(this.angle, angle);
				this.angle = angle;
			}
			var delta = mathlib.anglePoint(this.angle, Math.round(this.speed / this.game.ticksPerSecond));
			if (this.game.map.isPassable(this.x + delta[0], this.y + delta[1])
					&& !this.wouldCollideWithSomeActor(this.x + delta[0], this.y + delta[1])) {
				this.setPosition(this.x + delta[0], this.y + delta[1]);
			}
			if (mathlib.manhattanDistance(this.x, this.y, this.targetX, this.targetY) <= 5120) {
				this.targetX = null;
				this.targetY = null;
			}
		}
		// Continue reloading if not reloaded already
		if (this.reloadingCount > 0) {
			for (var i = 0; i < this.currentReloadMsecs.length; ++i) {
				if (this.currentReloadMsecs[i] > 0) {
					this.currentReloadMsecs[i] -= this.game.msecsPerTick;
					if (this.currentReloadMsecs[i] <= 0) {
						--this.reloadingCount;
					}
				}
			}
		}
		// Fire at any enemies in range
		if (this.reloadingCount < this.currentReloadMsecs.length) {
			for (var idx in this.game.actors) {
				var actor = this.game.actors[idx];
				if (actor.player !== this.player
						&& 'firingRadius' in actor   // FIXME: Use something more role-specific
						&& mathlib.distance(actor.x, actor.y, this.x, this.y) < this.firingRadius) {
					this.fireAtPos(actor.x, actor.y);
					break;
				}
			}
		}
	};

	Vehicle.prototype.draw = function (gl, client, viewport) {
		var mtw = tempModelToWorld;
		var joints = tempJointMatrices;
		var factor = client.factor;
		var angleRad = (this.angle - this.dflAngle * factor);
		// Rotation
		mtw[0] = Math.cos(angleRad);
		mtw[4] = -Math.sin(angleRad);
		mtw[1] = Math.sin(angleRad);
		mtw[5] = Math.cos(angleRad);
		// Translation
		mtw[12] = (this.x - this.dflX * factor) / 1024;
		mtw[13] = (this.y - this.dflY * factor) / 1024;

		vehicleMesh.draw(gl, viewport, mtw, joints, this.player.color);

		// If selected, draw the selection indicator
		if (client.selectedActors.indexOf(this) >= 0) {
			client.uiRenderer.addRectModel(viewport.worldToClip, mtw, 40, 40);
		}

		/*
			// If reloading, draw the reload indicator
			if (this.reloadingCount > 0 && this.player == this.game.localPlayer) {
				for (var i = 0; i < this.currentReloadMsecs.length; ++i) {
					if (this.currentReloadMsecs[i] > 0) {
						ctx.fillStyle = uiCtx.indicatorStyle;
						ctx.fillRect(-15, -35 - 6 * i, 30 * this.currentReloadMsecs[i] / this.reloadMsecs, 4);
						ctx.strokeStyle = uiCtx.indicatorStyle;
						ctx.strokeRect(-15, -35 - 6 * i, 30, 4);
					}
				}
			}
			// If there is a target, draw the target indicator
			if (this.targetX && this.targetY && this.player == this.game.localPlayer) {
				var distance = mathlib.distance(
					this.x - this.dflX * factor,
					this.y - this.dflY * factor,
					this.targetX,
					this.targetY
				);
				ctx.strokeStyle = uiCtx.indicatorStyle;
				if (distance > 49152) {
					ctx.beginPath();
					var angle = mathlib.angle(
						this.x - this.dflX * factor,
						this.y - this.dflY * factor,
						this.targetX,
						this.targetY
					);
					var delta = mathlib.anglePoint(angle, 35);
					ctx.moveTo(delta[0], delta[1]);
					delta = mathlib.anglePoint(angle - Math.PI, 13);
					ctx.lineTo(
						(this.targetX - (this.x - this.dflX * factor)) / 1024 + delta[0],
						(this.targetY - (this.y - this.dflY * factor)) / 1024 + delta[1]
					);
					ctx.stroke();
				}
				ctx.beginPath();
				ctx.arc(
					(this.targetX - (this.x - this.dflX * factor)) / 1024,
					(this.targetY - (this.y - this.dflY * factor)) / 1024,
					5, 0, Math.PI * 2, false
				);
				ctx.stroke();
			}
		*/
	};

	Vehicle.prototype.addFiringArc = function (ctx, expand, factor) {
		if (this.player == this.game.localPlayer) {
			ctx.arc(
				(this.x - this.dflX * factor) / 1024,
				(this.y - this.dflY * factor) / 1024,
				(this.firingRadius >> 10) + expand,
				0, Math.PI * 2, false
			);
		}
	};

	Vehicle.prototype.clickTest = function (x, y, client) {
		var factor = client.factor;
		return mathlib.distance(
			this.x - this.dflX * factor,
			this.y - this.dflY * factor,
			x,
			y
		) <= 20480;
	};

	// Returns true if this actor is selectable by the local player
	Vehicle.prototype.isSelectable = function () {
		return true;
	};

	Vehicle.prototype.projectileHitTest = function (x, y) {
		return mathlib.distance(this.x, this.y, x, y) <= 15360;
	};

	Vehicle.prototype.validateMove = function (player, x, y) {
		return this.player === player;
	};

	Vehicle.prototype.issueMove = function (x, y) {
		this.game.issueCommand(['GO', this.id, x, y]);
	};

	Vehicle.prototype.performMove = function (x, y) {
		this.targetX = x;
		this.targetY = y;
	};

	Vehicle.prototype.fireAtPos = function (x, y) {
		for (var gunIndex = 0; gunIndex < this.currentReloadMsecs.length; ++gunIndex) {
			if (this.currentReloadMsecs[gunIndex] <= 0) {
				break;
			}
		}
		if (gunIndex < this.currentReloadMsecs.length &&
				mathlib.distance(x, y, this.x, this.y) < this.firingRadius) {
			this.game.createActor(Projectile, {
				'player': this.player,
				'x': this.x, 'y': this.y,
				'angle': mathlib.angle(this.x, this.y, x, y),
				'range': mathlib.distance(this.x, this.y, x, y),
				'speed': this.projectileSpeed
			});
			this.currentReloadMsecs[gunIndex] = this.reloadMsecs;
			++this.reloadingCount;
		}
	};

	Vehicle.prototype.issueFireAtPos = function (x, y) {
		if (this.reloadingCount < this.currentReloadMsecs.length &&
				mathlib.distance(x, y, this.x, this.y) < this.firingRadius) {
			this.game.issueCommand(['FR', this.id, x, y]);
			return true;
		}
		return false;
	};

	// FIXME: Use a suitable data structure to determine collisions in better
	// than O(n^2).
	Vehicle.prototype.wouldCollideWithSomeActor = function (x, y) {
		for (var idx in this.game.actors) {
			var actor = this.game.actors[idx];
			if (actor !== this
					&& 'collisionRadius' in actor
					&& mathlib.distance(actor.x, actor.y, x, y) < actor.collisionRadius + this.collisionRadius) {
				return true;
			}
		}
		return false;
	};

	return Vehicle;

});<commit_msg>First, ugly implementation of turret rotation<commit_after>// Copyright © 2011 Aapo Laitinen <aapo.laitinen@iki.fi> unless otherwise noted

define(['engine/util/gllib', 'engine/util/mathlib', 'engine/world/Actor', 'engine/world/Player', 'tanks/world/Projectile', 'engine/util/JointedMesh!tanks/models/tank2.json!Tank,Turret'], function (gllib, mathlib, Actor, Player, Projectile, vehicleMesh) {

	var tempModelToWorld = gllib.Mat4.identity();
	var tempJointMatrices = [
		gllib.Mat4.identity(),
		gllib.Mat4.identity()
	];

	register('Vehicle', Vehicle);
	inherits(Vehicle, Actor);
	function Vehicle(opt /* id, player, x, y */) {
		Actor.call(this, opt);
		this.defaults(opt, {
			id: Number,
			player: Player,
			angle: 0,
			turretAngle: 0,
			rotationSpeed: 3,
			speed: 30 * 1024,
			targetX: null,
			targetY: null,
			firingRadius: 300 * 1024,
			collisionRadius: 15 * 1024,
			reloadMsecs: 4000,
			projectileSpeed: 90 * 1024,
			currentReloadMsecs: [0, 0, 0, 0],
			reloadingCount: 0
		});
		this.dflAngle = 0;
		this.dflTurretAngle = 0;
	}

	Vehicle.prototype.setPosition = function (x, y) {
		this.dflX += x - this.x;
		this.dflY += y - this.y;
		this.x = x;
		this.y = y;
	};

	Vehicle.prototype.tick = function () {

		function rotateTowards(angle, angleProp, dflAngleProp) {
			var angleDelta = mathlib.angleDelta(this[angleProp], angle);
			var rotationPerTick = this.rotationSpeed / this.game.ticksPerSecond;
			if (Math.abs(angleDelta) > rotationPerTick) {
				var lastAngle = this[angleProp];
				this[angleProp] = mathlib.normalizeAngle(this[angleProp] + angleDelta / Math.abs(angleDelta) * rotationPerTick);
				this[dflAngleProp] = mathlib.angleDelta(lastAngle, this[angleProp]);
			} else {
				this[dflAngleProp] = mathlib.angleDelta(this[angleProp], angle);
				this[angleProp] = angle;
			}
		}

		this.dflAngle = 0;
		this.dflX = 0;
		this.dflY = 0;
		this.dflTurretAngle = 0;
		if (this.targetX && this.targetY) {
			// Orient towards a waypoint if we have not reached it yet
			var angle = mathlib.angle(this.x, this.y, this.targetX, this.targetY);
			rotateTowards.call(this, angle, 'angle', 'dflAngle');
			var delta = mathlib.anglePoint(this.angle, Math.round(this.speed / this.game.ticksPerSecond));
			if (this.game.map.isPassable(this.x + delta[0], this.y + delta[1])
					&& !this.wouldCollideWithSomeActor(this.x + delta[0], this.y + delta[1])) {
				this.setPosition(this.x + delta[0], this.y + delta[1]);
			}
			if (mathlib.manhattanDistance(this.x, this.y, this.targetX, this.targetY) <= 5120) {
				this.targetX = null;
				this.targetY = null;
			}
		}
		// Continue reloading if not reloaded already
		if (this.reloadingCount > 0) {
			for (var i = 0; i < this.currentReloadMsecs.length; ++i) {
				if (this.currentReloadMsecs[i] > 0) {
					this.currentReloadMsecs[i] -= this.game.msecsPerTick;
					if (this.currentReloadMsecs[i] <= 0) {
						--this.reloadingCount;
					}
				}
			}
		}
		// Fire at any enemies in range
		var targetUnit = null;
		for (var idx in this.game.actors) {
			var actor = this.game.actors[idx];
			if (actor.player !== this.player
					&& 'firingRadius' in actor   // FIXME: Use something more role-specific
					&& mathlib.distance(actor.x, actor.y, this.x, this.y) < this.firingRadius) {
				targetUnit = actor;
				break;
			}
		}
		if (targetUnit) {
			// If there is a target, rotate the turret towards the target and fire
			var angleTowardsTarget = mathlib.angle(this.x, this.y, targetUnit.x, targetUnit.y);
			var turretAngle = mathlib.normalizeAngle(angleTowardsTarget - this.angle);
			rotateTowards.call(this, turretAngle, 'turretAngle', 'dflTurretAngle');
			if (this.turretAngle === turretAngle
					&& this.reloadingCount < this.currentReloadMsecs.length) {
				this.fireAtPos(targetUnit.x, targetUnit.y);
			}
		} else {
			// Otherwise make sure the turrent points forward
			rotateTowards.call(this, 0, 'turretAngle', 'dflTurretAngle');
		}
	};

	Vehicle.prototype.draw = function (gl, client, viewport) {
		var mtw = tempModelToWorld;
		var joints = tempJointMatrices;
		var factor = client.factor;
		var angleRad = (this.angle - this.dflAngle * factor);
		// Rotation
		mtw[0] = Math.cos(angleRad);
		mtw[4] = -Math.sin(angleRad);
		mtw[1] = Math.sin(angleRad);
		mtw[5] = Math.cos(angleRad);
		// Translation
		mtw[12] = (this.x - this.dflX * factor) / 1024;
		mtw[13] = (this.y - this.dflY * factor) / 1024;

		// Turret rotation
		var turret = joints[1];
		var turretAngleRad = (this.turretAngle - this.dflTurretAngle * factor);
		turret[0] = Math.cos(turretAngleRad);
		turret[4] = -Math.sin(turretAngleRad);
		turret[1] = Math.sin(turretAngleRad);
		turret[5] = Math.cos(turretAngleRad);

		vehicleMesh.draw(gl, viewport, mtw, joints, this.player.color);

		// If selected, draw the selection indicator
		if (client.selectedActors.indexOf(this) >= 0) {
			client.uiRenderer.addRectModel(viewport.worldToClip, mtw, 40, 40);
		}

		/*
			// If reloading, draw the reload indicator
			if (this.reloadingCount > 0 && this.player == this.game.localPlayer) {
				for (var i = 0; i < this.currentReloadMsecs.length; ++i) {
					if (this.currentReloadMsecs[i] > 0) {
						ctx.fillStyle = uiCtx.indicatorStyle;
						ctx.fillRect(-15, -35 - 6 * i, 30 * this.currentReloadMsecs[i] / this.reloadMsecs, 4);
						ctx.strokeStyle = uiCtx.indicatorStyle;
						ctx.strokeRect(-15, -35 - 6 * i, 30, 4);
					}
				}
			}
			// If there is a target, draw the target indicator
			if (this.targetX && this.targetY && this.player == this.game.localPlayer) {
				var distance = mathlib.distance(
					this.x - this.dflX * factor,
					this.y - this.dflY * factor,
					this.targetX,
					this.targetY
				);
				ctx.strokeStyle = uiCtx.indicatorStyle;
				if (distance > 49152) {
					ctx.beginPath();
					var angle = mathlib.angle(
						this.x - this.dflX * factor,
						this.y - this.dflY * factor,
						this.targetX,
						this.targetY
					);
					var delta = mathlib.anglePoint(angle, 35);
					ctx.moveTo(delta[0], delta[1]);
					delta = mathlib.anglePoint(angle - Math.PI, 13);
					ctx.lineTo(
						(this.targetX - (this.x - this.dflX * factor)) / 1024 + delta[0],
						(this.targetY - (this.y - this.dflY * factor)) / 1024 + delta[1]
					);
					ctx.stroke();
				}
				ctx.beginPath();
				ctx.arc(
					(this.targetX - (this.x - this.dflX * factor)) / 1024,
					(this.targetY - (this.y - this.dflY * factor)) / 1024,
					5, 0, Math.PI * 2, false
				);
				ctx.stroke();
			}
		*/
	};

	Vehicle.prototype.addFiringArc = function (ctx, expand, factor) {
		if (this.player == this.game.localPlayer) {
			ctx.arc(
				(this.x - this.dflX * factor) / 1024,
				(this.y - this.dflY * factor) / 1024,
				(this.firingRadius >> 10) + expand,
				0, Math.PI * 2, false
			);
		}
	};

	Vehicle.prototype.clickTest = function (x, y, client) {
		var factor = client.factor;
		return mathlib.distance(
			this.x - this.dflX * factor,
			this.y - this.dflY * factor,
			x,
			y
		) <= 20480;
	};

	// Returns true if this actor is selectable by the local player
	Vehicle.prototype.isSelectable = function () {
		return true;
	};

	Vehicle.prototype.projectileHitTest = function (x, y) {
		return mathlib.distance(this.x, this.y, x, y) <= 15360;
	};

	Vehicle.prototype.validateMove = function (player, x, y) {
		return this.player === player;
	};

	Vehicle.prototype.issueMove = function (x, y) {
		this.game.issueCommand(['GO', this.id, x, y]);
	};

	Vehicle.prototype.performMove = function (x, y) {
		this.targetX = x;
		this.targetY = y;
	};

	Vehicle.prototype.fireAtPos = function (x, y) {
		for (var gunIndex = 0; gunIndex < this.currentReloadMsecs.length; ++gunIndex) {
			if (this.currentReloadMsecs[gunIndex] <= 0) {
				break;
			}
		}
		if (gunIndex < this.currentReloadMsecs.length &&
				mathlib.distance(x, y, this.x, this.y) < this.firingRadius) {
			this.game.createActor(Projectile, {
				'player': this.player,
				'x': this.x, 'y': this.y,
				'angle': mathlib.angle(this.x, this.y, x, y),
				'range': mathlib.distance(this.x, this.y, x, y),
				'speed': this.projectileSpeed
			});
			this.currentReloadMsecs[gunIndex] = this.reloadMsecs;
			++this.reloadingCount;
		}
	};

	Vehicle.prototype.issueFireAtPos = function (x, y) {
		if (this.reloadingCount < this.currentReloadMsecs.length &&
				mathlib.distance(x, y, this.x, this.y) < this.firingRadius) {
			this.game.issueCommand(['FR', this.id, x, y]);
			return true;
		}
		return false;
	};

	// FIXME: Use a suitable data structure to determine collisions in better
	// than O(n^2).
	Vehicle.prototype.wouldCollideWithSomeActor = function (x, y) {
		for (var idx in this.game.actors) {
			var actor = this.game.actors[idx];
			if (actor !== this
					&& 'collisionRadius' in actor
					&& mathlib.distance(actor.x, actor.y, x, y) < actor.collisionRadius + this.collisionRadius) {
				return true;
			}
		}
		return false;
	};

	return Vehicle;

});<|endoftext|>


<commit_before>'use strict';

var multer  = require('multer')
var upload = multer({ dest: 'uploads/' })
var express = require('express');
var mongoose = require('mongoose');
var Polls = require('../models/polls.js');
//http://www.clementinejs.com/tutorials/tutorial-passport.html

//For creating polls

module.exports = function (app, db) {

var bodyParser = require('body-parser')
app.use(bodyParser.json() );       // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({     // to support URL-encoded bodies
  extended: true
}));

app.route('/create')
  .get(function (req, res) {
    res.sendFile(process.cwd() + '/public/create.html');
});

app.post('/create', upload.array(),  function (req, res, next) {

      console.log(req.body);

	  res.status(204).end();
});

//user visits poll creation page

};<commit_msg>multer tweaks<commit_after>'use strict';

var multer  = require('multer')
var upload = multer({ dest: 'uploads/' })
var express = require('express');
//var models = require('./app/models/polls.js');

//For creating polls

module.exports = function (app, db) {

var bodyParser = require('body-parser')
app.use(bodyParser.json() );       // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({     // to support URL-encoded bodies
  extended: true
}));

app.route('/create')
  .get(function (req, res) {
    res.sendFile(process.cwd() + '/public/create.html');
});

app.post('/create',  upload.array(), function (req, res, next) {

    console.log(req.body)

	  res.status(204).end();
});

//user visits poll creation page

};<|endoftext|>


<commit_before>var express = require('express')
,	Biz = require('./../models/biz')
,	router = express.Router();

function allBusiness(req, res, next) {
	Biz.find({}, function (err, businesses) {
		if (err) {
			next(err);
		} else {
			res.send(businesses);
		}
	});
}

function justOneBusiness(req, res, next) {
	Biz.findOne({name: req.params.name}, function (err, business) {
		if (err) {
			next(err);
		} else {
			res.send(business);
		}
	});
}

function addBusiness(req, res, next) {
	var business = new Biz({name: req.body.name, website: req.body.website, type: req.body.type});
	business.save(function (err, business) {
		if (err) {
			next(err);
		} else {
			res.send({message: "Business sucessfully added to database.", business: business});
		}
	});
}

function updateBusiness(req, res, next) {
	Biz.update({name: req.params.name}, {name: req.body.name, website: req.body.website, type: req.body.type}, function (err, updated) {
		if (err) {
			next(err);
		} else {
			res.send({message: "Business updated", updated: updated});
		}
	});
}

function removeBusiness(req, res, next) {
	Biz.remove({name: req.params.name}, function (err, deleted) {
		if (err) {
			next(err);
		} else {
			res.send({message: "Business deleted.", deleted: deleted});
		}
	});
}

// Route middleware that will happen on every request.
router.use(function (req, res, next) {

	// Log each request to the console.
	console.log(req.method, req.url);

	// Make sure the app go to the next routes and don't stop here.
	next();
});

router.get('/', function (req, res) {
	res.redirect('/business');
});

router.route('/business')

	.get(allBusiness)

	.post(addBusiness);

router.route('/business/:name')

	.get(justOneBusiness)

	.put(updateBusiness)

	.delete(removeBusiness);

module.exports = router;<commit_msg>Changed object structure<commit_after>var express = require('express')
,	Biz = require('./../models/biz')
,	router = express.Router();

function allBusiness(req, res, next) {
	Biz.find({}, function (err, businesses) {
		if (err) {
			next(err);
		} else {
			res.send(businesses);
		}
	});
}

function justOneBusiness(req, res, next) {
	Biz.findOne({name: req.params.name}, function (err, business) {
		if (err) {
			next(err);
		} else {
			res.send(business);
		}
	});
}

function addBusiness(req, res, next) {
	var business = new Biz({name: req.body.name, type: req.body.type, logo: req.body.logo, website: req.body.website});
	business.save(function (err, business) {
		if (err) {
			next(err);
		} else {
			res.send({message: "Business sucessfully added to database.", business: business});
		}
	});
}

function updateBusiness(req, res, next) {
	Biz.update({name: req.params.name}, {name: req.body.name, type: req.body.type, logo: req.body.logo, website: req.body.website}, function (err, updated) {
		if (err) {
			next(err);
		} else {
			res.send({message: "Business updated", updated: updated});
		}
	});
}

function removeBusiness(req, res, next) {
	Biz.remove({name: req.params.name}, function (err, deleted) {
		if (err) {
			next(err);
		} else {
			res.send({message: "Business deleted.", deleted: deleted});
		}
	});
}

// Route middleware that will happen on every request.
router.use(function (req, res, next) {

	// Log each request to the console.
	console.log(req.method, req.url);

	// Make sure the app go to the next routes and don't stop here.
	next();
});

router.get('/', function (req, res) {
	res.redirect('/business');
});

router.route('/business')

	.get(allBusiness)

	.post(addBusiness);

router.route('/business/:name')

	.get(justOneBusiness)

	.put(updateBusiness)

	.delete(removeBusiness);

module.exports = router;<|endoftext|>


<commit_before>var routes = (function(log4js, express) {
  var log = log4js.getLogger('/routes');
  var routes = express.Router();

  routes.get('/', function (req, res, next) {
    res.render('index',{title: 'New routing'});
  });

  routes.get('/jobs', function (req, res, next) {
    res.render('jobs/jobs',{title: 'New routing'});
  });

  routes.post('/notify', function(req,res, next) {
    res.send('POST received\n');
  });

  return routes;
});

module.exports = routes;
<commit_msg>example of using awesome middleware to handle variable urls<commit_after>var routes = (function(log4js, express) {
  var log = log4js.getLogger('/routes');
  var routes = express.Router();

  routes.get('/', function (req, res, next) {
    res.render('index',{title: 'New routing'});
  });

  routes.get('/jobs/', function (req, res, next) {
    res.render('jobs/jobs',{title: 'New routing'});
  });

  routes.post('/notify/:id', function (req, res, next) {console.log('this is middleware, id is ' + req.params.id); next();}, function(req,res, next) {
    res.send('POST received\n');
  });

  return routes;
});

module.exports = routes;
<|endoftext|>


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

  // Panel model
  var panel = panel || {};

  // Select menu
  var $select = $('[data-options]')[0];

  // Number input
  var $number = $('input[type=number]')[0];

  // Palette container
  var $palette = $('.palette')[0];

  // Theme title
  var $themeTitle = $('#currentTheme')[0];

  // Theme JSON file
  panel.themeJSON = '/dist/scripts/themes.json';

  // Current theme
  panel.currentTheme = '';

  // Default theme
  panel.defaultTheme = '3024';

  // Default fontSize
  panel.defaultFontSize = 12;

  function themeLookUp(theme){
    for (var i = 0; i < panel.themes.length; i++){
      if (panel.themes[i].name === theme) {
        return panel.themes[i].colors;
      }
    }
  }

  function createLI(palette){
    palette = palette.filter(function(item, pos, self) {
      return self.indexOf(item) === pos;
    });

    for (var i = 0; i < palette.length; i++){
      // Create the list item:
      var item = document.createElement('li');

      // Style item
      item.style.backgroundColor = palette[i];
      item.style.width = (100 / palette.length) + '%';

      // Add it to the list:
      $palette.appendChild(item);
    }
  }

  // Build select menus like ngOptions
  function _buildSelectMenu(menu, object){
    var options, array;

    // Get the data attribute value
    options = menu.dataset.options;

    // Clean string and create array
    options = options.replace(/in\s/g, '').split(' ');

    // Assign array from object by property name
    // using the value from the last item in options
    array = object[options[options.length - 1]];

    for (var j = 0; j < array.length; j++){

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

      // Assign option value & text from array
      option.value = array[j].name.replace(/\s+/g, '-').toLowerCase();
      option.text  = array[j].name;

      // Select currentTheme option
      if (object.currentTheme === array[j].name){
        option.selected = 'selected';
      }

      menu.add(option, null);
    }

    return menu;
  }

  // Set & save theme based on select menu change event
  function setTheme(event, obj){
    function save(theme){
      storage.set({ 'devtools-theme': theme.value },
      function(){ panel.currentTheme = theme.text; });
    }
    if (event && event.type === 'change'){
      $themeTitle.style.display = 'none';
      var el     = event.target || event.srcElement;
      var option = el.options[el.selectedIndex];
      save(option);
      $('.alert')[0].style.display = 'block';
    } else if (event === null && obj){
      save(obj);
    }
  }

  // Set & save font size based on input menu change event
  function setFontSize(event, value){
    function save(fontSize){
      storage.set({ 'devtools-fontSize': fontSize },
      function(){ panel.currentFontSize = fontSize; });
    }
    if (event && event.type === 'change'){
      var el = event.target || event.srcElement;
      save(el.value);
      $('.alert')[0].style.display = 'block';
    } else if (event === null && value){
      save(value);
    }
  }

  // Object observer for current theme
  function observer(changes){
    function updatePalette(theme){
      var children = $palette.querySelectorAll('li');

      // Remove children from $palette
      if (children.length){
        for (var i = 0; i < children.length; i++){
          $palette.removeChild(children[i]);
        }
      }

      createLI(themeLookUp(theme));
    }

    changes.forEach(function(change){
      if (change.name === 'currentTheme') {
        $themeTitle.innerHTML = change.object.currentTheme;
        $themeTitle.style.display = 'block';
        updatePalette(change.object.currentTheme);
      } else if (change.name === 'currentFontSize') {
        $number.value = change.object.currentFontSize;
      }
    });
  }

  // Get fontSize from chrome sync
  function getFontSize(value){
    if (!value){
      setFontSize(null, panel.defaultFontSize);
      return panel.defaultFontSize;
    } else {
      return value;
    }
  }

  // Get theme from chrome sync
  function getTheme(array, string){
    if (!array || !string){
      setTheme(null, {
        value: panel.defaultTheme.replace(/\s+/g, '-').toLowerCase(),
        text: panel.defaultTheme
      });
      return panel.defaultTheme;
    }
    for (var i = 0; i < array.length; i++){
      if (array[i].name.replace(/\s+/g, '-').toLowerCase() === string){
        return array[i].name;
      }
    }
  }

  // Initialize theme panel
  function init(){

    function callback(){
      // Listen for changes to the select menu
      $select.addEventListener('change', setTheme);

      // Listen for changes to the select menu
      $number.addEventListener('change', setFontSize);

      // Observe changes to panel model
      Object.observe(panel, observer, ['add', 'update']);

      // Get current theme from Chrome sync
      storage.get('devtools-theme', function(object){

        panel.currentTheme = getTheme(panel.themes, object['devtools-theme']);

        // Build select menus
        _buildSelectMenu($select, panel);
      });

      // Get current fontSize from Chrome sync
      storage.get('devtools-fontSize', function(object){
        panel.currentFontSize = getFontSize(object['devtools-fontSize']);
      });
    }

    var ajax = new XMLHttpRequest();
    ajax.open('GET', panel.themeJSON);
    ajax.send(null);
    ajax.onreadystatechange = function(){
      if (ajax.readyState === 4) {
        if (ajax.status === 200) {
          panel.themes = JSON.parse(ajax.responseText);

          panel.themes.sort(function(a, b){
            if(a.name < b.name) return -1;
            if(a.name > b.name) return 1;
            return 0;
          });

          callback();
        } else {
          console.log('Status Code: ' + ajax.status + '\nThere was an error with your request');
        }
      }
    };
  }

  // Kickstart panel
  init();
})(
document.querySelectorAll.bind(document),
chrome.storage.sync
);
<commit_msg>Fixing JShint issues<commit_after>(function($, storage){
  'use strict';

  // Panel model
  var panel = panel || {};

  // Select menu
  var $select = $('[data-options]')[0];

  // Number input
  var $number = $('input[type=number]')[0];

  // Palette container
  var $palette = $('.palette')[0];

  // Theme title
  var $themeTitle = $('#currentTheme')[0];

  // Theme JSON file
  panel.themeJSON = '/dist/scripts/themes.json';

  // Current theme
  panel.currentTheme = '';

  // Default theme
  panel.defaultTheme = '3024';

  // Default fontSize
  panel.defaultFontSize = 12;

  function themeLookUp(theme){
    for (var i = 0; i < panel.themes.length; i++){
      if (panel.themes[i].name === theme) {
        return panel.themes[i].colors;
      }
    }
  }

  function createLI(palette){
    palette = palette.filter(function(item, pos, self) {
      return self.indexOf(item) === pos;
    });

    for (var i = 0; i < palette.length; i++){
      // Create the list item:
      var item = document.createElement('li');

      // Style item
      item.style.backgroundColor = palette[i];
      item.style.width = (100 / palette.length) + '%';

      // Add it to the list:
      $palette.appendChild(item);
    }
  }

  // Build select menus like ngOptions
  function _buildSelectMenu(menu, object){
    var options, array;

    // Get the data attribute value
    options = menu.dataset.options;

    // Clean string and create array
    options = options.replace(/in\s/g, '').split(' ');

    // Assign array from object by property name
    // using the value from the last item in options
    array = object[options[options.length - 1]];

    for (var j = 0; j < array.length; j++){

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

      // Assign option value & text from array
      option.value = array[j].name.replace(/\s+/g, '-').toLowerCase();
      option.text  = array[j].name;

      // Select currentTheme option
      if (object.currentTheme === array[j].name){
        option.selected = 'selected';
      }

      menu.add(option, null);
    }

    return menu;
  }

  // Set & save theme based on select menu change event
  function setTheme(event, obj){
    function save(theme){
      storage.set({ 'devtools-theme': theme.value },
      function(){ panel.currentTheme = theme.text; });
    }
    if (event && event.type === 'change'){
      $themeTitle.style.display = 'none';
      var el     = event.target || event.srcElement;
      var option = el.options[el.selectedIndex];
      save(option);
      $('.alert')[0].style.display = 'block';
    } else if (event === null && obj){
      save(obj);
    }
  }

  // Set & save font size based on input menu change event
  function setFontSize(event, value){
    function save(fontSize){
      storage.set({ 'devtools-fontSize': fontSize },
      function(){ panel.currentFontSize = fontSize; });
    }
    if (event && event.type === 'change'){
      var el = event.target || event.srcElement;
      save(el.value);
      $('.alert')[0].style.display = 'block';
    } else if (event === null && value){
      save(value);
    }
  }

  // Object observer for current theme
  function observer(changes){
    function updatePalette(theme){
      var children = $palette.querySelectorAll('li');

      // Remove children from $palette
      if (children.length){
        for (var i = 0; i < children.length; i++){
          $palette.removeChild(children[i]);
        }
      }

      createLI(themeLookUp(theme));
    }

    changes.forEach(function(change){
      if (change.name === 'currentTheme') {
        $themeTitle.innerHTML = change.object.currentTheme;
        $themeTitle.style.display = 'block';
        updatePalette(change.object.currentTheme);
      } else if (change.name === 'currentFontSize') {
        $number.value = change.object.currentFontSize;
      }
    });
  }

  // Get fontSize from chrome sync
  function getFontSize(value){
    if (!value){
      setFontSize(null, panel.defaultFontSize);
      return panel.defaultFontSize;
    } else {
      return value;
    }
  }

  // Get theme from chrome sync
  function getTheme(array, string){
    if (!array || !string){
      setTheme(null, {
        value: panel.defaultTheme.replace(/\s+/g, '-').toLowerCase(),
        text: panel.defaultTheme
      });
      return panel.defaultTheme;
    }
    for (var i = 0; i < array.length; i++){
      if (array[i].name.replace(/\s+/g, '-').toLowerCase() === string){
        return array[i].name;
      }
    }
  }

  // Initialize theme panel
  function init(){

    function callback(){
      // Listen for changes to the select menu
      $select.addEventListener('change', setTheme);

      // Listen for changes to the select menu
      $number.addEventListener('change', setFontSize);

      // Observe changes to panel model
      Object.observe(panel, observer, ['add', 'update']);

      // Get current theme from Chrome sync
      storage.get('devtools-theme', function(object){

        panel.currentTheme = getTheme(panel.themes, object['devtools-theme']);

        // Build select menus
        _buildSelectMenu($select, panel);
      });

      // Get current fontSize from Chrome sync
      storage.get('devtools-fontSize', function(object){
        panel.currentFontSize = getFontSize(object['devtools-fontSize']);
      });
    }

    var ajax = new XMLHttpRequest();
    ajax.open('GET', panel.themeJSON);
    ajax.send(null);
    ajax.onreadystatechange = function(){
      if (ajax.readyState === 4) {
        if (ajax.status === 200) {
          panel.themes = JSON.parse(ajax.responseText);

          panel.themes.sort(function(a, b){
            if(a.name < b.name) { return -1; }
            if(a.name > b.name) { return 1; }
            return 0;
          });

          callback();
        } else {
          console.log('Status Code: ' + ajax.status + '\nThere was an error with your request');
        }
      }
    };
  }

  // Kickstart panel
  init();
})(
document.querySelectorAll.bind(document),
chrome.storage.sync
);
<|endoftext|>


<commit_before>import React, { useState, useRef, useEffect } from "react";
import { prepareTitle } from "../common.js";
import Layout from "../components/layout";
import SEO from "../components/seo";
import { graphql } from "gatsby";
import Img from "gatsby-image";
import {
  Citation,
  CitationButton,
  CitationText,
  CitationCopyNotice,
  Columns,
  CurrentDiscourse,
  Discourse,
  DiscourseNav,
  DiscourseTitle,
  EvenPageNumber,
  FirstPage,
  Flex,
  NextDiscourse,
  OddPageNumber,
  Page,
  PageHead,
  PageHeader,
  PreviousDiscourse,
  Reporter,
  SpeakerImage,
  Subtitle,
  SubtleLink,
  TraversalLink,
  TraversalLinkImageStart,
  TraversalLinkImageEnd,
  TraversalLinkText
} from "./discourse.styles.js";
import previousSvg from "../images/previous.svg";
import nextSvg from "../images/next.svg";

export default ({
  data,
  pageContext: {
    volumeNumbers,
    volumeNumber,
    discourse,
    previousDiscourse,
    nextDiscourse
  }
}) => {
  const [showCitation, setShowCitation] = useState(false);
  const [copySuccess, setCopySuccess] = useState(false);
  const citationSpanRef = useRef(null);

  useEffect(() => {
    if (showCitation && window.getSelection && citationSpanRef.current) {
      window.getSelection().removeAllRanges();
      const range = document.createRange();
      range.selectNode(citationSpanRef.current);
      window.getSelection().addRange(range);
      document.execCommand("copy");
      setCopySuccess(true);
    }
  }, [showCitation, citationSpanRef.current]);

  const seoTitle = `${prepareTitle(discourse.page_header)}, by ${
    discourse.speaker
  } (Journal of Discourses ${discourse.volume}:${discourse.start_page}-${
    discourse.end_page
  })`;

  let event;
  if (discourse.date) {
    const fullYear = parseInt(discourse.date.substr(0, 4), 10);
    const month = parseInt(discourse.date.substr(5, 7), 10) - 1;
    const day = parseInt(discourse.date.substr(8, 10), 10);
    event = new Date(Date.UTC(fullYear, month, day, 0, 0, 0));
  }
  const dateOptions = {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  };
  const formattedDate =
    discourse.date && event && event.toLocaleDateString("default", dateOptions);

  return (
    <Layout volumeNumbers={volumeNumbers} volumeNumber={volumeNumber}>
      <SEO
        title={seoTitle}
        description={prepareTitle(discourse.subtitle)}
        canonicalUrl={`https://jod.mrm.org/${volumeNumber}/${discourse.start_page}`}
      />
      <Discourse>
        <DiscourseNav>
          <PreviousDiscourse>
            {previousDiscourse && (
              <>
                <TraversalLink
                  to={`/${previousDiscourse.volume}/${previousDiscourse.start_page}`}
                  title={`${previousDiscourse.page_header}, by ${previousDiscourse.speaker}`}
                >
                  <TraversalLinkImageStart src={previousSvg} />
                  <TraversalLinkText>
                    {previousDiscourse.volume !== discourse.volume && (
                      <>vol. {previousDiscourse.volume}, </>
                    )}
                    pp. {previousDiscourse.start_page}-
                    {previousDiscourse.end_page}
                  </TraversalLinkText>
                </TraversalLink>
              </>
            )}
          </PreviousDiscourse>
          <CurrentDiscourse>
            vol. {volumeNumber}, pp. {discourse.start_page}-{discourse.end_page}
            <CitationButton onClick={() => setShowCitation(!showCitation)}>

            </CitationButton>
          </CurrentDiscourse>
          <NextDiscourse>
            {nextDiscourse && (
              <>
                <TraversalLink
                  to={`/${nextDiscourse.volume}/${nextDiscourse.start_page}`}
                  title={`${nextDiscourse.page_header}, by ${nextDiscourse.speaker}`}
                >
                  <TraversalLinkText>
                    {nextDiscourse.volume !== discourse.volume && (
                      <>vol. {nextDiscourse.volume}, </>
                    )}
                    pp. {nextDiscourse.start_page}-{nextDiscourse.end_page}
                  </TraversalLinkText>
                  <TraversalLinkImageEnd src={nextSvg} />
                </TraversalLink>
              </>
            )}
          </NextDiscourse>
        </DiscourseNav>
        {showCitation && (
          <Citation>
            <CitationText ref={citationSpanRef}>
              {discourse.speaker}, "{discourse.page_header}",{" "}
              Journal of Discourses, vol. {discourse.volume}, pp.{" "}
              {discourse.start_page}-{discourse.end_page}, {formattedDate}.
            </CitationText>
            {copySuccess && <CitationCopyNotice>(copied)</CitationCopyNotice>}
          </Citation>
        )}
        <FirstPage>
          <Flex>
            <DiscourseTitle
              dangerouslySetInnerHTML={{
                __html: prepareTitle(discourse.title)
              }}
            />
            <Subtitle>{prepareTitle(discourse.subtitle)}</Subtitle>
            <Reporter>
              Reported by {prepareTitle(discourse.reported_by)}.
            </Reporter>
            <SpeakerImage>
              {data.mug && (

              )}
            </SpeakerImage>
          </Flex>
        </FirstPage>
        {discourse.content.map((page, index) => {
          const pageNumber = discourse.start_page + index;
          return (
            <Page key={index} id={pageNumber}>
              <PageHead>
                <EvenPageNumber>
                  {pageNumber % 2 === 0 && (
                    <SubtleLink
                      to={`/${discourse.volume}/${discourse.start_page}#${pageNumber}`}
                    >
                      {pageNumber}
                    </SubtleLink>
                  )}
                </EvenPageNumber>
                <PageHeader
                  dangerouslySetInnerHTML={{
                    __html:
                      pageNumber % 2 === 1
                        ? prepareTitle(discourse.page_header)
                        : "Journal of Discourses"
                  }}
                />
                <OddPageNumber>
                  {pageNumber % 2 === 1 && (
                    <SubtleLink
                      to={`/${discourse.volume}/${discourse.start_page}#${pageNumber}`}
                    >
                      {pageNumber}
                    </SubtleLink>
                  )}
                </OddPageNumber>
              </PageHead>
              <Columns
                dangerouslySetInnerHTML={{
                  __html: page
                }}
              />
            </Page>
          );
        })}
      </Discourse>
    </Layout>
  );
};

export const query = graphql`
  query($mug: String!) {
    mug: file(relativePath: { eq: $mug }) {
      childImageSharp {
        fixed(width: 160, height: 200) {
          ...GatsbyImageSharpFixed_withWebp_noBase64
        }
      }
    }
    citationImage: file(relativePath: { eq: "cite.png" }) {
      childImageSharp {
        # Specify the image processing specifications right in the query.
        # Makes it trivial to update as your page's design changes.
        fixed(width: 18, height: 18) {
          ...GatsbyImageSharpFixed_tracedSVG
        }
      }
    }
  }
`;
<commit_msg>Fix discourse header citation bug<commit_after>import React, { useState, useRef, useEffect } from "react";
import { prepareTitle } from "../common.js";
import Layout from "../components/layout";
import SEO from "../components/seo";
import { graphql } from "gatsby";
import Img from "gatsby-image";
import {
  Citation,
  CitationButton,
  CitationText,
  CitationCopyNotice,
  Columns,
  CurrentDiscourse,
  Discourse,
  DiscourseNav,
  DiscourseTitle,
  EvenPageNumber,
  FirstPage,
  Flex,
  NextDiscourse,
  OddPageNumber,
  Page,
  PageHead,
  PageHeader,
  PreviousDiscourse,
  Reporter,
  SpeakerImage,
  Subtitle,
  SubtleLink,
  TraversalLink,
  TraversalLinkImageStart,
  TraversalLinkImageEnd,
  TraversalLinkText
} from "./discourse.styles.js";
import previousSvg from "../images/previous.svg";
import nextSvg from "../images/next.svg";

export default ({
  data,
  pageContext: {
    volumeNumbers,
    volumeNumber,
    discourse,
    previousDiscourse,
    nextDiscourse
  }
}) => {
  const [showCitation, setShowCitation] = useState(false);
  const [copySuccess, setCopySuccess] = useState(false);
  const citationSpanRef = useRef(null);

  useEffect(() => {
    if (showCitation && window.getSelection && citationSpanRef.current) {
      window.getSelection().removeAllRanges();
      const range = document.createRange();
      range.selectNode(citationSpanRef.current);
      window.getSelection().addRange(range);
      document.execCommand("copy");
      setCopySuccess(true);
    }
  }, [showCitation, citationSpanRef.current]);

  const seoTitle = `${prepareTitle(discourse.page_header)}, by ${
    discourse.speaker
  } (Journal of Discourses ${discourse.volume}:${discourse.start_page}-${
    discourse.end_page
  })`;

  let event;
  if (discourse.date) {
    const fullYear = parseInt(discourse.date.substr(0, 4), 10);
    const month = parseInt(discourse.date.substr(5, 7), 10) - 1;
    const day = parseInt(discourse.date.substr(8, 10), 10);
    event = new Date(Date.UTC(fullYear, month, day, 0, 0, 0));
  }
  const dateOptions = {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  };
  const formattedDate =
    discourse.date && event && event.toLocaleDateString("default", dateOptions);

  return (
    <Layout volumeNumbers={volumeNumbers} volumeNumber={volumeNumber}>
      <SEO
        title={seoTitle}
        description={prepareTitle(discourse.subtitle)}
        canonicalUrl={`https://jod.mrm.org/${volumeNumber}/${discourse.start_page}`}
      />
      <Discourse>
        <DiscourseNav>
          <PreviousDiscourse>
            {previousDiscourse && (
              <>
                <TraversalLink
                  to={`/${previousDiscourse.volume}/${previousDiscourse.start_page}`}
                  title={`${previousDiscourse.page_header}, by ${previousDiscourse.speaker}`}
                >
                  <TraversalLinkImageStart src={previousSvg} />
                  <TraversalLinkText>
                    {previousDiscourse.volume !== discourse.volume && (
                      <>vol. {previousDiscourse.volume}, </>
                    )}
                    pp. {previousDiscourse.start_page}-
                    {previousDiscourse.end_page}
                  </TraversalLinkText>
                </TraversalLink>
              </>
            )}
          </PreviousDiscourse>
          <CurrentDiscourse>
            vol. {volumeNumber}, pp. {discourse.start_page}-{discourse.end_page}
            <CitationButton onClick={() => setShowCitation(!showCitation)}>

            </CitationButton>
          </CurrentDiscourse>
          <NextDiscourse>
            {nextDiscourse && (
              <>
                <TraversalLink
                  to={`/${nextDiscourse.volume}/${nextDiscourse.start_page}`}
                  title={`${nextDiscourse.page_header}, by ${nextDiscourse.speaker}`}
                >
                  <TraversalLinkText>
                    {nextDiscourse.volume !== discourse.volume && (
                      <>vol. {nextDiscourse.volume}, </>
                    )}
                    pp. {nextDiscourse.start_page}-{nextDiscourse.end_page}
                  </TraversalLinkText>
                  <TraversalLinkImageEnd src={nextSvg} />
                </TraversalLink>
              </>
            )}
          </NextDiscourse>
        </DiscourseNav>
        {showCitation && (
          <Citation>
            <CitationText ref={citationSpanRef}>
              {discourse.speaker}, "

              ", Journal of Discourses, vol. {discourse.volume}, pp.{" "}
              {discourse.start_page}-{discourse.end_page}
              {formattedDate && <>, {formattedDate}</>}.
            </CitationText>
            {copySuccess && <CitationCopyNotice>(copied)</CitationCopyNotice>}
          </Citation>
        )}
        <FirstPage>
          <Flex>
            <DiscourseTitle
              dangerouslySetInnerHTML={{
                __html: prepareTitle(discourse.title)
              }}
            />
            <Subtitle>{prepareTitle(discourse.subtitle)}</Subtitle>
            <Reporter>
              Reported by {prepareTitle(discourse.reported_by)}.
            </Reporter>
            <SpeakerImage>
              {data.mug && (

              )}
            </SpeakerImage>
          </Flex>
        </FirstPage>
        {discourse.content.map((page, index) => {
          const pageNumber = discourse.start_page + index;
          return (
            <Page key={index} id={pageNumber}>
              <PageHead>
                <EvenPageNumber>
                  {pageNumber % 2 === 0 && (
                    <SubtleLink
                      to={`/${discourse.volume}/${discourse.start_page}#${pageNumber}`}
                    >
                      {pageNumber}
                    </SubtleLink>
                  )}
                </EvenPageNumber>
                <PageHeader
                  dangerouslySetInnerHTML={{
                    __html:
                      pageNumber % 2 === 1
                        ? prepareTitle(discourse.page_header)
                        : "Journal of Discourses"
                  }}
                />
                <OddPageNumber>
                  {pageNumber % 2 === 1 && (
                    <SubtleLink
                      to={`/${discourse.volume}/${discourse.start_page}#${pageNumber}`}
                    >
                      {pageNumber}
                    </SubtleLink>
                  )}
                </OddPageNumber>
              </PageHead>
              <Columns
                dangerouslySetInnerHTML={{
                  __html: page
                }}
              />
            </Page>
          );
        })}
      </Discourse>
    </Layout>
  );
};

export const query = graphql`
  query($mug: String!) {
    mug: file(relativePath: { eq: $mug }) {
      childImageSharp {
        fixed(width: 160, height: 200) {
          ...GatsbyImageSharpFixed_withWebp_noBase64
        }
      }
    }
    citationImage: file(relativePath: { eq: "cite.png" }) {
      childImageSharp {
        # Specify the image processing specifications right in the query.
        # Makes it trivial to update as your page's design changes.
        fixed(width: 18, height: 18) {
          ...GatsbyImageSharpFixed_tracedSVG
        }
      }
    }
  }
`;
<|endoftext|>


<commit_before>define([],
	function ()
	{
		configOptions = {
			//The appid for the configured application
			appid: "",
			//The web map id
			webmaps: [
			{
				id: "04744b7ad124458489ea821da3129352",
				title: "",
				// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
				showSingleTimeInstance: false,
				// Hide legend and legend toggle for specific map by setting this option to false
				legendVisible: true,
				// Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
				openLegendOnChange: false
			},{
				id: "d0f1c5ae11d24c778d597f8bbddcf85f",
				title: "",
				// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
				showSingleTimeInstance: false,
				// Hide legend and legend toggle for specific map by setting this option to false
				legendVisible: true,
				// Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
				openLegendOnChange: false
			},
			{
				id: "bb5a3da962ac4040b2f52f5869fddbd8",
				title: "",
				// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
				showSingleTimeInstance: false,
				// Hide legend and legend toggle for specific map by setting this option to false
				legendVisible: true,
				// Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
				openLegendOnChange: false
			}
			// To add additional maps to the template, uncomment the below section for
			// each map you would like to add. Webmap titles from ArcGIS Online will
			// be used unless you fill in title attribute.
			//, {
			//
			//	id: "739db23c3f674005a405c68e337f5011",
			//	title: "",
			//	// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
			//	showSingleTimeInstance: false,
			//  // Hide legend and legend toggle for specific map by setting this option to false
			//	legendVisible: true,
			//  // Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
			//	openLegendOnChange: false
			//	},{
			//
			//	id: "739db23c3f674005a405c68e337f5011",
			//	title: "",
			//	// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
			//	showSingleTimeInstance: false,
			//  // Hide legend and legend toggle for specific map by setting this option to false
			//	legendVisible: true,
			//  // Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
			//	openLegendOnChange: false
			//	}
			],
			//Enter a title, if no title is specified, the first webmap's title is used.
			title: "Mapping the Shale Gas Boom",
			//Enter a subtitle, if no subtitle is specified, the first webmap's subtitle is used.
			subtitle: "Where in the United States is fracking unlocking natural gas from shale rock?",
			// If false, each tab will have a number on it. If true, the first tab will not have a number and the second tab will start counting at 1.
			startCountOnSecondTab: false,
			//Sync maps scale and location
			syncMaps: false,
			//Display geocoder search widget
			geocoderWidget: false,
			// Specify a proxy for custom deployment
			proxyurl: "",
			//specify the url to a geometry service
			geometryserviceurl: "http://tasks.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer",
			//If the webmap uses Bing Maps data, you will need to provided your Bing Maps Key
			bingmapskey : "",
			//Modify this to point to your sharing service URL if you are using the portal
			sharingurl: "http://www.arcgis.com/sharing/rest/content/items"
		}
	}
);
<commit_msg>sync maps<commit_after>define([],
	function ()
	{
		configOptions = {
			//The appid for the configured application
			appid: "",
			//The web map id
			webmaps: [
			{
				id: "04744b7ad124458489ea821da3129352",
				title: "",
				// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
				showSingleTimeInstance: false,
				// Hide legend and legend toggle for specific map by setting this option to false
				legendVisible: true,
				// Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
				openLegendOnChange: false
			},{
				id: "d0f1c5ae11d24c778d597f8bbddcf85f",
				title: "",
				// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
				showSingleTimeInstance: false,
				// Hide legend and legend toggle for specific map by setting this option to false
				legendVisible: true,
				// Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
				openLegendOnChange: false
			},
			{
				id: "bb5a3da962ac4040b2f52f5869fddbd8",
				title: "",
				// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
				showSingleTimeInstance: false,
				// Hide legend and legend toggle for specific map by setting this option to false
				legendVisible: true,
				// Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
				openLegendOnChange: false
			}
			// To add additional maps to the template, uncomment the below section for
			// each map you would like to add. Webmap titles from ArcGIS Online will
			// be used unless you fill in title attribute.
			//, {
			//
			//	id: "739db23c3f674005a405c68e337f5011",
			//	title: "",
			//	// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
			//	showSingleTimeInstance: false,
			//  // Hide legend and legend toggle for specific map by setting this option to false
			//	legendVisible: true,
			//  // Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
			//	openLegendOnChange: false
			//	},{
			//
			//	id: "739db23c3f674005a405c68e337f5011",
			//	title: "",
			//	// If your map has time properties, choose to show a single time instance instead of the time interval saved with the web map.
			//	showSingleTimeInstance: false,
			//  // Hide legend and legend toggle for specific map by setting this option to false
			//	legendVisible: true,
			//  // Set to true if you want the legend of this specific map to open when a user selects the tab for this map.
			//	openLegendOnChange: false
			//	}
			],
			//Enter a title, if no title is specified, the first webmap's title is used.
			title: "Mapping the Shale Gas Boom",
			//Enter a subtitle, if no subtitle is specified, the first webmap's subtitle is used.
			subtitle: "Where in the United States is fracking unlocking natural gas from shale rock?",
			// If false, each tab will have a number on it. If true, the first tab will not have a number and the second tab will start counting at 1.
			startCountOnSecondTab: false,
			//Sync maps scale and location
			syncMaps: true,
			//Display geocoder search widget
			geocoderWidget: false,
			// Specify a proxy for custom deployment
			proxyurl: "",
			//specify the url to a geometry service
			geometryserviceurl: "http://tasks.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer",
			//If the webmap uses Bing Maps data, you will need to provided your Bing Maps Key
			bingmapskey : "",
			//Modify this to point to your sharing service URL if you are using the portal
			sharingurl: "http://www.arcgis.com/sharing/rest/content/items"
		}
	}
);
<|endoftext|>