<commit_before>var Promise = require('bluebird'),
    bases = require('bases'),
    RateLimit = require('express-rate-limit'),
    logger = require('../utils/logger'),
    nconf = require('nconf'),
    emapicOpinionFreq = parseFloat(nconf.get('app').emapicOpinionFreq),
    sequelize = models.sequelize;

module.exports = function(app) {

    app.get('/survey/:id', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.redirect('/');
            }
            survey.getOwner().then(function(owner){
                // Survey is in draft mode and it's not being tested by its owner
                if ((survey.active === null) && (!req.user || (owner.id != req.user.id))) {
                    return res.redirect('/');
                }
                // Survey is closed
                if (survey.active === false) {
                    return res.redirect('/survey/' + req.params.id + '/results');
                }
                Promise.join(survey.getHtml(), survey.getSubTitle(), survey.getUserFullResponses(req.user ? req.user.id : null), function(html, subTitle, responses) {
                    // If the survey allows multiple answers, then we don't
                    // set an already voted position
                    var position = survey.multiple_answer ? null : responses.geometry.coordinates;
                    res.render('templates/survey', {
                        title : survey.title,
                        survey : survey,
                        subTitle : subTitle,
                        questions_html : html,
                        responses : JSON.stringify(responses.properties),
                        position : JSON.stringify(position),
                        emapicOpinion : (survey.active === null) ? false : (Math.random() < emapicOpinionFreq),
                        layout : 'layouts/map'
                    });
                }).done();
            });
        });
    });

    app.get('/survey/:id/results', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.redirect('/');
            }
            survey.getOwner().then(function(owner){
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    return Promise.join(survey.getHtml(), survey.getSubTitle(), survey.getUserFullResponses(req.user ? req.user.id : null), function(html, subTitle, responses) {
                        // If the survey allows multiple answers, then we don't
                        // set an already voted position
                        var position = survey.multiple_answer ? null : responses.geometry.coordinates;
                        res.render('templates/survey', {
                            title : survey.title,
                            survey : survey,
                            results : true,
                            subTitle : subTitle,
                            questions_html : html,
                            responses : JSON.stringify(responses.properties),
                            position : JSON.stringify(position),
                            emapicOpinion : (Math.random() < 0.2),
                            layout : 'layouts/map'
                        });
                    }).done();
                }
                return res.redirect('/survey/' + req.params.id);
            });
        });
    });

    app.get('/api/survey/:id/results', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    Promise.join(survey.getAnonymizedResponses(), survey.getQuestions({
                        scope: 'includeAnswers'
                    }), function(responses, questions) {
                        var answers = [];
                        for (var i = 0, iLen = questions.length; i<iLen; i++) {
                            answers.push(questions[i].Answers);
                        }
                        res.json(postGISQueryToFeatureCollection(addIndivVotePopupMessage(responses, questions, answers)));
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    app.get('/api/survey/:id/totals', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    survey.getTotals().then(function(response) {
                        res.json(response);
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    app.get('/api/survey/:id/totals/:layer', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    var layer = req.params.layer,
                        params = req.query,
                        promise;
                    survey.getAggregatedTotals(layer, params).then(function(features) {
                        var results = [];
                        if (features && features.length > 0) {
                            if ('geojson' in features[0]) {
                                results = postGISQueryToFeatureCollection(features);
                            } else {
                                results = features;
                            }
                        }
                        res.json(results);
                    }).catch(function(err) {
                        var errorMsg;
                        switch (err.message) {
                            case 'INVALID GEOM TYPE':
                                errorMsg = 'requested invalid geom type.';
                                logger.info("Requested invalid aggregation by geom type '" + params.geom + "' for base layer '" + layer + "'.");
                                break;
                            case 'INVALID BASE LAYER':
                                errorMsg = 'requested invalid base layer.';
                                logger.info("Requested invalid aggregation by base layer '" + layer + "'.");
                                break;
                            default:
                                errorMsg = 'an error happened while aggregating the data.';
                                logger.info("Requested aggregation by geom type '" + params.geom + "' for base layer '" + layer + "' raised error: " + err.message);
                        }
                        return res.status(404).json({ error: errorMsg });
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    app.get('/api/survey/:id/legend', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    survey.getLegend().then(function(legend) {
                        res.json(legend);
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    app.post('/api/survey/:id/results', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null || (typeof req.body.responses === undefined)) {
                return res.end();
            }
            survey.saveResponse(req).catch(function(err) {
                logger.error('Error while saving response for survey with id ' + survey.id + ' : ' + err);
            }).lastly(function(response) {
                res.end();
            });
        });
    });

    app.get('/api/survey/:id/export', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    Promise.join(survey.getFullResponses(), survey.getQuestions({
                        scope: 'includeAnswers'
                    }), function(responses, questions) {
                        res.attachment(survey.title + '.csv');
                        pgQueryFullResultsToCsv(responses, questions).then(function(csv) {
                            res.send(csv);
                        });
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    // One request per minute limit for full/simple geom requests as data can be
    // quite big
    var baseLayerFullGeomLimiter = new RateLimit({
        windowMs: 60*1000,
        max: 1,
        delayMs: 0,
        handler: function (req, res, next) {
            return res.status(429).json({ warning: "Base layer API full/simple geom requests limited to 1 per minute." });
        }
    });

    app.get('/api/baselayers/:layer', function(req, res, next) {
        // Limit full/simple geom requests in production environmentes only
        if ('production' == app.get('env')) {
            switch (req.query.geom) {
                case 'simple':
                case 'full':
                    return baseLayerFullGeomLimiter(req, res, next);
            }
        }
        next();

    }, function(req, res) {
        var layer = req.params.layer,
            params = req.query,
            sql,
            where = [],
            geom,
            replacements = {},
            namePromise;
        params.geom = params.geom || 'simple';
        if ('geom' in params) {
            switch (params.geom) {
                case 'simple':
                    geom = ', st_asgeojson(a.simp_geom) as geojson';
                    break;
                case 'full':
                    geom = ', st_asgeojson(a.geom) as geojson';
                    break;
                case 'bbox':
                    geom = ', st_asgeojson(st_envelope(a.geom)) as geojson';
                    break;
                case 'centroid':
                    geom = ', st_asgeojson(st_centroid(a.geom)) as geojson';
                    break;
                case 'none':
                    geom = '';
                    break;
                default:
                    logger.info("Requested invalid geom type '" + params.geom + "' for base layer '" + layer + "'.");
                    return res.status(404).json({ error: "requested invalid geom type." });
            }
        }
        if ('lang' in params) {
            namePromise = checkColumnExists('name_' + params.lang, layer, 'base_layers').then(function(result) {
                return (result[0].exists) ? 'a.name_' + params.lang : 'a.name';
            });
        } else {
            namePromise = Promise.resolve('a.name');
        }
        namePromise.then(function(nameCol) {
            switch (layer) {
                case 'municipalities':
                    if ('prov' in params && !(isNaN(params.prov))) {
                        where.push('a.cod_prov = :prov');
                        replacements.prov = params.prov;
                    } else if ('ccaa' in params && !(isNaN(params.ccaa))) {
                        where.push('a.cod_ccaa = :ccaa');
                        replacements.ccaa = params.ccaa;
                    }
                    sql = 'SELECT a.codigo AS adm_code, ' + nameCol + ' AS name, a.cod_prov, a.provincia, a.cod_ccaa, a.comautonom, a.pobmun15_p AS pobumn15, a.pobmun15_h, a.pobmun15_m, lower(b.iso_a2) AS country_iso_code' + geom + ' FROM base_layers.municipalities a JOIN base_layers.provinces b ON a.province_gid = b.gid' + (where.length > 0 ? ' WHERE ' + where.join(' AND ') : '') + ' ORDER BY adm_code;';
                    break;
                case 'provinces':
                    if ('country' in params) {
                        where.push('lower(a.iso_a2) = lower(:country)');
                        replacements.country = params.country;
                    }
                    sql = 'SELECT a.diss_me AS province_id, a.iso_3166_2 AS iso_code, a.adm1_code AS adm_code, ' + nameCol + ' AS name, a.type_en AS type, lower(a.iso_a2) AS country_iso_code' + geom + ' FROM base_layers.provinces a' + (where.length > 0 ? ' WHERE ' + where.join(' AND ') : '') + ' ORDER BY adm_code;';
                    break;
                case 'countries':
                    where.push('iso_code_2 IS NOT NULL');
                    sql = 'SELECT lower(a.iso_code_2) AS iso_code, ' + nameCol + ' AS name' + geom + ' FROM base_layers.countries a' + (where.length > 0 ? ' WHERE ' + where.join(' AND ') : '') + ' ORDER BY iso_code;';
                    break;
                default:
                    logger.info("Requested base layer '" + layer + "' doesn't exist.");
                    return res.status(404).json({ error: "requested layer doesn't exist." });
            }
            sequelize.query(sql, {
                replacements: replacements,
                type: sequelize.QueryTypes.SELECT
            }).then(function(features) {
                var results = [];
                if (features && features.length > 0) {
                    if ('geojson' in features[0]) {
                        results = postGISQueryToFeatureCollection(features);
                    } else {
                        results = features;
                    }
                }
                var size = Buffer.byteLength(JSON.stringify(results)) / 1000000;
                if (size > 25) {
                    logger.warn("Requested layer '" + layer + "' with " + (('geom' in params) ? params.geom : 'simple') + " geom, which is too big for being served as geojson (" + size + " MB).");
                    return res.status(404).json({ error: "requested layer is too big for being served as geojson (25 MB max)." });
                }
                res.json(results);
            });
        });
    });

};
<commit_msg>Return http code 400 with survey answer save error<commit_after>var Promise = require('bluebird'),
    bases = require('bases'),
    RateLimit = require('express-rate-limit'),
    logger = require('../utils/logger'),
    nconf = require('nconf'),
    emapicOpinionFreq = parseFloat(nconf.get('app').emapicOpinionFreq),
    sequelize = models.sequelize;

module.exports = function(app) {

    app.get('/survey/:id', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.redirect('/');
            }
            survey.getOwner().then(function(owner){
                // Survey is in draft mode and it's not being tested by its owner
                if ((survey.active === null) && (!req.user || (owner.id != req.user.id))) {
                    return res.redirect('/');
                }
                // Survey is closed
                if (survey.active === false) {
                    return res.redirect('/survey/' + req.params.id + '/results');
                }
                Promise.join(survey.getHtml(), survey.getSubTitle(), survey.getUserFullResponses(req.user ? req.user.id : null), function(html, subTitle, responses) {
                    // If the survey allows multiple answers, then we don't
                    // set an already voted position
                    var position = survey.multiple_answer ? null : responses.geometry.coordinates;
                    res.render('templates/survey', {
                        title : survey.title,
                        survey : survey,
                        subTitle : subTitle,
                        questions_html : html,
                        responses : JSON.stringify(responses.properties),
                        position : JSON.stringify(position),
                        emapicOpinion : (survey.active === null) ? false : (Math.random() < emapicOpinionFreq),
                        layout : 'layouts/map'
                    });
                }).done();
            });
        });
    });

    app.get('/survey/:id/results', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.redirect('/');
            }
            survey.getOwner().then(function(owner){
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    return Promise.join(survey.getHtml(), survey.getSubTitle(), survey.getUserFullResponses(req.user ? req.user.id : null), function(html, subTitle, responses) {
                        // If the survey allows multiple answers, then we don't
                        // set an already voted position
                        var position = survey.multiple_answer ? null : responses.geometry.coordinates;
                        res.render('templates/survey', {
                            title : survey.title,
                            survey : survey,
                            results : true,
                            subTitle : subTitle,
                            questions_html : html,
                            responses : JSON.stringify(responses.properties),
                            position : JSON.stringify(position),
                            emapicOpinion : (Math.random() < 0.2),
                            layout : 'layouts/map'
                        });
                    }).done();
                }
                return res.redirect('/survey/' + req.params.id);
            });
        });
    });

    app.get('/api/survey/:id/results', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    Promise.join(survey.getAnonymizedResponses(), survey.getQuestions({
                        scope: 'includeAnswers'
                    }), function(responses, questions) {
                        var answers = [];
                        for (var i = 0, iLen = questions.length; i<iLen; i++) {
                            answers.push(questions[i].Answers);
                        }
                        res.json(postGISQueryToFeatureCollection(addIndivVotePopupMessage(responses, questions, answers)));
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    app.get('/api/survey/:id/totals', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    survey.getTotals().then(function(response) {
                        res.json(response);
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    app.get('/api/survey/:id/totals/:layer', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    var layer = req.params.layer,
                        params = req.query,
                        promise;
                    survey.getAggregatedTotals(layer, params).then(function(features) {
                        var results = [];
                        if (features && features.length > 0) {
                            if ('geojson' in features[0]) {
                                results = postGISQueryToFeatureCollection(features);
                            } else {
                                results = features;
                            }
                        }
                        res.json(results);
                    }).catch(function(err) {
                        var errorMsg;
                        switch (err.message) {
                            case 'INVALID GEOM TYPE':
                                errorMsg = 'requested invalid geom type.';
                                logger.info("Requested invalid aggregation by geom type '" + params.geom + "' for base layer '" + layer + "'.");
                                break;
                            case 'INVALID BASE LAYER':
                                errorMsg = 'requested invalid base layer.';
                                logger.info("Requested invalid aggregation by base layer '" + layer + "'.");
                                break;
                            default:
                                errorMsg = 'an error happened while aggregating the data.';
                                logger.info("Requested aggregation by geom type '" + params.geom + "' for base layer '" + layer + "' raised error: " + err.message);
                        }
                        return res.status(404).json({ error: errorMsg });
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    app.get('/api/survey/:id/legend', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    survey.getLegend().then(function(legend) {
                        res.json(legend);
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    app.post('/api/survey/:id/results', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null || (typeof req.body.responses === undefined)) {
                return res.end();
            }
            survey.saveResponse(req).catch(function(err) {
                logger.error('Error while saving response for survey with id ' + survey.id + ' : ' + err);
                res.status(400);
            }).lastly(function(response) {
                res.end();
            });
        });
    });

    app.get('/api/survey/:id/export', function(req, res) {
        models.Survey.findById(decryptSurveyId(req.params.id)).then(function(survey) {
            if (survey === null) {
                return res.end();
            }
            survey.getOwner().then(function(owner) {
                // Survey is closed
                if ((survey.active === false) ||
                // Data required by the owner
                (req.user && owner.id == req.user.id) ||
                // Results are always public
                survey.public_results ||
                // Results after vote
                survey.results_after_vote ||
                // It's a local request from the server itself
                (req.ip == '127.0.0.1' && (req.host == '127.0.0.1' || req.host == 'localhost'))) {
                    Promise.join(survey.getFullResponses(), survey.getQuestions({
                        scope: 'includeAnswers'
                    }), function(responses, questions) {
                        res.attachment(survey.title + '.csv');
                        pgQueryFullResultsToCsv(responses, questions).then(function(csv) {
                            res.send(csv);
                        });
                    });
                } else {
                    res.end();
                }
            });
        });
    });

    // One request per minute limit for full/simple geom requests as data can be
    // quite big
    var baseLayerFullGeomLimiter = new RateLimit({
        windowMs: 60*1000,
        max: 1,
        delayMs: 0,
        handler: function (req, res, next) {
            return res.status(429).json({ warning: "Base layer API full/simple geom requests limited to 1 per minute." });
        }
    });

    app.get('/api/baselayers/:layer', function(req, res, next) {
        // Limit full/simple geom requests in production environmentes only
        if ('production' == app.get('env')) {
            switch (req.query.geom) {
                case 'simple':
                case 'full':
                    return baseLayerFullGeomLimiter(req, res, next);
            }
        }
        next();

    }, function(req, res) {
        var layer = req.params.layer,
            params = req.query,
            sql,
            where = [],
            geom,
            replacements = {},
            namePromise;
        params.geom = params.geom || 'simple';
        if ('geom' in params) {
            switch (params.geom) {
                case 'simple':
                    geom = ', st_asgeojson(a.simp_geom) as geojson';
                    break;
                case 'full':
                    geom = ', st_asgeojson(a.geom) as geojson';
                    break;
                case 'bbox':
                    geom = ', st_asgeojson(st_envelope(a.geom)) as geojson';
                    break;
                case 'centroid':
                    geom = ', st_asgeojson(st_centroid(a.geom)) as geojson';
                    break;
                case 'none':
                    geom = '';
                    break;
                default:
                    logger.info("Requested invalid geom type '" + params.geom + "' for base layer '" + layer + "'.");
                    return res.status(404).json({ error: "requested invalid geom type." });
            }
        }
        if ('lang' in params) {
            namePromise = checkColumnExists('name_' + params.lang, layer, 'base_layers').then(function(result) {
                return (result[0].exists) ? 'a.name_' + params.lang : 'a.name';
            });
        } else {
            namePromise = Promise.resolve('a.name');
        }
        namePromise.then(function(nameCol) {
            switch (layer) {
                case 'municipalities':
                    if ('prov' in params && !(isNaN(params.prov))) {
                        where.push('a.cod_prov = :prov');
                        replacements.prov = params.prov;
                    } else if ('ccaa' in params && !(isNaN(params.ccaa))) {
                        where.push('a.cod_ccaa = :ccaa');
                        replacements.ccaa = params.ccaa;
                    }
                    sql = 'SELECT a.codigo AS adm_code, ' + nameCol + ' AS name, a.cod_prov, a.provincia, a.cod_ccaa, a.comautonom, a.pobmun15_p AS pobumn15, a.pobmun15_h, a.pobmun15_m, lower(b.iso_a2) AS country_iso_code' + geom + ' FROM base_layers.municipalities a JOIN base_layers.provinces b ON a.province_gid = b.gid' + (where.length > 0 ? ' WHERE ' + where.join(' AND ') : '') + ' ORDER BY adm_code;';
                    break;
                case 'provinces':
                    if ('country' in params) {
                        where.push('lower(a.iso_a2) = lower(:country)');
                        replacements.country = params.country;
                    }
                    sql = 'SELECT a.diss_me AS province_id, a.iso_3166_2 AS iso_code, a.adm1_code AS adm_code, ' + nameCol + ' AS name, a.type_en AS type, lower(a.iso_a2) AS country_iso_code' + geom + ' FROM base_layers.provinces a' + (where.length > 0 ? ' WHERE ' + where.join(' AND ') : '') + ' ORDER BY adm_code;';
                    break;
                case 'countries':
                    where.push('iso_code_2 IS NOT NULL');
                    sql = 'SELECT lower(a.iso_code_2) AS iso_code, ' + nameCol + ' AS name' + geom + ' FROM base_layers.countries a' + (where.length > 0 ? ' WHERE ' + where.join(' AND ') : '') + ' ORDER BY iso_code;';
                    break;
                default:
                    logger.info("Requested base layer '" + layer + "' doesn't exist.");
                    return res.status(404).json({ error: "requested layer doesn't exist." });
            }
            sequelize.query(sql, {
                replacements: replacements,
                type: sequelize.QueryTypes.SELECT
            }).then(function(features) {
                var results = [];
                if (features && features.length > 0) {
                    if ('geojson' in features[0]) {
                        results = postGISQueryToFeatureCollection(features);
                    } else {
                        results = features;
                    }
                }
                var size = Buffer.byteLength(JSON.stringify(results)) / 1000000;
                if (size > 25) {
                    logger.warn("Requested layer '" + layer + "' with " + (('geom' in params) ? params.geom : 'simple') + " geom, which is too big for being served as geojson (" + size + " MB).");
                    return res.status(404).json({ error: "requested layer is too big for being served as geojson (25 MB max)." });
                }
                res.json(results);
            });
        });
    });

};
<|endoftext|>


<commit_before>/**
 * Created by Gaplo917 on 23/1/2016.
 */
import * as HKEPC from '../../data/config/hkepc'
import {FindMessageRequest} from "../model/find-message-request"

export class TabController{
  static get STATE() { return 'tab'}
  static get NAME() { return 'TabCtrl'}
  static get CONFIG() { return {
    url: '/tab',
    abstract: true,
    templateUrl: 'templates/tabs.html',
    controller: 'TabCtrl',
    controllerAs: 'vm'

  }}

  constructor($scope,$ionicModal,MessageResolver,$stateParams,AuthService,ngToast,LocalStorageService) {
    this.scope = $scope
    this.scope.messageModal = $scope.$new()
    this.scope.eulaModal = $scope.$new()

    this.localStorageService = LocalStorageService
    this.authService = AuthService
    // cache the value
    this._isLoggedIn = AuthService.isLoggedIn()

    $scope.$on('accountTabUpdate', (event,arg) =>{
      if(arg){
        this.login = arg
        this._isLoggedIn = true
      }
      else {
        this._isLoggedIn = false

        this.login = undefined

        if (AuthService.isLoggedIn()){
          ngToast.danger(`<i class="ion-alert-circled"> 你的登入認証己過期，請重新登入！</i>`)
          AuthService.logout()
        }
      }
    })

    $scope.$on('find', (event,arg) =>{
      if(arg instanceof FindMessageRequest){
        this.messageModal.show()
        // reset the message first
        this.scope.messageModal.message = {}

        MessageResolver.resolve(HKEPC.forum.findMessage(arg.postId,arg.messageId))
            .then((data) => {

              this.scope.messageModal.message = data.message
              this.scope.messageModal.hide = () => this.messageModal.hide()

            })
      }

    })

    $ionicModal.fromTemplateUrl('templates/modals/find-message.html', {
      scope: $scope.messageModal
    }).then((modal) => {
      this.messageModal = modal
    })

    if(!this.localStorageService.get('agreeEULA',0)){

      $ionicModal.fromTemplateUrl('templates/modals/EULA.html', {
        scope: $scope.eulaModal
      }).then((modal) => {
        this.eulaModal = modal
        this.eulaModal.show()

        this.scope.eulaModal.disagree = () => {
          alert("請自行離開！")
        }

        this.scope.eulaModal.agree = () => {
          this.localStorageService.set('agreeEULA',1)
          this.eulaModal.hide()
        }
      })
    }

  }

  isLoggedIn(){
    return this._isLoggedIn
  }
}<commit_msg>disable backdrop click to close on eula<commit_after>/**
 * Created by Gaplo917 on 23/1/2016.
 */
import * as HKEPC from '../../data/config/hkepc'
import {FindMessageRequest} from "../model/find-message-request"

export class TabController{
  static get STATE() { return 'tab'}
  static get NAME() { return 'TabCtrl'}
  static get CONFIG() { return {
    url: '/tab',
    abstract: true,
    templateUrl: 'templates/tabs.html',
    controller: 'TabCtrl',
    controllerAs: 'vm'

  }}

  constructor($scope,$ionicModal,MessageResolver,$stateParams,AuthService,ngToast,LocalStorageService) {
    this.scope = $scope
    this.scope.messageModal = $scope.$new()
    this.scope.eulaModal = $scope.$new()

    this.localStorageService = LocalStorageService
    this.authService = AuthService
    // cache the value
    this._isLoggedIn = AuthService.isLoggedIn()

    $scope.$on('accountTabUpdate', (event,arg) =>{
      if(arg){
        this.login = arg
        this._isLoggedIn = true
      }
      else {
        this._isLoggedIn = false

        this.login = undefined

        if (AuthService.isLoggedIn()){
          ngToast.danger(`<i class="ion-alert-circled"> 你的登入認証己過期，請重新登入！</i>`)
          AuthService.logout()
        }
      }
    })

    $scope.$on('find', (event,arg) =>{
      if(arg instanceof FindMessageRequest){
        this.messageModal.show()
        // reset the message first
        this.scope.messageModal.message = {}

        MessageResolver.resolve(HKEPC.forum.findMessage(arg.postId,arg.messageId))
            .then((data) => {

              this.scope.messageModal.message = data.message
              this.scope.messageModal.hide = () => this.messageModal.hide()

            })
      }

    })

    $ionicModal.fromTemplateUrl('templates/modals/find-message.html', {
      scope: $scope.messageModal
    }).then((modal) => {
      this.messageModal = modal
    })

    if(!this.localStorageService.get('agreeEULA',0)){

      $ionicModal.fromTemplateUrl('templates/modals/EULA.html', {
        scope: $scope.eulaModal,
        backdropClickToClose: false
      }).then((modal) => {
        this.eulaModal = modal
        this.eulaModal.show()

        this.scope.eulaModal.disagree = () => {
          alert("請自行離開！")
        }

        this.scope.eulaModal.agree = () => {
          this.localStorageService.set('agreeEULA',1)
          this.eulaModal.hide()
        }
      })
    }

  }

  isLoggedIn(){
    return this._isLoggedIn
  }
}<|endoftext|>


<commit_before>import moment from 'moment';
import distance from 'turf-distance';

const metersToMiles = (meters) => meters * 0.00062137;

export function computeFilteredEvents(events, filters, zipcodes) {
  // Bail out early if possible. Huge array!
  if (!Object.keys(filters).length) {
    return events;
  }

  const zipcodesLength = Object.keys(zipcodes).length;
  const validZipcode = zipcodesLength && !!zipcodes[filters.zipcode];

  // A zipcode is selected but either we do not yet have valid zipcodes
  // to check it against and get its coords, or the zipcode is invalid;
  // either way there can be no valid events. Bail early.
  if (filters.zipcode && (!zipcodesLength || !validZipcode)) {
    return [];
  }

  return events.filter(function(event) {

    if (filters.eventType) {
      if (!event.categories) {
        return false;
      }

      const allCategories = event.categories.split(',');
      const allEventTypes = filters.eventType.split(',');

      const eventIsInNoSelectedCategories = allEventTypes.every(type =>
        !allCategories.includes(type)
      );

      if (eventIsInNoSelectedCategories) {
        return false;
      }
    }

    // note: there is also a utc timestamp, this one is localized
    const localDatetime = moment(event.start_datetime);

    if (filters.startDate) {
      const startDate = moment(filters.startDate, 'YYYY-MM-DD');

      if (localDatetime.isBefore(startDate, 'day')) {
        return false;
      }
    }

    if (filters.endDate) {
      const endDate = moment(filters.endDate, 'YYYY-MM-DD');

      if (localDatetime.isAfter(endDate, 'day')) {
        return false;
      }
    }

    if (filters.zipcode) {
      const milesFromZipcode = metersToMiles(
        distance([zipcodes[filters.zipcode][1], zipcodes[filters.zipcode][0]], [event.lng, event.lat])
      );

      const MAX_MILES_FROM_ZIPCODE = 50;

      if (milesFromZipcode > MAX_MILES_FROM_ZIPCODE) {
        return false;
      }
    }

    return true;
  });
}

export const eventTypes = {
  freedomcities: "Freedom Cities Action",
  muslimban: "Muslim Ban Action",
  protestrally: "Protest/Rally",
  townmeeting: "Town Meeting",
  organizing: "Organizing Meeting",
  healthcareaction: "Health Care Action",
  other: "Other"
};
<commit_msg>Hotfix to event filtering<commit_after>import moment from 'moment';
import distance from 'turf-distance';

export function computeFilteredEvents(events, filters, zipcodes) {
  // Bail out early if possible. Huge array!
  if (!Object.keys(filters).length) {
    return events;
  }

  const zipcodesLength = Object.keys(zipcodes).length;
  const validZipcode = zipcodesLength && !!zipcodes[filters.zipcode];

  // A zipcode is selected but either we do not yet have valid zipcodes
  // to check it against and get its coords, or the zipcode is invalid;
  // either way there can be no valid events. Bail early.
  if (filters.zipcode && (!zipcodesLength || !validZipcode)) {
    return [];
  }

  return events.filter(function(event) {

    if (filters.eventType) {
      if (!event.categories) {
        return false;
      }

      const allCategories = event.categories.split(',');
      const allEventTypes = filters.eventType.split(',');

      const eventIsInNoSelectedCategories = allEventTypes.every(type =>
        !allCategories.includes(type)
      );

      if (eventIsInNoSelectedCategories) {
        return false;
      }
    }

    // note: there is also a utc timestamp, this one is localized
    const localDatetime = moment(event.start_datetime);

    if (filters.startDate) {
      const startDate = moment(filters.startDate, 'YYYY-MM-DD');

      if (localDatetime.isBefore(startDate, 'day')) {
        return false;
      }
    }

    if (filters.endDate) {
      const endDate = moment(filters.endDate, 'YYYY-MM-DD');

      if (localDatetime.isAfter(endDate, 'day')) {
        return false;
      }
    }

    if (filters.zipcode) {
      const milesFromZipcode = distance(
        [zipcodes[filters.zipcode][1], zipcodes[filters.zipcode][0]],
        [event.lng, event.lat],
        'miles'
      );

      const MAX_MILES_FROM_ZIPCODE = 50;

      if (milesFromZipcode > MAX_MILES_FROM_ZIPCODE) {
        return false;
      }
    }

    return true;
  });
}

export const eventTypes = {
  freedomcities: "Freedom Cities Action",
  muslimban: "Muslim Ban Action",
  protestrally: "Protest/Rally",
  townmeeting: "Town Meeting",
  organizing: "Organizing Meeting",
  healthcareaction: "Health Care Action",
  other: "Other"
};
<|endoftext|>


<commit_before>'use strict';

const Entry = require('../entry');

Entry.STATIC = {
    /* data type */
    OBJECT: 0,
    ENTITY: 1,
    SPRITE: 2,
    SOUND: 3,
    VARIABLE: 4,
    FUNCTION: 5,
    SCENE: 6,
    MESSAGE: 7,
    BLOCK_MODEL: 8,
    BLOCK_RENDER_MODEL: 9,
    BOX_MODEL: 10,
    THREAD_MODEL: 11,
    DRAG_INSTANCE: 12,

    /* block state */
    BLOCK_STATIC: 0,
    BLOCK_MOVE: 1,
    BLOCK_FOLLOW: 2,

    /* execute return state */
    RETURN: 0,
    CONTINUE: 1,
    BREAK: 2,
    PASS: 3,

    //if command type number > 500
    //undo redo not working for usual workspace
    //but recorded and validated in guide

    COMMAND_TYPES_ALWAYS: {
        sceneAdd: 1,
        sceneRemove: 2,
        sceneRename: 3,
        sceneSort: 4,
        sceneSelect: 5,

        addThread: 101,
        destroyThread: 102,
        destroyBlock: 103,
        recoverBlock: 104,
        insertBlock: 105,
        separateBlock: 106,
        moveBlock: 107,
        cloneBlock: 108,
        uncloneBlock: 109,
        scrollBoard: 110,
        setFieldValue: 111,
        selectBlockMenu: 112,
        destroyBlockBelow: 113,
        destroyThreads: 114,
        addThreads: 115,
        recoverBlockBelow: 116,
        addThreadFromBlockMenu: 117,
        insertBlockFromBlockMenu: 118,
        moveBlockFromBlockMenu: 119,
        separateBlockForDestroy: 120,
        moveBlockForDestroy: 121,
        insertBlockFromBlockMenuFollowSeparate: 122,
        insertBlockFollowSeparate: 123,
        separateBlockByCommand: 124,

        selectObject: 201,
        objectEditButtonClick: 202,
        objectAddPicture: 203,
        objectRemovePicture: 204,
        objectAddSound: 205,
        objectRemoveSound: 206,
        objectNameEdit: 207,
        addObject: 208,
        removeObject: 209,
        objectUpdatePosX: 211,
        objectUpdatePosY: 212,
        objectUpdateSize: 213,
        objectUpdateRotationValue: 214,
        objectUpdateDirectionValue: 215,
        objectUpdateRotateMethod: 216,
        entitySetModel: 217,
        objectAddExpansionBlocks: 218,
        objectRemoveExpansionBlocks: 219,
        objectReorder: 220,

        objectAddAIUtilizeBlocks: 221,
        objectRemoveAIUtilizeBlocks: 222,

        objectAddHardwareLiteBlocks: 223,
        objectRemoveHardwareLiteBlocks: 224,

        do: 301,
        undo: 302,
        redo: 303,

        editPicture: 401,
        uneditPicture: 402,
        processPicture: 403,
        unprocessPicture: 404,
        editText: 405,

        variableContainerAddMessage: 807,
        variableContainerRemoveMessage: 808,

        funcEditStart: 1001,
        funcEditEnd: 1002,
        funcRemove: 1003,
        funcCreate: 1004,

        createComment: 1201,
        removeComment: 1202,
        showAllComment: 1203,
        hideAllComment: 1204,
        moveComment: 1205,
        toggleComment: 1206,
        cloneComment: 1207,
        uncloneComment: 1208,
        separateComment: 1209,
        connectComment: 1210,
        writeComment: 1211,

        dataTableAddSource: 1301,
        dataTableRemoveSource: 1302,
        //TODO commands development
    },

    COMMAND_TYPES_NOT_ALWAYS: {
        addObjectButtonClick: 210,

        toggleRun: 501,
        toggleStop: 502,

        containerSelectObject: 601,

        playgroundChangeViewMode: 701,
        playgroundClickAddPicture: 702,
        playgroundClickAddSound: 703,
        playgroundClickAddPictureCancel: 704,
        playgroundClickAddSoundCancel: 705,
        playgroundClickAddTable: 706,
        playgroundClickAddTableCancel: 707,

        variableContainerSelectFilter: 801,
        variableContainerClickVariableAddButton: 802,
        variableContainerAddVariable: 803,
        variableContainerRemoveVariable: 804,
        variableAddSetName: 805,
        messageSetName: 806,
        variableAddSetScope: 809,
        variableAddSetCloud: 810,
        variableSetVisibility: 811,
        variableSetDefaultValue: 812,
        variableSetSlidable: 813,
        variableSetMinValue: 814,
        variableSetMaxValue: 815,
        variableContainerClickListAddButton: 816,
        variableContainerAddList: 817,
        variableContainerRemoveList: 818,
        listAddSetName: 819,
        listAddSetScope: 820,
        listAddSetCloud: 821,
        listSetVisibility: 822,
        listChangeLength: 823,
        listSetDefaultValue: 824,
        setMessageEditable: 825,
        setVariableEditable: 826,
        setListEditable: 827,
        variableSetName: 828,
        listSetName: 829,
        variableContainerClickMessageAddButton: 830,

        dismissModal: 900,

        playgroundClickAddExpansionBlock: 1101,
        playgroundClickAddExpansionBlockCancel: 1102,

        playgroundClickAddAIUtilizeBlock: 1103,
        playgroundClickAddAIUtilizeBlockCancel: 1104,

        playgroundClickAddHardwareLiteBlock: 1105,
        playgroundClickAddHardwareLiteBlockCancel: 1106,
    },

    RECORDABLE: {
        SUPPORT: 1,
        SKIP: 2,
        ABANDON: 3,
    },

    get COMMAND_TYPES() {
        return Object.assign(
            {},
            Entry.STATIC.COMMAND_TYPES_ALWAYS,
            Entry.STATIC.COMMAND_TYPES_NOT_ALWAYS
        );
    },

    getCommandName(commandType) {
        return _.findKey(Entry.STATIC.COMMAND_TYPES, _.partial(_.isEqual, commandType));
    },
};
<commit_msg>액션키 추가<commit_after>'use strict';

const Entry = require('../entry');

Entry.STATIC = {
    /* data type */
    OBJECT: 0,
    ENTITY: 1,
    SPRITE: 2,
    SOUND: 3,
    VARIABLE: 4,
    FUNCTION: 5,
    SCENE: 6,
    MESSAGE: 7,
    BLOCK_MODEL: 8,
    BLOCK_RENDER_MODEL: 9,
    BOX_MODEL: 10,
    THREAD_MODEL: 11,
    DRAG_INSTANCE: 12,

    /* block state */
    BLOCK_STATIC: 0,
    BLOCK_MOVE: 1,
    BLOCK_FOLLOW: 2,

    /* execute return state */
    RETURN: 0,
    CONTINUE: 1,
    BREAK: 2,
    PASS: 3,

    //if command type number > 500
    //undo redo not working for usual workspace
    //but recorded and validated in guide

    COMMAND_TYPES_ALWAYS: {
        sceneAdd: 1,
        sceneRemove: 2,
        sceneRename: 3,
        sceneSort: 4,
        sceneSelect: 5,

        addThread: 101,
        destroyThread: 102,
        destroyBlock: 103,
        recoverBlock: 104,
        insertBlock: 105,
        separateBlock: 106,
        moveBlock: 107,
        cloneBlock: 108,
        uncloneBlock: 109,
        scrollBoard: 110,
        setFieldValue: 111,
        selectBlockMenu: 112,
        destroyBlockBelow: 113,
        destroyThreads: 114,
        addThreads: 115,
        recoverBlockBelow: 116,
        addThreadFromBlockMenu: 117,
        insertBlockFromBlockMenu: 118,
        moveBlockFromBlockMenu: 119,
        separateBlockForDestroy: 120,
        moveBlockForDestroy: 121,
        insertBlockFromBlockMenuFollowSeparate: 122,
        insertBlockFollowSeparate: 123,
        separateBlockByCommand: 124,

        selectObject: 201,
        objectEditButtonClick: 202,
        objectAddPicture: 203,
        objectRemovePicture: 204,
        objectAddSound: 205,
        objectRemoveSound: 206,
        objectNameEdit: 207,
        addObject: 208,
        removeObject: 209,
        objectUpdatePosX: 211,
        objectUpdatePosY: 212,
        objectUpdateSize: 213,
        objectUpdateRotationValue: 214,
        objectUpdateDirectionValue: 215,
        objectUpdateRotateMethod: 216,
        entitySetModel: 217,
        objectAddExpansionBlocks: 218,
        objectRemoveExpansionBlocks: 219,
        objectReorder: 220,

        objectAddAIUtilizeBlocks: 221,
        objectRemoveAIUtilizeBlocks: 222,

        objectAddHardwareLiteBlocks: 223,
        objectRemoveHardwareLiteBlocks: 224,

        do: 301,
        undo: 302,
        redo: 303,

        editPicture: 401,
        uneditPicture: 402,
        processPicture: 403,
        unprocessPicture: 404,
        editText: 405,

        variableContainerAddMessage: 807,
        variableContainerRemoveMessage: 808,

        funcEditStart: 1001,
        funcEditEnd: 1002,
        funcRemove: 1003,
        funcCreate: 1004,
        funcChangeType: 1005,
        funcLocalVarChangeLength: 1006,
        toggleFuncUseLocalVariables: 1007,
        insertFuncLocalVariable: 1008,
        removeFuncLocalVariableByIndex: 1009,

        createComment: 1201,
        removeComment: 1202,
        showAllComment: 1203,
        hideAllComment: 1204,
        moveComment: 1205,
        toggleComment: 1206,
        cloneComment: 1207,
        uncloneComment: 1208,
        separateComment: 1209,
        connectComment: 1210,
        writeComment: 1211,

        dataTableAddSource: 1301,
        dataTableRemoveSource: 1302,
        //TODO commands development
    },

    COMMAND_TYPES_NOT_ALWAYS: {
        addObjectButtonClick: 210,

        toggleRun: 501,
        toggleStop: 502,

        containerSelectObject: 601,

        playgroundChangeViewMode: 701,
        playgroundClickAddPicture: 702,
        playgroundClickAddSound: 703,
        playgroundClickAddPictureCancel: 704,
        playgroundClickAddSoundCancel: 705,
        playgroundClickAddTable: 706,
        playgroundClickAddTableCancel: 707,

        variableContainerSelectFilter: 801,
        variableContainerClickVariableAddButton: 802,
        variableContainerAddVariable: 803,
        variableContainerRemoveVariable: 804,
        variableAddSetName: 805,
        messageSetName: 806,
        variableAddSetScope: 809,
        variableAddSetCloud: 810,
        variableSetVisibility: 811,
        variableSetDefaultValue: 812,
        variableSetSlidable: 813,
        variableSetMinValue: 814,
        variableSetMaxValue: 815,
        variableContainerClickListAddButton: 816,
        variableContainerAddList: 817,
        variableContainerRemoveList: 818,
        listAddSetName: 819,
        listAddSetScope: 820,
        listAddSetCloud: 821,
        listSetVisibility: 822,
        listChangeLength: 823,
        listSetDefaultValue: 824,
        setMessageEditable: 825,
        setVariableEditable: 826,
        setListEditable: 827,
        variableSetName: 828,
        listSetName: 829,
        variableContainerClickMessageAddButton: 830,

        dismissModal: 900,

        playgroundClickAddExpansionBlock: 1101,
        playgroundClickAddExpansionBlockCancel: 1102,

        playgroundClickAddAIUtilizeBlock: 1103,
        playgroundClickAddAIUtilizeBlockCancel: 1104,

        playgroundClickAddHardwareLiteBlock: 1105,
        playgroundClickAddHardwareLiteBlockCancel: 1106,
    },

    RECORDABLE: {
        SUPPORT: 1,
        SKIP: 2,
        ABANDON: 3,
    },

    get COMMAND_TYPES() {
        return Object.assign(
            {},
            Entry.STATIC.COMMAND_TYPES_ALWAYS,
            Entry.STATIC.COMMAND_TYPES_NOT_ALWAYS
        );
    },

    getCommandName(commandType) {
        return _.findKey(Entry.STATIC.COMMAND_TYPES, _.partial(_.isEqual, commandType));
    },
};
<|endoftext|>


<commit_before>// Import
var BinaryWriter = require("./BinaryWriter");

function SetBorder(playerTracker, border, gameType, serverName) {
    this.playerTracker = playerTracker;
    this.border = border;
    this.gameType = gameType;
    this.serverName = serverName;
}

module.exports = SetBorder;

SetBorder.prototype.build = function(protocol) {
    var writer = new BinaryWriter();
    writer.writeUInt8(0x40);                                // Packet ID
    writer.writeDouble(this.border.minx + this.playerTracker.scrambleX);
    writer.writeDouble(this.border.miny + this.playerTracker.scrambleY);
    writer.writeDouble(this.border.maxx + this.playerTracker.scrambleX);
    writer.writeDouble(this.border.maxy + this.playerTracker.scrambleY);
    if (this.gameType != null) {
        writer.writeUInt32(this.gameType >> 0);
        var name = this.serverName;
        if (name == null) name = "";
        writer.writeStringZeroUtf8(name);
    }
    return writer.toBuffer();
};
<commit_msg>protocol optimizations<commit_after>// Import
var BinaryWriter = require("./BinaryWriter");

function SetBorder(playerTracker, border, gameType, serverName) {
    this.playerTracker = playerTracker;
    this.border = border;
    this.gameType = gameType;
    this.serverName = serverName;
}

module.exports = SetBorder;

SetBorder.prototype.build = function (protocol) {
    var scrambleX = this.playerTracker.scrambleX;
    var scrambleY = this.playerTracker.scrambleY;
    if (this.gameType == null) {
        var buffer = new Buffer(33);
        buffer.writeUInt8(0x40, 0, true);
        buffer.writeDoubleLE(this.border.minx + scrambleX, 1, true);
        buffer.writeDoubleLE(this.border.miny + scrambleY, 9, true);
        buffer.writeDoubleLE(this.border.maxx + scrambleX, 17, true);
        buffer.writeDoubleLE(this.border.maxy + scrambleY, 25, true);
        return buffer;
    }
    var writer = new BinaryWriter();
    writer.writeUInt8(0x40);                                // Packet ID
    writer.writeDouble(this.border.minx + scrambleX);
    writer.writeDouble(this.border.miny + scrambleY);
    writer.writeDouble(this.border.maxx + scrambleX);
    writer.writeDouble(this.border.maxy + scrambleY);
    writer.writeUInt32(this.gameType >> 0);
    var name = this.serverName;
    if (name == null) name = "";
    writer.writeStringZeroUtf8(name);
    return writer.toBuffer();
};
<|endoftext|>


<commit_before>import React, { Component } from 'react'

// Library
import is from 'is_js'
import { gql } from 'react-apollo'
import { trackScreen, trackEvent, trackException } from '../lib/analytics'

import { Buttonz } from '../styles/buttons'

// Styles
import styled from 'styled-components'

const Containerz = styled.div`
  p {
    margin: 0;
    padding: 0;
  }
  text-align: center;
  padding-top: 25vh;
`
const Imagez = styled.img`
  display: inline-block;
  width: 32px;
  height: 32px;
`

class ActionPage extends Component {
  constructor (props) {
    super(props)

    const { search } = this.props

    // Track
    trackScreen(search.action)

    this.state = { status: 'EMPTY_QUERY' }
    if (search.action === 'verify') this.verify(search.id)
  }

  verify = id => {
    console.log('verify:', id)

    // Guard empty
    if (is.undefined(id) || is.null(id)) {
      return
    }

    // Track
    trackEvent('verify', { id })

    // Mark as verify
    this.props.client
      .mutate({
        mutation: gql`mutation {
        updateUser(
          id: "${id}",
          emailVerified: true
        ) {
          emailVerified
        }
      }`
      })
      .then(({ emailVerified }) => {
        trackEvent('verified.succeed', { id })
        this.setState({ status: 'VERIFIED' })
      })
      .catch(err => {
        trackEvent('verify.failed', { id, err })
        trackException(err)
        this.setState({ status: 'NOT_VERIFIED' })
      })
  }

  onClickHome = e => {
    window.location.href = window.location.origin
    trackEvent('go', { page: window.location.origin })
  }

  render () {
    return (
      <Containerz>
        {
          {
            EMPTY_QUERY:
Something gone wrong!?
,
            VERIFIED:
You has been subscribe to our mailing list! Thanks!
,
            NOT_VERIFIED:
Something gone wrong! Please try again later.

          }[this.state.status]
        }

 /

        <Imagez src='./kat.png' />

        <Buttonz onClick={this.onClickHome}>
          GO HOME
        </Buttonz>
      </Containerz>
    )
  }
}

export default ActionPage
<commit_msg>fix(action): Fixed wrong link<commit_after>import React, { Component } from 'react'

// Library
import is from 'is_js'
import { gql } from 'react-apollo'
import { trackScreen, trackEvent, trackException } from '../lib/analytics'

import { Buttonz } from '../styles/buttons'

// Styles
import styled from 'styled-components'

const Containerz = styled.div`
  p {
    margin: 0;
    padding: 0;
  }
  text-align: center;
  padding-top: 25vh;
`
const Imagez = styled.img`
  display: inline-block;
  width: 32px;
  height: 32px;
`

class ActionPage extends Component {
  constructor (props) {
    super(props)

    const { search } = this.props

    // Track
    trackScreen(search.action)

    this.state = { status: 'EMPTY_QUERY' }
    if (search.action === 'verify') this.verify(search.id)
  }

  verify = id => {
    console.log('verify:', id)

    // Guard empty
    if (is.undefined(id) || is.null(id)) {
      return
    }

    // Track
    trackEvent('verify', { id })

    // Mark as verify
    this.props.client
      .mutate({
        mutation: gql`mutation {
        updateUser(
          id: "${id}",
          emailVerified: true
        ) {
          emailVerified
        }
      }`
      })
      .then(({ emailVerified }) => {
        trackEvent('verified.succeed', { id })
        this.setState({ status: 'VERIFIED' })
      })
      .catch(err => {
        trackEvent('verify.failed', { id, err })
        trackException(err)
        this.setState({ status: 'NOT_VERIFIED' })
      })
  }

  onClickHome = e => {
    trackEvent('go', { page: window.location.origin })
    window.location.href = window.location.origin + window.location.pathname
  }

  render () {
    return (
      <Containerz>
        {
          {
            EMPTY_QUERY:
Something gone wrong!?
,
            VERIFIED:
You has been subscribe to our mailing list! Thanks!
,
            NOT_VERIFIED:
Something gone wrong! Please try again later.

          }[this.state.status]
        }

 /

        <Imagez src='./kat.png' />

        <Buttonz onClick={this.onClickHome}>
          GO HOME
        </Buttonz>
      </Containerz>
    )
  }
}

export default ActionPage
<|endoftext|>


<commit_before>/**
 * wrapper around PIXI.tween OR CreateJS/TweenJS to do animations/tweening
 * for exxample for List or Scroller, see Scroller.thrownTo.
 *
 * TODO: support greensock?
 */

function Tween(target, duration, easing, type) {
    this.duration = duration;
    this.easing = easing || 'linear';
    this.type = type || this.checkLibrary();
    if (this.type === Tween.NONE) {
        this._target = target;
    }
    this.createTween(target, duration, easing);
}

Tween.prototype = Object.create( {} );
Tween.prototype.constructor = Tween;
module.exports = Tween;

Tween.PIXI_TWEEN = 'PIXI_TWEEN';
Tween.CREATEJS_TWEEN = 'CREATEJS_TWEEN';
Tween.NONE = 'NONE';

// uppercase first latter, does NOT work like capitalize in python
// it just capitalizes the first letter and let the other characters untouched
function capitalize(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

// TODO: possible alternative: create own easing data type
// e.g. (in, out, inout and type)

// get string e.g. 'linear' or 'quadIn', return ease function
Tween.CREATEJS_EASING = function(ease) {
    // inQutQuad to quadInOut
    if (ease.substring(0, 5) === 'inOut') {
        ease = ease.slice(5).toLowerCase() + 'InOut';
    }
    // inQuad to quadIn
    if (ease.substring(0, 2) === 'in') {
        ease = ease.slice(2).toLowerCase() + 'In';
    }
    if (ease.substring(0, 3) === 'out') {
        ease = ease.slice(3).toLowerCase() + 'Out';
    }
    return createjs.Ease[ease];
};

Tween.PIXI_EASING = function(ease) {
    if (ease.substring(ease.length-5) === 'InOut') {
        ease = 'inOut' + capitalize(ease.slice(0, -5));
    }
    if (ease.substring(ease.length-3) === 'Out') {
        ease = 'out' + capitalize(ease.slice(0, -3));
    }
    if (ease.substring(ease.length-2) === 'In') {
        ease = 'in' + capitalize(ease.slice(0, -2));
    }
    return PIXI.tween.Easing[ease]();
};

/**
 * helper function to check if a tweening-library is present
 */
Tween.prototype.checkLibrary = function() {
    if (window.PIXI && PIXI.tween) {
        return Tween.PIXI_TWEEN;
    } else if (window.createjs && window.createjs.Tween) {
        return Tween.CREATEJS_TWEEN;
    } else {
        return Tween.NONE;
    }
};

Tween.prototype.createTween = function(target, duration, easing) {
    if (this.type === Tween.PIXI_TWEEN) {
        this._tween = PIXI.tweenManager.createTween(target);
        // tweenjs stores time in ms
        this._tween.time = duration;
        // Easing is a function in PIXI.tween.Easing
        this._tween.easing = Tween.PIXI_EASING(easing);
    } else if (this.type === Tween.CREATEJS_TWEEN) {
        createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED;
        createjs.Ticker.setFPS(60);
        this._tween = createjs.Tween.get(target, {loop: false});
    } else {
        this._tween = null;
    }
};

Tween.prototype.to = function(data) {
    if (this.type === Tween.PIXI_TWEEN && this._tween) {
        this._tween.to(data);
        this._tween.start();
    } else if (this.type === Tween.CREATEJS_TWEEN && this._tween) {
        this._tween.to(data, this.duration, Tween.CREATEJS_EASING(this.easing));
    } else if (this.type === Tween.NONE) {
        // no tween, set values directly and without wait
        // maybe we'd like to do some basic linear transitioning
        // in the future even if there is nothing set?
        for (var key in data) {
            this._target[key] = data[key];
        }
    }
};

/**
 * stop tween
 */
Tween.prototype.remove = function() {
    if (this.type === Tween.PIXI_TWEEN && this._tween) {
        PIXI.tween.TweenManager.removeTween(this._tween);
    }
    this._tween = null;
};
<commit_msg>stop/restart Tween when to-value changes<commit_after>/**
 * wrapper around PIXI.tween OR CreateJS/TweenJS to do animations/tweening
 * for exxample for List or Scroller, see Scroller.thrownTo.
 *
 * TODO: support greensock?
 */

function Tween(target, duration, easing, type) {
    this.duration = duration;
    this.easing = easing || 'linear';
    this.type = type || this.checkLibrary();
    if (this.type === Tween.NONE) {
        this._target = target;
    }
    this.createTween(target, duration, easing);
}

Tween.prototype = Object.create( {} );
Tween.prototype.constructor = Tween;
module.exports = Tween;

Tween.PIXI_TWEEN = 'PIXI_TWEEN';
Tween.CREATEJS_TWEEN = 'CREATEJS_TWEEN';
Tween.NONE = 'NONE';

// uppercase first latter, does NOT work like capitalize in python
// it just capitalizes the first letter and let the other characters untouched
function capitalize(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

// TODO: possible alternative: create own easing data type
// e.g. (in, out, inout and type)

// get string e.g. 'linear' or 'quadIn', return ease function
Tween.CREATEJS_EASING = function(ease) {
    // inQutQuad to quadInOut
    if (ease.substring(0, 5) === 'inOut') {
        ease = ease.slice(5).toLowerCase() + 'InOut';
    }
    // inQuad to quadIn
    if (ease.substring(0, 2) === 'in') {
        ease = ease.slice(2).toLowerCase() + 'In';
    }
    if (ease.substring(0, 3) === 'out') {
        ease = ease.slice(3).toLowerCase() + 'Out';
    }
    return createjs.Ease[ease];
};

Tween.PIXI_EASING = function(ease) {
    if (ease.substring(ease.length-5) === 'InOut') {
        ease = 'inOut' + capitalize(ease.slice(0, -5));
    }
    if (ease.substring(ease.length-3) === 'Out') {
        ease = 'out' + capitalize(ease.slice(0, -3));
    }
    if (ease.substring(ease.length-2) === 'In') {
        ease = 'in' + capitalize(ease.slice(0, -2));
    }
    return PIXI.tween.Easing[ease]();
};

/**
 * helper function to check if a tweening-library is present
 */
Tween.prototype.checkLibrary = function() {
    if (window.PIXI && PIXI.tween) {
        return Tween.PIXI_TWEEN;
    } else if (window.createjs && window.createjs.Tween) {
        return Tween.CREATEJS_TWEEN;
    } else {
        return Tween.NONE;
    }
};

Tween.prototype.createTween = function(target, duration, easing) {
    if (this.type === Tween.PIXI_TWEEN) {
        this._tween = PIXI.tweenManager.createTween(target);
        // tweenjs stores time in ms
        this._tween.time = duration;
        // Easing is a function in PIXI.tween.Easing
        this._tween.easing = Tween.PIXI_EASING(easing);
    } else if (this.type === Tween.CREATEJS_TWEEN) {
        createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED;
        createjs.Ticker.setFPS(60);
        this._tween = createjs.Tween.get(target, {loop: false});
    } else {
        this._tween = null;
    }
};

Tween.prototype.to = function(data) {
    if (this.type === Tween.PIXI_TWEEN && this._tween) {
        this._tween.stop();
        this._tween.to(data);
        this._tween.start();
    } else if (this.type === Tween.CREATEJS_TWEEN && this._tween) {
        this._tween.to(data, this.duration, Tween.CREATEJS_EASING(this.easing));
        this._tween.play();
    } else if (this.type === Tween.NONE) {
        // no tween, set values directly and without wait
        // maybe we'd like to do some basic linear transitioning
        // in the future even if there is nothing set?
        for (var key in data) {
            this._target[key] = data[key];
        }
    }
};

/**
 * stop tween
 */
Tween.prototype.remove = function() {
    if (this.type === Tween.PIXI_TWEEN && this._tween) {
        PIXI.tween.TweenManager.removeTween(this._tween);
    }
    this._tween = null;
};
<|endoftext|>


<commit_before>export default class Utils {

  static addKeysToArray(oldArray) {
    const newArray = [];
    oldArray.forEach((item) => {
      const newItem = item;
      if (item.key !== undefined) {
        newItem.primaryKey = item.key;
      }
      newItem.key = oldArray.indexOf(item);
      newArray.push(newItem);
    });
    return newArray;
  }

  static loadUnusedPairs(allProps, properties) {
    if (allProps === undefined || properties === undefined) return {};
    const propMap = {};
    properties.forEach((prop) => {
      if (!propMap[prop.namespace] || propMap[prop.namespace] === undefined) propMap[prop.namespace] = [prop.name];
      else {
        const newList = propMap[prop.namespace];
        newList.push(prop.name);
        propMap[prop.namespace] = newList;
      }
    });

    const resultMap = {};
    Object.keys(allProps).forEach((namespace) => {
      if (!propMap[namespace] || propMap[namespace] === undefined) resultMap[namespace] = allProps[namespace];
      else {
        const resultList = allProps[namespace].filter((name) => {
          return !propMap[namespace].includes(name);
        });
        resultMap[namespace] = resultList;
      }
    });
    return resultMap;
  }

  static getFqnObj(namespace, name) {
    return { namespace, name };
  }
}
<commit_msg>no 1 line if statements<commit_after>export default class Utils {

  static addKeysToArray(oldArray) {
    const newArray = [];
    oldArray.forEach((item) => {
      const newItem = item;
      if (item.key !== undefined) {
        newItem.primaryKey = item.key;
      }
      newItem.key = oldArray.indexOf(item);
      newArray.push(newItem);
    });
    return newArray;
  }

  static loadUnusedPairs(allProps, properties) {
    if (allProps === undefined || properties === undefined) {
      return {};
    }
    const propMap = {};
    properties.forEach((prop) => {
      if (!propMap[prop.namespace] || propMap[prop.namespace] === undefined) {
        propMap[prop.namespace] = [prop.name];
      }
      else {
        const newList = propMap[prop.namespace];
        newList.push(prop.name);
        propMap[prop.namespace] = newList;
      }
    });

    const resultMap = {};
    Object.keys(allProps).forEach((namespace) => {
      if (!propMap[namespace] || propMap[namespace] === undefined) {
        resultMap[namespace] = allProps[namespace];
      }
      else {
        const resultList = allProps[namespace].filter((name) => {
          return !propMap[namespace].includes(name);
        });
        resultMap[namespace] = resultList;
      }
    });
    return resultMap;
  }

  static getFqnObj(namespace, name) {
    return { namespace, name };
  }
}
<|endoftext|>


<commit_before>module.exports = Utils;

/**
 * Misc utility functions
 * @class Utils
 * @constructor
 */
function Utils(){};

/**
 * Append the values in array b to the array a. See this for an explanation.
 * @method appendArray
 * @static
 * @param  {Array} a
 * @param  {Array} b
 */
Utils.appendArray = function(a,b){
    if (b.length < 150000) {
        a.push.apply(a, b);
    } else {
        for (var i = 0, len = b.length; i !== len; ++i) {
            a.push(b[i]);
        }
    }
};

/**
 * Garbage free Array.splice(). Does not allocate a new array.
 * @method splice
 * @static
 * @param  {Array} array
 * @param  {Number} index
 * @param  {Number} howmany
 */
Utils.splice = function(array,index,howmany){
    howmany = howmany || 1;
    for (var i=index, len=array.length-howmany; i < len; i++){
        array[i] = array[i + howmany];
    }
    array.length = len;
};

/**
 * The array type to use for internal numeric computations.
 * @type {Array}
 * @static
 * @property ARRAY_TYPE
 */

//Check the type explicit to prevent IE errors
//Utils.ARRAY_TYPE = Float32Array || Array;
if (typeof Float32Array !== undefined){
  Utils.ARRAY_TYPE = Float32Array;
}else{
  Utils.ARRAY_TYPE = Array;
}

/**
 * Extend an object with the properties of another
 * @static
 * @method extend
 * @param  {object} a
 * @param  {object} b
 */
Utils.extend = function(a,b){
    for(var key in b){
        a[key] = b[key];
    }
};

/**
 * Extend an object with the properties of another
 * @static
 * @method extend
 * @param  {object} a
 * @param  {object} b
 */
Utils.defaults = function(options, defaults){
    options = options || {};
    for(var key in defaults){
        if(!(key in options)){
            options[key] = defaults[key];
        }
    }
    return options;
};
<commit_msg>wrap undefined in quote :cookie:<commit_after>module.exports = Utils;

/**
 * Misc utility functions
 * @class Utils
 * @constructor
 */
function Utils(){};

/**
 * Append the values in array b to the array a. See this for an explanation.
 * @method appendArray
 * @static
 * @param  {Array} a
 * @param  {Array} b
 */
Utils.appendArray = function(a,b){
    if (b.length < 150000) {
        a.push.apply(a, b);
    } else {
        for (var i = 0, len = b.length; i !== len; ++i) {
            a.push(b[i]);
        }
    }
};

/**
 * Garbage free Array.splice(). Does not allocate a new array.
 * @method splice
 * @static
 * @param  {Array} array
 * @param  {Number} index
 * @param  {Number} howmany
 */
Utils.splice = function(array,index,howmany){
    howmany = howmany || 1;
    for (var i=index, len=array.length-howmany; i < len; i++){
        array[i] = array[i + howmany];
    }
    array.length = len;
};

/**
 * The array type to use for internal numeric computations.
 * @type {Array}
 * @static
 * @property ARRAY_TYPE
 */

//Check the type explicit to prevent IE errors
//Utils.ARRAY_TYPE = Float32Array || Array;
if (typeof Float32Array !== 'undefined'){
  Utils.ARRAY_TYPE = Float32Array;
}else{
  Utils.ARRAY_TYPE = Array;
}

/**
 * Extend an object with the properties of another
 * @static
 * @method extend
 * @param  {object} a
 * @param  {object} b
 */
Utils.extend = function(a,b){
    for(var key in b){
        a[key] = b[key];
    }
};

/**
 * Extend an object with the properties of another
 * @static
 * @method extend
 * @param  {object} a
 * @param  {object} b
 */
Utils.defaults = function(options, defaults){
    options = options || {};
    for(var key in defaults){
        if(!(key in options)){
            options[key] = defaults[key];
        }
    }
    return options;
};
<|endoftext|>


<commit_before>import axios from 'axios';
import store from '../store';
import vue from 'vue';
// import router from '../router';

// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 5000                  // 请求超时时间
});

// request拦截器
service.interceptors.request.use(config => {
  // Do something before request is sent
  if (store.getters.token) {
    config.headers['X-Token'] = store.getters.token; // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
  }
  return config;
}, error => {
  // Do something with request error
  console.log(error); // for debug
  Promise.reject(error);
})

// respone拦截器
service.interceptors.response.use(
  response => response,
  /**
  * 下面的注释为通过response自定义code来标示请求状态，当code返回如下情况为权限有问题，登出并返回到登录页
  * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
  */
//  const res = response.data;
//     if (res.code !== 20000) {
//       Message({
//         message: res.message,
//         type: 'error',
//         duration: 5 * 1000
//       });
//       // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
//       if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
//         MessageBox.confirm('你已被登出，可以取消继续留在该页面，或者重新登录', '确定登出', {
//           confirmButtonText: '重新登录',
//           cancelButtonText: '取消',
//           type: 'warning'
//         }).then(() => {
//           store.dispatch('FedLogOut').then(() => {
//             location.reload();// 为了重新实例化vue-router对象 避免bug
//           });
//         })
//       }
//       return Promise.reject(error);
//     } else {
//       return response.data;
//     }
  error => {
    console.log('err' + error);// for debug
    vue.$Message.error({
                    message: error.message,
                    duration: 5 * 1000,
                    closable: true
                });
    return Promise.reject(error);
  }
)

export default service;
<commit_msg>Delete useless code<commit_after>import axios from 'axios';
import store from '../store';
import vue from 'vue';
// import router from '../router';

// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 5000                  // 请求超时时间
});

// request拦截器
service.interceptors.request.use(config => {
  // Do something before request is sent
  return config;
}, error => {
  // Do something with request error
  console.log(error); // for debug
  Promise.reject(error);
})

// respone拦截器
service.interceptors.response.use(
  response => response,
  error => {
    console.log('err' + error);// for debug
    vue.$Message.error({
                    message: error.message,
                    duration: 5 * 1000,
                    closable: true
                });
    return Promise.reject(error);
  }
)

export default service;
<|endoftext|>


<commit_before>import { ViewPropTypes } from 'react-native';

import InteractionManager from './interactionManager';

export { InteractionManager, ViewPropTypes };
<commit_msg>Addresses Expo Web Compatibility Fixes: - [X] Use prop-types instead of ViewPropTypes from react-native<commit_after>import PropTypes from 'prop-types';

import InteractionManager from './interactionManager';

const ViewPropTypes = {
    style:  PropTypes.shape({
        style: PropTypes.any,
    })
}
export { InteractionManager, ViewPropTypes };
<|endoftext|>


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

// Modified from https://github.com/Khan/KAS,
// which is MIT licensed

var fs = require("fs");
var path = require("path");
var jison = require("jison");

var grammar = {
    lex: {
        rules: [
            ["\\s+",                '/* skip whitespace */'],
            ["[0-9]+\\.?",          'return "NUMBER"'],
            ["([0-9]+)?\\.[0-9]+",  'return "NUMBER"'],
            ["\\*",                 'return "*"'],
            ["\\/",                 'return "/"'],
            ["-",                   'return "-"'],
            ["\\+",                 'return "+"'],
            ["\\^",                 'return "^"'],
            ["\\(",                 'return "("'],
            ["\\)",                 'return ")"'],
            ["\\{",                 'return "}"'],
            ["\\{",                 'return "}"'],
            ["\\|",                 'return "|"'],
            ["\\[",                 'return "["'],
            ["\\]",                 'return "]"'],
            ["_",                   'return "_"'],
            ["\\!",                 'return "!"'],
            ["==|!=|<|>|<=|>=",     'return "SIGN"'],
            ["[a-zA-Z_$]",          'return yy.symbolLexer(yytext)'],
            ["$",                   'return "EOF"'],
            [".",                   'return "INVALID"'],
            [";",                   'return "TERMINATOR"']
        ],
        options: {
            flex: true              // pick longest matching token
        }
    },
    operators: [
        ["right", "|"],
        ["left", "+", "-"],
        ["left", "*", "/"],
        ["left", "UMINUS"],
        ["right", "^"]
    ],
    start: "program",
    bnf: {
        "program": [
            ["statementList EOF", "return $1;"],
            ["EOF", "return new yy.StatementList([]);"]
        ],
        "statementList": [
            ["statement", "$$ = [$1]"],
            ["statementList TERMINATOR statement", "$$ = $1; $1.push($3);"],
            ["statementList TERMINATOR", "$$ = $1;"]
        ],
        "line": [

        ],
        "function": [
            ["[ statementList ]", "$$ = new yy.Function([], $2);"],
            ["[ argList | statementList ]", "$$ = new yy.Function($2, $4);"]
        ],
        "expression": [
            ["additive", "$$ = $1;"]
        ],
        "additive": [
            ["additive + multiplicative", "$$ = yy.Add.createOrAppend($1, $3);"],
            ["additive - multiplicative", "$$ = yy.Add.createOrAppend($1, yy.Mul.handleNegative($3, \"subtract\"));"],
            ["multiplicative", "$$ = $1;", {prec: "+"}]
        ],
        "multiplicative": [
            // the second term in an implicit multiplication cannot be negative
            ["multiplicative triglog", "$$ = yy.Mul.fold(yy.Mul.createOrAppend($1, $2));"],
            ["multiplicative * negative", "$$ = yy.Mul.fold(yy.Mul.createOrAppend($1, $3));"],
            ["multiplicative / negative", "$$ = yy.Mul.fold(yy.Mul.handleDivide($1, $3));"],
            ["negative", "$$ = $1;"]
        ],
        "negative": [
            ["- negative", "$$ = yy.Mul.handleNegative($2);", {prec: "UMINUS"}],
            ["triglog", "$$ = $1;"]
        ],
        "trig": [
            ["TRIG", "$$ = [yytext];"]
        ],
        "trigfunc": [
            ["trig", "$$ = $1;"],
            ["trig ^ negative", "$$ = $1.concat($3);"],
            ["TRIGINV", "$$ = [yytext];"]
        ],
        "logbase": [
            ["ln", "$$ = yy.Log.natural();"],
            ["log", "$$ = yy.Log.common();"],
            ["log _ subscriptable", "$$ = $3;"]
        ],
        "triglog": [
            ["trigfunc negative", "$$ = yy.Trig.create($1, $2);"],
            ["logbase negative", "$$ = yy.Log.create($1, $2);"],
            ["power", "$$ = $1;"]
        ],
        "power": [
            ["primitive ^ negative", "$$ = new yy.Pow($1, $3);"],
            ["primitive", "$$ = $1;"]
        ],
        "variable": [
            ["VAR", "$$ = yytext;"]
        ],
        "subscriptable": [
            ["variable _ subscriptable", "$$ = new yy.Var($1, $3);"],
            ["variable", "$$ = new yy.Var($1);"],
            ["CONST", "$$ = new yy.Const(yytext.toLowerCase());"],
            ["INT", "$$ = yy.Int.create(Number(yytext));"],
            ["FLOAT", "$$ = yy.Float.create(Number(yytext));"],
            ["( additive )", "$$ = $2.completeParse().addHint('parens');"] // this probably shouldn't be a hint...
        ],
        "invocation": [
            ["sqrt ( additive )", "$$ = yy.Pow.sqrt($3);"],
            ["abs ( additive )", "$$ = new yy.Abs($3);"],
            ["| additive |", "$$ = new yy.Abs($2);"],
            ["function ( additive )", "$$ = new yy.Func($1, $3);"]
        ],
        "primitive": [
            ["subscriptable", "$$ = $1;"],
            ["invocation", "$$ = $1;"]
        ]
    }
};

var prelude = "";
var parser = (new jison.Parser(grammar)).generate({moduleType: "js"});
var postlude = "\n\nexports.parser = parser;\n";

fs.writeFileSync(path.resolve(__dirname, "parser.js"), prelude + parser + postlude);
<commit_msg>more parser work<commit_after>#!/usr/bin/env node

// Modified from https://github.com/Khan/KAS,
// which is MIT licensed

var fs = require("fs");
var path = require("path");
var jison = require("jison");

var grammar = {
    lex: {
        rules: [
            ["\\s+",                '/* skip whitespace */'],

            ["==|!=|<|>|<=|>=",     'return "SIGN"'],

            ["\\*",                 'return "*"'],
            ["\\/",                 'return "/"'],
            ["-",                   'return "-"'],
            ["\\+",                 'return "+"'],
            ["\\%",                 'return "%"'],

            ["\\(",                 'return "("'],
            ["\\)",                 'return ")"'],
            ["\\{",                 'return "}"'],
            ["\\{",                 'return "}"'],
            ["\\|",                 'return "|"'],
            ["\\[",                 'return "["'],
            ["\\]",                 'return "]"'],

            ["=",                   'return "="'],
            ["\\n+",                'return "NEWLINE"'],
            ["\\!",                 'return "!"'],
            ["\\:",                 'return ":"'],

            ["var",                 'return "VARKEYWORD"'],
            ["mutate",              'return "MUTATE"'],

            ['\\"(\\\\.|[^"\\\n])*\\"', 'return "STRING"'],
            ["[0-9]+\\.?",          'return "NUMBER"'],
            ["([0-9]+)?\\.[0-9]+",  'return "NUMBER"'],
            ["[a-zA-Z_$][a-zA-Z0-9_$]*", 'return "IDENTIFIER"'],

            ["$",                   'return "EOF"'],
            [".",                   'return "INVALID"'],
        ],
        options: {
            flex: true              // pick longest matching token
        }
    },
    operators: [
        ["right", "|"],
        ["left", "+", "-"],
        ["left", "*", "/"],
        ["left", "UMINUS"],
        ["right", "^"]
    ],
    start: "program",
    bnf: {
        "program": [
            ["statementList EOF", "return $1;"],
            ["EOF", "return new yy.StatementList([]);"]
        ],
        "statementList": [
            ["statement", "$$ = [$1]"],
            ["statementList NEWLINE statement", "$$ = $1; $1.push($3);"],
            ["statementList NEWLINE", "$$ = $1;"],
            ["NEWLINE statementList", "$$ = $2;"]
        ],
        "statement": [

        ],
        "expression": [
            ["function", "$$ = $1;"],
            ["literal", "$$ = $1;"]
        ],
        "literal": [
            ["NUMBER", "$$ = $1;"],
            ["STRING", "$$ = $1;"]
        ],
        "table": [
            ["{ }", "$$ = new yy.Table([]);"],
            ["{ fieldList }", "$$ = new yy.Table([]);"]
        ],
        "fieldList": [
            ["field", "$$ = [$1];"],
            ["field NEWLINE field", "$$ = $1; $1.push($3);"]
        ],
        "field": [
            ["expression", "$$ = new yy.Field(null, $1);"],
            ["IDENTIFIER : expression", "$$ = new yy.Field($1, $3);"]
        ],
        "function": [
            ["[ statementList ]", "$$ = new yy.Function([], $2);"],
            ["[ argList | statementList ]", "$$ = new yy.Function($2, $4);"]
        ],
        "additive": [
            ["additive + multiplicative", "$$ = yy.Add.createOrAppend($1, $3);"],
            ["additive - multiplicative", "$$ = yy.Add.createOrAppend($1, yy.Mul.handleNegative($3, \"subtract\"));"],
            ["multiplicative", "$$ = $1;", {prec: "+"}]
        ],
        "multiplicative": [
            // the second term in an implicit multiplication cannot be negative
            ["multiplicative triglog", "$$ = yy.Mul.fold(yy.Mul.createOrAppend($1, $2));"],
            ["multiplicative * negative", "$$ = yy.Mul.fold(yy.Mul.createOrAppend($1, $3));"],
            ["multiplicative / negative", "$$ = yy.Mul.fold(yy.Mul.handleDivide($1, $3));"],
            ["negative", "$$ = $1;"]
        ],
        "negative": [
            ["- negative", "$$ = yy.Mul.handleNegative($2);", {prec: "UMINUS"}],
            ["triglog", "$$ = $1;"]
        ],
        "trig": [
            ["TRIG", "$$ = [yytext];"]
        ],
        "trigfunc": [
            ["trig", "$$ = $1;"],
            ["trig ^ negative", "$$ = $1.concat($3);"],
            ["TRIGINV", "$$ = [yytext];"]
        ],
        "logbase": [
            ["ln", "$$ = yy.Log.natural();"],
            ["log", "$$ = yy.Log.common();"],
            ["log _ subscriptable", "$$ = $3;"]
        ],
        "triglog": [
            ["trigfunc negative", "$$ = yy.Trig.create($1, $2);"],
            ["logbase negative", "$$ = yy.Log.create($1, $2);"],
            ["power", "$$ = $1;"]
        ],
        "power": [
            ["primitive ^ negative", "$$ = new yy.Pow($1, $3);"],
            ["primitive", "$$ = $1;"]
        ],
        "variable": [
            ["VAR", "$$ = yytext;"]
        ],
        "subscriptable": [
            ["variable _ subscriptable", "$$ = new yy.Var($1, $3);"],
            ["variable", "$$ = new yy.Var($1);"],
            ["CONST", "$$ = new yy.Const(yytext.toLowerCase());"],
            ["INT", "$$ = yy.Int.create(Number(yytext));"],
            ["FLOAT", "$$ = yy.Float.create(Number(yytext));"],
            ["( additive )", "$$ = $2.completeParse().addHint('parens');"] // this probably shouldn't be a hint...
        ],
        "invocation": [
            ["sqrt ( additive )", "$$ = yy.Pow.sqrt($3);"],
            ["abs ( additive )", "$$ = new yy.Abs($3);"],
            ["| additive |", "$$ = new yy.Abs($2);"],
            ["function ( additive )", "$$ = new yy.Func($1, $3);"]
        ],
        "primitive": [
            ["subscriptable", "$$ = $1;"],
            ["invocation", "$$ = $1;"]
        ]
    }
};

var prelude = "";
var parser = (new jison.Parser(grammar)).generate({moduleType: "js"});
var postlude = "\n\nexports.parser = parser;\n";

fs.writeFileSync(path.resolve(__dirname, "parser.js"), prelude + parser + postlude);
<|endoftext|>