<commit_before>'use strict'
// Import standardized modules
const sequelize		= require('sequelize')
const express 		= require ('express')
const bodyParser 	= require('body-parser')
const bcrypt 		= require ('bcrypt-nodejs')
const session 		= require('express-session')
const fs 			= require('fs')
const router  		= express.Router ( )

let db = require(__dirname + '/../modules/database')

router.get('/goaloverview', (req, res) => {
	// db.Goal.findAll().then( goals => console.log('Number of goals ' + goals.length) )
	let user = req.session.user
	let message = req.query.message

	if (user === undefined) {

		res.redirect('login?message=' + encodeURIComponent("Please log in."))
	}

	else {
		db.Goal.findAll({
			include: [{
				model: db.Complete,
				where: {userId: req.session.user.id},
				required: false
			}]
		}).then( (goals) => {
			var unfGoalArray = []
			// var finGoalArray = []
			for (var i = 0; i < goals.length; i++) {
				//console.log(goals[i].title + ' has ' + goals[i].completes.length + ' completes')

				if(goals[i].completes.length === 0) {
					unfGoalArray.push(goals[i])
				}
				// if (goals[i].completes.length !== 0) {
				// 	finGoalArray.push(goals[i])
				// }
			}
			return unfGoalArray
		}).then( (unfinishedGoals) => {
			// console.log(unfinishedGoals[0].title)
			for (var i = 0; i < unfinishedGoals.length; i++) {
				// console.log(unfinishedGoals[i])
			}
			fs.writeFile (__dirname + '/../static/json/goals.json', JSON.stringify(unfinishedGoals), 'utf-8', function(error) {
				if(error) throw error
			})
		}).then( () => {
			res.render('goaloverview')
		})
	}
})

router.get('/goal-overview', (req, res) => {
	if(req.session.user) {
		db.Goal.findOne({
			where: {
				id: req.query.id
			}
		}).then((goal) => {
			var goalPoints = goal.points
			console.log(goalPoints)
			console.log('------------------------------------------------')
			db.Complete.create({
				lat: goal.lat,
				lng: goal.lng,
				userId: req.session.user.id,
				goalId: goal.id,
			}).then( () => {
				db.User.findOne({
					where: {
						id: req.session.user.id
					}
				}).then ( (user) => {
					var oldScore = user.score
					console.log(user)
					user.updateAttributes({
						score: oldScore + goalPoints
					})
				})
				res.redirect('goaloverview?message=' + encodeURIComponent("Goal Marked as complete!"))
			})
		})
	}
	else {
		res.redirect('/index')
	}
})

module.exports = router
<commit_msg>fixed merged conflict<commit_after>'use strict'
// Import standardized modules
const sequelize		= require('sequelize')
const express 		= require ('express')
const bodyParser 	= require('body-parser')
const bcrypt 		= require ('bcrypt-nodejs')
const session 		= require('express-session')
const fs 			= require('fs')
const router  		= express.Router ( )

let db = require(__dirname + '/../modules/database')

router.get('/goaloverview', (req, res) => {
	// db.Goal.findAll().then( goals => console.log('Number of goals ' + goals.length) )
	let user = req.session.user
	let message = req.query.message

	if (user === undefined) {

		res.redirect('login?message=' + encodeURIComponent("Please log in."))
	}

	else {
		db.Goal.findAll({
			include: [{
				model: db.Complete,
				where: {userId: req.session.user.id},
				required: false
			}]
		}).then( (goals) => {
			var goalArray = []
			for (var i = 0; i < goals.length; i++) {
				//console.log(goals[i].title + ' has ' + goals[i].completes.length + ' completes')

				if(goals[i].completes.length === 0) {
					goalArray.push(goals[i])
				}
			}
			return goalArray
		}).then( (unfinishedGoals) => {
			for (var i = 0; i < unfinishedGoals.length; i++) {
				// console.log(unfinishedGoals[i])
			}
			fs.writeFile (__dirname + '/../static/json/goals.json', JSON.stringify(unfinishedGoals), 'utf-8', function(error) {
				if(error) throw error
			})
		}).then( () => {
			res.render('goaloverview')
		})
	}
})

router.get('/goal-overview', (req, res) => {
	if(req.session.user) {
		db.Goal.findOne({
			where: {
				id: req.query.id
			}
		}).then((goal) => {
			var goalPoints = goal.points
			console.log(goalPoints)
			console.log('------------------------------------------------')
			db.Complete.create({
				lat: goal.lat,
				lng: goal.lng,
				userId: req.session.user.id,
				goalId: goal.id,
			}).then( () => {
				db.User.findOne({
					where: {
						id: req.session.user.id
					}
				}).then ( (user) => {
					var oldScore = user.score
					console.log(user)
					user.updateAttributes({
						score: oldScore + goalPoints
					})
				})
				res.redirect('goaloverview?message=' + encodeURIComponent("Goal Marked as complete!"))
			})
		})
	}
	else {
		res.redirect('/index')
	}
})

module.exports = router
<|endoftext|>


<commit_before>'use strict';

var BBPromise = require('bluebird');
var _ = require('underscore');
var express = require('express');
var router = require('../lib/util').router();
var pathLib = require('path');

var core = require('kartotherian-core');
var Err = core.Err;

var tilelive = require('tilelive');
BBPromise.promisifyAll(tilelive);

var conf;
var sources;
var metrics;
var maxZoom = 20;
//var vectorHeaders = {'Content-Encoding': 'gzip'};
//var rasterHeaders = {}; // {'Content-Type': 'image/png'};

/**
 * Initialize module
 * @param app
 * @returns {*}
 */
function init(app) {
    //var log = app.logger.log.bind(app.logger);

    metrics = app.metrics;
    metrics.increment('init');

    core.registerProtocols(require('tilelive-bridge'), tilelive);
    core.registerProtocols(require('tilelive-file'), tilelive);
    //core.registerProtocols(require('./dynogen'), tilelive);
    core.registerProtocols(require('kartotherian-overzoom'), tilelive);
    core.registerProtocols(require('kartotherian-cassandra'), tilelive);
    core.registerProtocols(require('tilelive-vector'), tilelive);

    var resolver = function (module) {
        return require.resolve(module);
    };

    app.use('/static/leaflet', express.static(core.sources.getModulePath('leaflet', resolver), core.getStaticOpts(app.conf)));

    core.sources
        .initAsync(app, tilelive, resolver, pathLib.resolve(__dirname, '..'))
        .then(function (srcs) {
            sources = srcs;
            conf = app.conf;
        })
        .catch(function (err) {
            console.error((err.body && (err.body.stack || err.body.detail)) || err.stack || err);
            process.exit(1);
        });
}

/**
 * Web server (express) route handler to get requested tile
 * @param req request object
 * @param res response object
 */
function getTile(req, res) {

    var start = Date.now();
    var opts;

    return BBPromise.try(function () {
        var srcId = req.params.src;
        if (!sources.hasOwnProperty(srcId)) {
            throw new Err('Unknown source %s', srcId).metrics('err.req.source');
        }
        var source = sources[srcId];
        if (!source.public) {
            throw new Err('Source %s not public', srcId).metrics('err.req.source');
        }
        var z = req.params.z | 0;
        var x = req.params.x | 0;
        var y = req.params.y | 0;

        if (!core.isInteger(z) || !core.isInteger(x) || !core.isInteger(y) || z < 0 || z > maxZoom || x < 0 || y < 0) {
            throw new Err('z,x,y must be positive integers').metrics('err.req.coords');
        }
        var maxCoord = Math.pow(2, z);
        if (x >= maxCoord || y >= maxCoord) {
            throw new Err('x,y exceeded max allowed for this zoom').metrics('err.req.coords');
        }
        if (source.minzoom !== undefined && z < source.minzoom) {
            throw new Err('Minimum zoom is %d', source.minzoom).metrics('err.req.zoom');
        }
        if (source.maxzoom !== undefined && z > source.maxzoom) {
            throw new Err('Maximum zoom is %d', source.maxzoom).metrics('err.req.zoom');
        }

        if (source.formats) {
            if (!_.contains(source.formats, req.params.format)) {
                throw new Err('Format %s is not known', req.params.format).metrics('err.req.format');
            }
            opts = {format: req.params.format};

            // For now, if format is allowed, so is scale. We might want to introduce another source config param later
            var scale = req.params.scale ? req.params.scale[1] | 0 : undefined;
            scale = scale > 4 ? 4 : scale; // limits scale to 4x (1024 x 1024 tiles or 288dpi)
            if (scale) {
                opts.scale = scale;
            }
        }
        return core.getTitleWithParamsAsync(source.handler, z, x, y, opts);
    }).spread(function (data, headers) {
        if (opts && opts.format === 'json') {
            if ('summary' in req.query) {
                data = _(data).reduce(function (memo, layer) {
                    memo[layer.name] = {
                        features: layer.features.length,
                        jsonsize: JSON.stringify(layer).length
                    };
                    return memo;
                }, {});
            } else if ('nogeo' in req.query) {
                var filter = function (val, key) {
                    if (key === 'geometry') {
                        return val.length;
                    } else if (_.isArray(val)) {
                        return _.map(val, filter);
                    } else if (_.isObject(val)) {
                        _.each(val, function (v, k) {
                            val[k] = filter(v, k);
                        });
                    }
                    return val;
                };
                data = _.map(data, filter);
            }
        }
        if (conf.cache) {
            res.header('Cache-Control', conf.cache);
        }
        res.set(headers);
        res.send(data);
        var mx = util.format('req.%s.%s', srcId, z);
        if (opts) {
            mx += '.' + opts.format;
            if (opts.scale) {
                mx += '.' + opts.scale;
            }
        }
        metrics.endTiming(mx, start);
    }).catch(function (err) {
        res
            .status(400)
            .header('Cache-Control', 'public, s-maxage=30, max-age=30')
            .json(err.message || 'error/unknown');
        metrics.increment(err.metrics || 'err.unknown');
        req.logger.log(err);
    });
}

router.get('/:src(\\w+)/:z(\\d+)/:x(\\d+)/:y(\\d+).:format([\\w\\.]+)', getTile);
router.get('/:src(\\w+)/:z(\\d+)/:x(\\d+)/:y(\\d+):scale(@\\d+x).:format([\\w\\.]+)', getTile);

module.exports = function(app) {

    init(app);

    return {
        path: '/',
        api_version: 1,
        skip_domain: true,
        router: router
    };

};
<commit_msg>Fixed error/metrics reporting<commit_after>'use strict';

var util = require('util');
var BBPromise = require('bluebird');
var _ = require('underscore');
var express = require('express');
var router = require('../lib/util').router();
var pathLib = require('path');

var core = require('kartotherian-core');
var Err = core.Err;

var tilelive = require('tilelive');
BBPromise.promisifyAll(tilelive);

var conf;
var sources;
var metrics;
var maxZoom = 20;
//var vectorHeaders = {'Content-Encoding': 'gzip'};
//var rasterHeaders = {}; // {'Content-Type': 'image/png'};

function reportError(errReporterFunc, err) {
    try {
        errReporterFunc(err);
    } catch (e2) {
        console.error('Unable to report: ' +
            ((err.body && (err.body.stack || err.body.detail)) || err.stack || err) +
            '\n\nDue to: ' +
            ((e2.body && (e2.body.stack || e2.body.detail)) || e2.stack || e2));
    }
}

/**
 * Initialize module
 * @param app
 * @returns {*}
 */
function init(app) {
    //var log = app.logger.log.bind(app.logger);

    metrics = app.metrics;
    metrics.increment('init');

    core.registerProtocols(require('tilelive-bridge'), tilelive);
    core.registerProtocols(require('tilelive-file'), tilelive);
    //core.registerProtocols(require('./dynogen'), tilelive);
    core.registerProtocols(require('kartotherian-overzoom'), tilelive);
    core.registerProtocols(require('kartotherian-cassandra'), tilelive);
    core.registerProtocols(require('tilelive-vector'), tilelive);

    var resolver = function (module) {
        return require.resolve(module);
    };

    app.use('/static/leaflet', express.static(core.sources.getModulePath('leaflet', resolver), core.getStaticOpts(app.conf)));

    core.sources
        .initAsync(app, tilelive, resolver, pathLib.resolve(__dirname, '..'))
        .then(function (srcs) {
            sources = srcs;
            conf = app.conf;
        })
        .catch(function (err) {
            reportError(function (err) {
                app.logger.log('fatal', err);
            }, err);
            process.exit(1);
        });
}

/**
 * Web server (express) route handler to get requested tile
 * @param req request object
 * @param res response object
 */
function getTile(req, res) {

    var start = Date.now();
    // These vars might get set before finishing validation.
    // Do not use them unless successful
    var opts, srcId, z, x, y;

    return BBPromise.try(function () {
        srcId = req.params.src;
        if (!sources) {
            throw new Err('The service has not started yet');
        }
        if (!sources.hasOwnProperty(srcId)) {
            throw new Err('Unknown source %s', srcId).metrics('err.req.source');
        }
        var source = sources[srcId];
        if (!source.public) {
            throw new Err('Source %s not public', srcId).metrics('err.req.source');
        }
        z = req.params.z | 0;
        x = req.params.x | 0;
        y = req.params.y | 0;

        if (!core.isInteger(z) || !core.isInteger(x) || !core.isInteger(y) || z < 0 || z > maxZoom || x < 0 || y < 0) {
            throw new Err('z,x,y must be positive integers').metrics('err.req.coords');
        }
        var maxCoord = Math.pow(2, z);
        if (x >= maxCoord || y >= maxCoord) {
            throw new Err('x,y exceeded max allowed for this zoom').metrics('err.req.coords');
        }
        if (source.minzoom !== undefined && z < source.minzoom) {
            throw new Err('Minimum zoom is %d', source.minzoom).metrics('err.req.zoom');
        }
        if (source.maxzoom !== undefined && z > source.maxzoom) {
            throw new Err('Maximum zoom is %d', source.maxzoom).metrics('err.req.zoom');
        }

        if (source.formats) {
            if (!_.contains(source.formats, req.params.format)) {
                throw new Err('Format %s is not known', req.params.format).metrics('err.req.format');
            }
            opts = {format: req.params.format};

            // For now, if format is allowed, so is scale. We might want to introduce another source config param later
            var scale = req.params.scale ? req.params.scale[1] | 0 : undefined;
            scale = scale > 4 ? 4 : scale; // limits scale to 4x (1024 x 1024 tiles or 288dpi)
            if (scale) {
                opts.scale = scale;
            }
        }
        return core.getTitleWithParamsAsync(source.handler, z, x, y, opts);
    }).spread(function (data, headers) {
        if (opts && opts.format === 'json') {
            if ('summary' in req.query) {
                data = _(data).reduce(function (memo, layer) {
                    memo[layer.name] = {
                        features: layer.features.length,
                        jsonsize: JSON.stringify(layer).length
                    };
                    return memo;
                }, {});
            } else if ('nogeo' in req.query) {
                var filter = function (val, key) {
                    if (key === 'geometry') {
                        return val.length;
                    } else if (_.isArray(val)) {
                        return _.map(val, filter);
                    } else if (_.isObject(val)) {
                        _.each(val, function (v, k) {
                            val[k] = filter(v, k);
                        });
                    }
                    return val;
                };
                data = _.map(data, filter);
            }
        }
        if (conf.cache) {
            res.header('Cache-Control', conf.cache);
        }
        res.set(headers);
        res.send(data);
        var mx = util.format('req.%s.%s', srcId, z);
        if (opts) {
            mx += '.' + opts.format;
            if (opts.scale) {
                mx += '.' + opts.scale;
            }
        }
        metrics.endTiming(mx, start);
    }).catch(function (err) {
        reportError(function (err) {
            res
                .status(400)
                .header('Cache-Control', 'public, s-maxage=30, max-age=30')
                .json(err.message || 'error/unknown');
            req.logger.log(err);
            metrics.increment(err.metrics || 'err.unknown');
        }, err);
    });
}

router.get('/:src(\\w+)/:z(\\d+)/:x(\\d+)/:y(\\d+).:format([\\w\\.]+)', getTile);
router.get('/:src(\\w+)/:z(\\d+)/:x(\\d+)/:y(\\d+):scale(@\\d+x).:format([\\w\\.]+)', getTile);

module.exports = function(app) {

    init(app);

    return {
        path: '/',
        api_version: 1,
        skip_domain: true,
        router: router
    };

};
<|endoftext|>


<commit_before>/**
 * mobileapp provides page content for the Mobile Apps.
 * The goal is to avoid having to use a web view and style the content natively inside the app
 * using plain TextViews.
 * The payload should not have any extra data, and should be easy to consume by the apps.
 *
 * Status: Prototype -- not ready for production
 * Currently using the mobileview action MW API, and removing some data we don't display.
 * TODO: Try Parsoid
 * TODO: add some transformations that currently are being done by the apps and remove some more unneeded data
 */

'use strict';

var BBPromise = require('bluebird');
var preq = require('preq');
var domino = require('domino');
var sUtil = require('../lib/util');

// shortcut
var HTTPError = sUtil.HTTPError;

/**
 * The main router object
 */
var router = sUtil.router();

/**
 * The main application object reported when this module is require()d
 */
var app;

// gallery constants:
var MAX_ITEM_COUNT = 256;
var MIN_IMAGE_SIZE = 64;
var MAX_IMAGE_WIDTH = 1280;

/**
 * A helper function that obtains the HTML from the MW API and
 * loads it into a domino DOM document instance.
 *
 * @param {String} domain the domain to contact
 * @param {Object} params an Object with all the query parameters for the MW API
 * @return {Promise} a promise resolving as the HTML element object
 */
function apiGet(domain, params) {
    // get the page from the MW API
    return preq.get({
        uri: 'http://' + domain + '/w/api.php',
        query: params
    });
}

function rmSelectorAll(doc, selector) {
    var ps = doc.querySelectorAll(selector) || [];
    for (var idx = 0; idx < ps.length; idx++) {
        var node = ps[idx];
        node.parentNode.removeChild(node);
    }
}

function moveFirstParagraphUpInLeadSection(text) {
    var doc = domino.createDocument(text);
    // TODO: mhurd, feel free to add your lead section magic here
    return doc.body.innerHTML;
}

/**
 * Nukes stuff from the DOM we don't want.
 */
function runDomTransforms(text) {
    var doc = domino.createDocument(text);
    rmSelectorAll(doc, 'div.noprint');
    rmSelectorAll(doc, 'div.infobox');
    rmSelectorAll(doc, 'div.hatnote');
    rmSelectorAll(doc, 'div.metadata');
    rmSelectorAll(doc, 'table'); // TODO: later we may want to transform some of the tables into a JSON structure
    return doc.body.innerHTML;
}

function checkApiResult(apiRes) {
    // check if the query failed
    if (apiRes.status > 299) {
        // there was an error in the MW API, propagate that
        throw new HTTPError({
            status: apiRes.status,
            type: 'api_error',
            title: 'MW API error',
            detail: apiRes.body
        });
    }
}

function checkForQueryPagesIn(apiRes) {
    if (!apiRes.body.query || !apiRes.body.query.pages) {
        // we did not get our expected query.pages from the MW API, propagate that
        console.log('ERROR: no query.pages in result: ' + JSON.stringify(apiRes, null, 2));
        throw new HTTPError({
            status: apiRes.status,
            type: 'api_error',
            title: 'no query.pages in result',
            detail: apiRes.body
        });
    }
}

/** Returns a promise to retrieve the page content from MW API mobileview */
function pagePromise(domain, title) {
    return apiGet(domain, {
        "action": "mobileview",
        "format": "json",
        "page": title,
        "prop": "text|sections|thumb|image|id|revision|description|lastmodified|normalizedtitle|displaytitle|protection|editable",
        "sections": "all",
        "sectionprop": "toclevel|line|anchor",
        "noheadings": true,
        "noimages": true
    })
        .then(function (result) {
            checkApiResult(result);

            // transform all sections
            var sections = result.body.mobileview.sections;
            for (var idx = 0; idx < sections.length; idx++) {
                var section = sections[idx];
                section.text = runDomTransforms(section.text);
            }

            if (!result.body.mobileview.mainpage) {
                // don't do anything if this is the main page, since many wikis
                // arrange the main page in a series of tables.
                // TODO: should we also exclude file and other special pages?
                sections[0].text = moveFirstParagraphUpInLeadSection(sections[0].text);
            }

            return result.body.mobileview;
        });
}

/** Returns a promise to retrieve one or more gallery items. */
function galleryItemsPromise(domain, titles, params) {
    Object.assign(params, {
        "action": "query",
        "format": "json",
        "titles": titles,
        "continue": ""
    });
    //console.log("DEBUG: " + JSON.stringify(params, null, 2));

    return apiGet(domain, params)
        .then(function (result) {
            checkApiResult(result);
            checkForQueryPagesIn(result);

            // TODO: iterate over all items and massage the data
            //var items = result.body.query.pages;
            //for (var key in items) {
            //    if (items.hasOwnProperty(key)) {
            //        var item = items[key];
            //        //console.log("-----");
            //        //console.log(item);
            //    }
            //}

            return result.body.query.pages;
        });
}

/** Gets the gallery content from MW API */
function galleryCollectionPromise(domain, title) {
    return apiGet(domain, {
        "action": "query",
        "format": "json",
        "titles": title,
        "continue": "",
        "prop": "imageinfo",
        "iiprop": "dimensions|mime",
        "generator": "images",
        "gimlimit": MAX_ITEM_COUNT
    })
        .then(function (result) {
            var detailsPromises = [];
            var videos = [], images = [];
            var isVideo;

            checkApiResult(result);
            checkForQueryPagesIn(result);

            // iterate over all items
            var items = result.body.query.pages;
            for (var key in items) {
                if (items.hasOwnProperty(key)) {
                    var item = items[key];

                    // remove the ones that are too small or are of the wrong type
                    var imageinfo = item.imageinfo[0];  // TODO: why this is an array?

                    // Reject gallery items if they're too small.
                    // Also reject SVG and PNG items by default, because they're likely to be
                    // logos and/or presentational images.
                    if (imageinfo.width < MIN_IMAGE_SIZE
                        || imageinfo.height < MIN_IMAGE_SIZE
                        || imageinfo.mime.indexOf("svg") > -1
                        || imageinfo.mime.indexOf("png") > -1
                    ) {
                        delete items[key];
                    } else {
                        delete item.ns;
                        delete item.imagerepository; // we probably don't care where the repo is
                        delete imageinfo.size;
                        // TODO instead of deleting properties we probably want to just add well-known properties

                        isVideo = imageinfo.mime.indexOf("ogg") > -1 || imageinfo.mime.indexOf("video") > -1;

                        // request details individually, to keep the order
                        //detailsPromises.push(galleryItemsPromise(domain, item.title, isVideo));

                        if (isVideo) {
                            videos.push(item.title);
                        } else {
                            images.push(item.title);
                        }
                    }
                }
            }

            // one more request for all the videos
            if (videos.length > 0) {
                detailsPromises.push(galleryItemsPromise(domain, videos.join('|'), {
                    "prop": "videoinfo",
                    "viprop": "url|dimensions|mime|extmetadata|derivatives",
                    "viurlwidth": MAX_IMAGE_WIDTH,
                }));
            }

            // another one request for all the images
            if (images.length > 0) {
                detailsPromises.push(galleryItemsPromise(domain, images.join('|'), {
                    "prop": "imageinfo",
                    "iiprop": "url|dimensions|mime|extmetadata",
                    "iiurlwidth": MAX_IMAGE_WIDTH
                }));
            }

            if (detailsPromises.length > 0) {
                // bring all gallery info together
                return BBPromise.all(detailsPromises);
            } else {
                // no media associated with the page
                return BBPromise.resolve({});
            }
        });
}

/**
 * GET {domain}/v1/mobileapp/{title}
 * Gets the mobile app version of a given wiki page.
 */
router.get('/mobileapp/:title', function (req, res) {
    BBPromise.props({
        page: pagePromise(req.params.domain, req.params.title),
        gallery: galleryCollectionPromise(req.params.domain, req.params.title)
    }).then(function(result) {
        res.status(200).type('json').end(JSON.stringify(result));
    });
});

module.exports = function (appObj) {
    app = appObj;
    return {
        path: '/',
        api_version: 1,
        router: router
    };
};

<commit_msg>mobile: minor refactor<commit_after>/**
 * mobileapp provides page content for the Mobile Apps.
 * The goal is to avoid having to use a web view and style the content natively inside the app
 * using plain TextViews.
 * The payload should not have any extra data, and should be easy to consume by the apps.
 *
 * Status: Prototype -- not ready for production
 * Currently using the mobileview action MW API, and removing some data we don't display.
 * TODO: Try Parsoid
 * TODO: add some transformations that currently are being done by the apps and remove some more unneeded data
 */

'use strict';

var BBPromise = require('bluebird');
var preq = require('preq');
var domino = require('domino');
var sUtil = require('../lib/util');

// shortcut
var HTTPError = sUtil.HTTPError;

/**
 * The main router object
 */
var router = sUtil.router();

/**
 * The main application object reported when this module is require()d
 */
var app;

// gallery constants:
var MAX_ITEM_COUNT = 256;
var MIN_IMAGE_SIZE = 64;
var MAX_IMAGE_WIDTH = 1280;

/**
 * A helper function that obtains the HTML from the MW API and
 * loads it into a domino DOM document instance.
 *
 * @param {String} domain the domain to contact
 * @param {Object} params an Object with all the query parameters for the MW API
 * @return {Promise} a promise resolving as the HTML element object
 */
function apiGet(domain, params) {
    // get the page from the MW API
    return preq.get({
        uri: 'http://' + domain + '/w/api.php',
        query: params
    });
}

function rmSelectorAll(doc, selector) {
    var ps = doc.querySelectorAll(selector) || [];
    for (var idx = 0; idx < ps.length; idx++) {
        var node = ps[idx];
        node.parentNode.removeChild(node);
    }
}

function moveFirstParagraphUpInLeadSection(text) {
    var doc = domino.createDocument(text);
    // TODO: mhurd: feel free to add your lead section magic here
    return doc.body.innerHTML;
}

/**
 * Nukes stuff from the DOM we don't want.
 */
function runDomTransforms(text) {
    var doc = domino.createDocument(text);
    rmSelectorAll(doc, 'div.noprint');
    rmSelectorAll(doc, 'div.infobox');
    rmSelectorAll(doc, 'div.hatnote');
    rmSelectorAll(doc, 'div.metadata');
    rmSelectorAll(doc, 'table'); // TODO: later we may want to transform some of the tables into a JSON structure
    // TODO: mhurd: add more references to functions where you do more transforms here
    return doc.body.innerHTML;
}

function checkApiResult(apiRes) {
    // check if the query failed
    if (apiRes.status > 299) {
        // there was an error in the MW API, propagate that
        throw new HTTPError({
            status: apiRes.status,
            type: 'api_error',
            title: 'MW API error',
            detail: apiRes.body
        });
    }
}

function checkForQueryPagesIn(apiRes) {
    if (!apiRes.body.query || !apiRes.body.query.pages) {
        // we did not get our expected query.pages from the MW API, propagate that
        console.log('ERROR: no query.pages in result: ' + JSON.stringify(apiRes, null, 2));
        throw new HTTPError({
            status: apiRes.status,
            type: 'api_error',
            title: 'no query.pages in result',
            detail: apiRes.body
        });
    }
}

/** Returns a promise to retrieve the page content from MW API mobileview */
function pageContentPromise(domain, title) {
    return apiGet(domain, {
        "action": "mobileview",
        "format": "json",
        "page": title,
        "prop": "text|sections|thumb|image|id|revision|description|lastmodified|normalizedtitle|displaytitle|protection|editable",
        "sections": "all",
        "sectionprop": "toclevel|line|anchor",
        "noheadings": true,
        "noimages": true
    })
        .then(function (result) {
            checkApiResult(result);

            // transform all sections
            var sections = result.body.mobileview.sections;
            for (var idx = 0; idx < sections.length; idx++) {
                var section = sections[idx];
                section.text = runDomTransforms(section.text);
            }

            if (!result.body.mobileview.mainpage) {
                // don't do anything if this is the main page, since many wikis
                // arrange the main page in a series of tables.
                // TODO: should we also exclude file and other special pages?
                sections[0].text = moveFirstParagraphUpInLeadSection(sections[0].text);
            }

            return result.body.mobileview;
        });
}

/** Returns a promise to retrieve one or more gallery items. */
function galleryItemsPromise(domain, titles, params) {
    Object.assign(params, {
        "action": "query",
        "format": "json",
        "titles": titles,
        "continue": ""
    });
    //console.log("DEBUG: " + JSON.stringify(params, null, 2));

    return apiGet(domain, params)
        .then(function (result) {
            checkApiResult(result);
            checkForQueryPagesIn(result);

            // TODO: iterate over all items and massage the data
            //var items = result.body.query.pages;
            //for (var key in items) {
            //    if (items.hasOwnProperty(key)) {
            //        var item = items[key];
            //        //console.log("-----");
            //        //console.log(item);
            //    }
            //}

            return result.body.query.pages;
        });
}

/** Gets the gallery content from MW API */
function galleryCollectionPromise(domain, title) {
    return apiGet(domain, {
        "action": "query",
        "format": "json",
        "titles": title,
        "continue": "",
        "prop": "imageinfo",
        "iiprop": "dimensions|mime",
        "generator": "images",
        "gimlimit": MAX_ITEM_COUNT
    })
        .then(function (result) {
            var detailsPromises = [];
            var videos = [], images = [];
            var isVideo;

            checkApiResult(result);
            checkForQueryPagesIn(result);

            // iterate over all items
            var items = result.body.query.pages;
            for (var key in items) {
                if (items.hasOwnProperty(key)) {
                    var item = items[key];

                    // remove the ones that are too small or are of the wrong type
                    var imageinfo = item.imageinfo[0];  // TODO: why this is an array?

                    // Reject gallery items if they're too small.
                    // Also reject SVG and PNG items by default, because they're likely to be
                    // logos and/or presentational images.
                    if (imageinfo.width < MIN_IMAGE_SIZE
                        || imageinfo.height < MIN_IMAGE_SIZE
                        || imageinfo.mime.indexOf("svg") > -1
                        || imageinfo.mime.indexOf("png") > -1
                    ) {
                        delete items[key];
                    } else {
                        delete item.ns;
                        delete item.imagerepository; // we probably don't care where the repo is
                        delete imageinfo.size;
                        // TODO instead of deleting properties we probably want to just add well-known properties

                        isVideo = imageinfo.mime.indexOf("ogg") > -1 || imageinfo.mime.indexOf("video") > -1;

                        // request details individually, to keep the order
                        //detailsPromises.push(galleryItemsPromise(domain, item.title, isVideo));

                        if (isVideo) {
                            videos.push(item.title);
                        } else {
                            images.push(item.title);
                        }
                    }
                }
            }

            // one more request for all the videos
            if (videos.length > 0) {
                detailsPromises.push(galleryItemsPromise(domain, videos.join('|'), {
                    "prop": "videoinfo",
                    "viprop": "url|dimensions|mime|extmetadata|derivatives",
                    "viurlwidth": MAX_IMAGE_WIDTH,
                }));
            }

            // another one request for all the images
            if (images.length > 0) {
                detailsPromises.push(galleryItemsPromise(domain, images.join('|'), {
                    "prop": "imageinfo",
                    "iiprop": "url|dimensions|mime|extmetadata",
                    "iiurlwidth": MAX_IMAGE_WIDTH
                }));
            }

            if (detailsPromises.length > 0) {
                // bring all gallery info items together
                return BBPromise.all(detailsPromises);
            } else {
                // no media associated with the page
                return BBPromise.resolve({});
            }
        });
}

/**
 * GET {domain}/v1/mobileapp/{title}
 * Gets the mobile app version of a given wiki page.
 */
router.get('/mobileapp/:title', function (req, res) {
    BBPromise.props({
        page: pageContentPromise(req.params.domain, req.params.title),
        gallery: galleryCollectionPromise(req.params.domain, req.params.title)
    }).then(function(result) {
        res.status(200).type('json').end(JSON.stringify(result));
    });
});

module.exports = function (appObj) {
    app = appObj;
    return {
        path: '/',
        api_version: 1,
        router: router
    };
};

<|endoftext|>


<commit_before>const aqp = require('api-query-params').default;    // uses ES6
// --------------------------------------------------------------------------------------
exp = {};
// --------------------------------------------------------------------------------------
// parse query string
// args:
// 1) request url
// 2) array of collection keys, that should be whitelisted
// 3) function to validate time interval
// 4) optional parameter to validation function [bool]
//    only used in validate_interval
//    defines if any date is requied
//    -> true => both are required
//    -> false => none is required
// --------------------------------------------------------------------------------------
exp.parse_query_string = function(url, whitelist, validate, date_req) {
  var qs = url.match(/\?.*$/);  // try to match query string if present

  if(!qs)
    throw { error : "no query string present!"};

  qs = qs[0];           // extract query string part from request url
  qs = qs.substr(1);   // remove '?'

  var query = aqp(qs, {         // parse query string
    whitelist : whitelist       // whitelist collection keys
  });

  validate(query, date_req);    // check correctly specified time interval, throws exception on error
  return query;
}
// --------------------------------------------------------------------------------------
// validate query for correct time interval
// --------------------------------------------------------------------------------------
exp.validate_days = function(query) {
  check_timestamp(query);       // throws exception on error
  valid_timestamp(query);       // throws exception on error

  if(query.filter.timestamp.constructor == Object && Object.keys(query.filter.timestamp).length == 2) {  // range
    var keys = Object.keys(query.filter.timestamp);
    for(var key in keys) {
      if(!(query.filter.timestamp[keys[key]].getHours() == 0 && query.filter.timestamp[keys[key]].getMinutes() == 0
        && query.filter.timestamp[keys[key]].getSeconds() == 0 && query.filter.timestamp[keys[key]].getMilliseconds() == 0))
        throw { error : "incorrect interval specification!" };  // some part is set incorrectly
    }
  }
  else if(query.filter.timestamp.constructor == Object && Object.keys(query.filter.timestamp).length != 2)   // more or less values
  {
    throw { error : "bad number of timestamp values for interval!" };
  }
  else {    // one day only
    if(!(query.filter.timestamp.getHours() == 0 && query.filter.timestamp.getMinutes() == 0
      && query.filter.timestamp.getSeconds() == 0 && query.filter.timestamp.getMilliseconds() == 0))
      throw { error : "incorrect date!" };  // some part is set incorrectly
  }
}
// --------------------------------------------------------------------------------------
// validate query for correct time interval
// --------------------------------------------------------------------------------------
exp.validate_interval = function(query, date_req) {
  if(date_req) {
    check_timestamp(query);
    valid_timestamp(query);

    if(typeof(query.filter.timestamp) == "object" && Object.keys(query.filter.timestamp).length > 1) {  // range
      // is any checking needed here ?
      // TODO
    }
    else {
      throw { error : "end timestamp not defined!" };
    }
  }

  if(query.filter.timestamp != undefined)   // timestamp is defined
    valid_timestamp(query);     // check if date is correct
    // no other validation is needed
}
// --------------------------------------------------------------------------------------
function valid_timestamp(query)
{
  var keys = query.filter.timestamp;

  for(var key in keys)
    if(isNaN(Date.parse(keys[key])))    // invalid date
      throw { error : "invalid date: " + keys[key]};
}
// --------------------------------------------------------------------------------------
function check_timestamp(query)
{
  if(query.filter.timestamp == undefined)
    throw { error : "timestamp must be defined!"};
}
// --------------------------------------------------------------------------------------
module.exports = exp;
<commit_msg>localite errors to czech<commit_after>const aqp = require('api-query-params').default;    // uses ES6
// --------------------------------------------------------------------------------------
exp = {};
// --------------------------------------------------------------------------------------
// parse query string
// args:
// 1) request url
// 2) array of collection keys, that should be whitelisted
// 3) function to validate time interval
// 4) optional parameter to validation function [bool]
//    only used in validate_interval
//    defines if any date is requied
//    -> true => both are required
//    -> false => none is required
// --------------------------------------------------------------------------------------
exp.parse_query_string = function(url, whitelist, validate, date_req) {
  var qs = url.match(/\?.*$/);  // try to match query string if present

  if(!qs)
    throw { error : "V dotazu není přítomen query string!"};

  qs = qs[0];           // extract query string part from request url
  qs = qs.substr(1);   // remove '?'

  var query = aqp(qs, {         // parse query string
    whitelist : whitelist       // whitelist collection keys
  });

  validate(query, date_req);    // check correctly specified time interval, throws exception on error
  return query;
}
// --------------------------------------------------------------------------------------
// validate query for correct time interval
// --------------------------------------------------------------------------------------
exp.validate_days = function(query) {
  check_timestamp(query);       // throws exception on error
  valid_timestamp(query);       // throws exception on error

  if(query.filter.timestamp.constructor == Object && Object.keys(query.filter.timestamp).length == 2) {  // range
    var keys = Object.keys(query.filter.timestamp);
    for(var key in keys) {
      if(!(query.filter.timestamp[keys[key]].getHours() == 0 && query.filter.timestamp[keys[key]].getMinutes() == 0
        && query.filter.timestamp[keys[key]].getSeconds() == 0 && query.filter.timestamp[keys[key]].getMilliseconds() == 0))
        throw { error : "nesprávná specifikace intervalu!" };  // some part is set incorrectly
    }
  }
  else if(query.filter.timestamp.constructor == Object && Object.keys(query.filter.timestamp).length != 2)   // more or less values
  {
    throw { error : "nesprávný počet hodnot proměnné timestamp pro interval!" };
  }
  else {    // one day only
    if(!(query.filter.timestamp.getHours() == 0 && query.filter.timestamp.getMinutes() == 0
      && query.filter.timestamp.getSeconds() == 0 && query.filter.timestamp.getMilliseconds() == 0))
      throw { error : "nesprávné datum!" };  // some part is set incorrectly
  }
}
// --------------------------------------------------------------------------------------
// validate query for correct time interval
// --------------------------------------------------------------------------------------
exp.validate_interval = function(query, date_req) {
  if(date_req) {
    check_timestamp(query);
    valid_timestamp(query);

    if(typeof(query.filter.timestamp) == "object" && Object.keys(query.filter.timestamp).length > 1) {  // range
      // is any checking needed here ?
      // TODO
    }
    else {
      throw { error : "koncový timestamp musí být definován!" };
    }
  }

  if(query.filter.timestamp != undefined)   // timestamp is defined
    valid_timestamp(query);     // check if date is correct
    // no other validation is needed
}
// --------------------------------------------------------------------------------------
function valid_timestamp(query)
{
  var keys = query.filter.timestamp;

  for(var key in keys)
    if(isNaN(Date.parse(keys[key])))    // invalid date
      throw { error : "neplatné datum: " + keys[key]};
}
// --------------------------------------------------------------------------------------
function check_timestamp(query)
{
  if(query.filter.timestamp == undefined)
    throw { error : "timestamp musí být definován!"};
}
// --------------------------------------------------------------------------------------
module.exports = exp;
<|endoftext|>


<commit_before>import _ from 'lodash';
import { Observable } from 'rx';
import { Actions } from 'thundercats';
import debugFactory from 'debug';

const debug = debugFactory('freecc:hikes:actions');

function getCurrentHike(hikes = [{}], dashedName, currentHike) {
  if (!dashedName) {
    debug('no dashedName');
    return hikes[0];
  }

  const filterRegex = new RegExp(dashedName, 'i');
  if (currentHike && filterRegex.test(currentHike.dashedName)) {
    return currentHike;
  }

  debug('setting new hike');
  return hikes
    .filter(({ dashedName }) => {
      return filterRegex.test(dashedName);
    })
    .reduce((throwAway, hike) => {
      return hike;
    }, currentHike || {});
}

function findNextHike(hikes, id) {
  if (!id) {
    debug('find next hike no id provided');
    return hikes[0];
  }
  const currentIndex = _.findIndex(hikes, ({ id: _id }) => _id === id);
  return hikes[currentIndex + 1] || hikes[0];
}

function releaseQuestion(state) {
  const oldHikesApp = state.hikesApp;
  const hikesApp = {
    ...oldHikesApp,
    isPressed: false,
    delta: [0, 0],
    mouse: oldHikesApp.isCorrect ?
      oldHikesApp.mouse :
      [0, 0]
  };

  return { ...state, hikesApp };
}

export default Actions({
  refs: { displayName: 'HikesActions' },
  shouldBindMethods: true,
  fetchHikes({ isPrimed, dashedName }) {
    if (isPrimed) {
      return {
        transform: (state) => {

          const { hikesApp: oldState } = state;
          const currentHike = getCurrentHike(
            oldState.hikes,
            dashedName,
            oldState.currentHike
          );

          const hikesApp = { ...oldState, currentHike };
          return Object.assign({}, state, { hikesApp });
        }
      };
    }

    return this.readService$('hikes', null, null)
      .map(hikes => {
        const currentHike = getCurrentHike(hikes, dashedName);
        return {
          transform(state) {
            const hikesApp = { ...state.hikesApp, currentHike, hikes };
            return { ...state, hikesApp };
          }
        };
      })
      .catch(err => {
        console.error(err);
      });
  },

  toggleQuestions() {
    return {
      transform(state) {
        const hikesApp = { ...state.hikesApp, showQuestions: true };
        return { ...state, hikesApp };
      }
    };
  },

  hideInfo() {
    return {
      transform(state) {
        const hikesApp = { ...state.hikesApp, showInfo: false };
        return { ...state, hikesApp };
      }
    };
  },

  grabQuestion({ pressX, pressY, pageX, pageY }) {
    const dx = pageX - pressX;
    const dy = pageY - pressY;

    const delta = [dx, dy];
    const mouse = [pageX - dx, pageY - dy];

    return {
      transform(state) {
        const hikesApp = { ...state.hikesApp, isPressed: true, delta, mouse };
        return { ...state, hikesApp };
      }
    };
  },

  releaseQuestion() {
    return { transform: releaseQuestion };
  },

  moveQuestion(mouse) {
    return {
      transform(state) {
        const hikesApp = { ...state.hikesApp, mouse };
        return { ...state, hikesApp };
      }
    };
  },

  answer({
    answer,
    userAnswer,
    props: {
      hike: { id, name, tests, challengeType },
      currentQuestion,
      username
    }
  }) {

    // incorrect question
    if (answer !== userAnswer) {
      const startShake = {
        transform(state) {
          const hikesApp = { ...state.hikesApp, showInfo: true, shake: true };
          return { ...state, hikesApp };
        }
      };

      const removeShake = {
        transform(state) {
          const hikesApp = { ...state.hikesApp, shake: false };
          return { ...state, hikesApp };
        }
      };

      return Observable
        .just(removeShake)
        .delay(500)
        .startWith({ transform: releaseQuestion }, startShake);
    }

    // move to next question
    // index 0
    if (tests[currentQuestion]) {

      return {
        transform(state) {

          const hikesApp = {
            ...state.hikesApp,
            currentQuestion: currentQuestion + 1
          };

          return { ...state, hikesApp };
        }
      };
    }

    // challenge completed
    const optimisticSave = username ?
      this.post$('/completed-challenge', { id, name, challengeType }) :
      Observable.just(true);

    const correctAnswer = {
      transform(state) {
        const hikesApp = {
          ...state.hikesApp,
          isCorrect: true,
          isPressed: false,
          delta: [0, 0],
          mouse: [ userAnswer ? 1000 : -1000, 0]
        };
        return { ...state, hikesApp };
      }
    };

    return Observable.just({
        transform(state) {
          const { hikes, currentHike: { id } } = state.hikesApp;
          const currentHike = findNextHike(hikes, id);

          // go to next route
          state.route = currentHike && currentHike.dashedName ?
            `/hikes/${ currentHike.dashedName }` :
            '/hikes';

          const hikesApp = {
            ...state.hikesApp,
            currentHike,
            showQuestions: false,
            currentQuestion: 1,
            mouse: [0, 0]
          };

          return { ...state, hikesApp };
        },
        optimistic: optimisticSave
      })
      .delay(500)
      .startWith(correctAnswer)
      .catch(err => {
        console.error(err);
        return Observable.just({
          set: {
            error: err
          }
        });
      });
  }
});
<commit_msg>Fix question motion on correct answer<commit_after>import _ from 'lodash';
import { Observable } from 'rx';
import { Actions } from 'thundercats';
import debugFactory from 'debug';

const debug = debugFactory('freecc:hikes:actions');

function getCurrentHike(hikes = [{}], dashedName, currentHike) {
  if (!dashedName) {
    debug('no dashedName');
    return hikes[0];
  }

  const filterRegex = new RegExp(dashedName, 'i');
  if (currentHike && filterRegex.test(currentHike.dashedName)) {
    return currentHike;
  }

  debug('setting new hike');
  return hikes
    .filter(({ dashedName }) => {
      return filterRegex.test(dashedName);
    })
    .reduce((throwAway, hike) => {
      return hike;
    }, currentHike || {});
}

function findNextHike(hikes, id) {
  if (!id) {
    debug('find next hike no id provided');
    return hikes[0];
  }
  const currentIndex = _.findIndex(hikes, ({ id: _id }) => _id === id);
  return hikes[currentIndex + 1] || hikes[0];
}

function releaseQuestion(state) {
  const oldHikesApp = state.hikesApp;
  const hikesApp = {
    ...oldHikesApp,
    isPressed: false,
    delta: [0, 0],
    mouse: oldHikesApp.isCorrect ?
      oldHikesApp.mouse :
      [0, 0]
  };

  return { ...state, hikesApp };
}

export default Actions({
  refs: { displayName: 'HikesActions' },
  shouldBindMethods: true,
  fetchHikes({ isPrimed, dashedName }) {
    if (isPrimed) {
      return {
        transform: (state) => {

          const { hikesApp: oldState } = state;
          const currentHike = getCurrentHike(
            oldState.hikes,
            dashedName,
            oldState.currentHike
          );

          const hikesApp = { ...oldState, currentHike };
          return Object.assign({}, state, { hikesApp });
        }
      };
    }

    return this.readService$('hikes', null, null)
      .map(hikes => {
        const currentHike = getCurrentHike(hikes, dashedName);
        return {
          transform(state) {
            const hikesApp = { ...state.hikesApp, currentHike, hikes };
            return { ...state, hikesApp };
          }
        };
      })
      .catch(err => {
        console.error(err);
      });
  },

  toggleQuestions() {
    return {
      transform(state) {
        const hikesApp = { ...state.hikesApp, showQuestions: true };
        return { ...state, hikesApp };
      }
    };
  },

  hideInfo() {
    return {
      transform(state) {
        const hikesApp = { ...state.hikesApp, showInfo: false };
        return { ...state, hikesApp };
      }
    };
  },

  grabQuestion({ pressX, pressY, pageX, pageY }) {
    const dx = pageX - pressX;
    const dy = pageY - pressY;

    const delta = [dx, dy];
    const mouse = [pageX - dx, pageY - dy];

    return {
      transform(state) {
        const hikesApp = { ...state.hikesApp, isPressed: true, delta, mouse };
        return { ...state, hikesApp };
      }
    };
  },

  releaseQuestion() {
    return { transform: releaseQuestion };
  },

  moveQuestion(mouse) {
    return {
      transform(state) {
        const hikesApp = { ...state.hikesApp, mouse };
        return { ...state, hikesApp };
      }
    };
  },

  answer({
    answer,
    userAnswer,
    props: {
      hike: { id, name, tests, challengeType },
      currentQuestion,
      username
    }
  }) {

    // incorrect question
    if (answer !== userAnswer) {
      const startShake = {
        transform(state) {
          const hikesApp = { ...state.hikesApp, showInfo: true, shake: true };
          return { ...state, hikesApp };
        }
      };

      const removeShake = {
        transform(state) {
          const hikesApp = { ...state.hikesApp, shake: false };
          return { ...state, hikesApp };
        }
      };

      return Observable
        .just(removeShake)
        .delay(500)
        .startWith({ transform: releaseQuestion }, startShake);
    }

    // move to next question
    // index 0
    if (tests[currentQuestion]) {

      return Observable.just({
        transform(state) {
          const hikesApp = {
            ...state.hikesApp,
            mouse: [0, 0]
          };
          return { ...state, hikesApp };
        }
      })
        .delay(300)
        .startWith({
          transform(state) {

            const hikesApp = {
              ...state.hikesApp,
              currentQuestion: currentQuestion + 1,
              mouse: [ userAnswer ? 1000 : -1000, 0]
            };

            return { ...state, hikesApp };
          }
        });
    }

    // challenge completed
    const optimisticSave = username ?
      this.post$('/completed-challenge', { id, name, challengeType }) :
      Observable.just(true);

    const correctAnswer = {
      transform(state) {
        const hikesApp = {
          ...state.hikesApp,
          isCorrect: true,
          isPressed: false,
          delta: [0, 0],
          mouse: [ userAnswer ? 1000 : -1000, 0]
        };
        return { ...state, hikesApp };
      }
    };

    return Observable.just({
        transform(state) {
          const { hikes, currentHike: { id } } = state.hikesApp;
          const currentHike = findNextHike(hikes, id);

          // go to next route
          state.route = currentHike && currentHike.dashedName ?
            `/hikes/${ currentHike.dashedName }` :
            '/hikes';

          const hikesApp = {
            ...state.hikesApp,
            currentHike,
            showQuestions: false,
            currentQuestion: 1,
            mouse: [0, 0]
          };

          return { ...state, hikesApp };
        },
        optimistic: optimisticSave
      })
      .delay(300)
      .startWith(correctAnswer)
      .catch(err => {
        console.error(err);
        return Observable.just({
          set: {
            error: err
          }
        });
      });
  }
});
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

var paintings = {
  "paintings": [
  {
    "paintingId": 1,
    "creatorId": 11,
    "ownerId": 111,
    "title": "depressed",
    "image": "https://www.flickr.com/photos/imagomundiphoto/25636563386/",
    "price": 5
  },
  {
    "paintingId": 2,
    "creatorId": 22,
    "ownerId": 222,
    "title": "disturbed",
    "image": "https://www.flickr.com/photos/imagomundiphoto/25636574156/",
    "price": 20
  },
  {
    "paintingId": 3,
    "creatorId": 33,
    "ownerId": 333,
    "title": "artsy fartsy",
    "image": "https://www.flickr.com/photos/ajajajajajajaj/5130007088/",
    "price": 300
  },
  {
    "paintingId": 7,
    "creatorId": 11,
    "ownerId": 111,
    "title": "Pixel",
    "image": "https://www.flickr.com/photos/abababababbaabbab/51300070000/",
    "price": 55
  },
  {
    "paintingId": 11,
    "creatorId": 2,
    "ownerId": 666,
    "title": "Pixel v Vector",
    "image": "https://www.flickr.com/photos/uuuuuumememememem/51300072222/",
    "price": 5
  },
  {
    "paintingId": 50,
    "creatorId": 33,
    "ownerId": 333,
    "title": "Vector",
    "image": "https://www.flickr.com/photos/jjejejjejejejejej/5130066666/",
    "price": 10
  },
  {
    "paintingId": 8,
    "creatorId": 2,
    "ownerId": 55,
    "title": "Pixel2",
    "image": "https://www.flickr.com/photos/yuyouyouyou/51300123123/",
    "price": 10}
    ]
  }

  /* GET all paintings || get all paintings by creatorId*/
  router.get('/', function(req, res, next) {
    if(req.query.creatorId){
      var filteredPaintings = paintings.filter(function (painting) {
        return painting.creatorId === req.query.creatorId
      })
      res.json(filteredPaintings);
    } else {
      res.json(paintings)
    }
});

module.exports = router;
<commit_msg>Add final query test<commit_after>var express = require('express');
var router = express.Router();

var paintings = {
  "paintings": [
  {
    "paintingId": 1,
    "creatorId": 11,
    "ownerId": 111,
    "title": "depressed",
    "image": "https://www.flickr.com/photos/imagomundiphoto/25636563386/",
    "price": 5
  },
  {
    "paintingId": 2,
    "creatorId": 22,
    "ownerId": 222,
    "title": "disturbed",
    "image": "https://www.flickr.com/photos/imagomundiphoto/25636574156/",
    "price": 20
  },
  {
    "paintingId": 3,
    "creatorId": 33,
    "ownerId": 333,
    "title": "artsy fartsy",
    "image": "https://www.flickr.com/photos/ajajajajajajaj/5130007088/",
    "price": 300
  },
  {
    "paintingId": 7,
    "creatorId": 11,
    "ownerId": 111,
    "title": "Pixel",
    "image": "https://www.flickr.com/photos/abababababbaabbab/51300070000/",
    "price": 55
  },
  {
    "paintingId": 11,
    "creatorId": 2,
    "ownerId": 666,
    "title": "Pixel v Vector",
    "image": "https://www.flickr.com/photos/uuuuuumememememem/51300072222/",
    "price": 5
  },
  {
    "paintingId": 50,
    "creatorId": 33,
    "ownerId": 333,
    "title": "Vector",
    "image": "https://www.flickr.com/photos/jjejejjejejejejej/5130066666/",
    "price": 10
  },
  {
    "paintingId": 8,
    "creatorId": 2,
    "ownerId": 55,
    "title": "Pixel2",
    "image": "https://www.flickr.com/photos/yuyouyouyou/51300123123/",
    "price": 10}
    ]
  }

  /* GET all paintings || get all paintings by creatorId*/
  router.get('/', function(req, res, next) {
    if(req.query.creatorId){
      var filteredPaintings = paintings.paintings.filter(function (painting) {
        return painting.creatorId == req.query.creatorId
      })
      res.json(filteredPaintings);
    } else {
      res.json(paintings)
    }
});

module.exports = router;
<|endoftext|>


<commit_before>var app = angular.module('routing', []);
var hash_params = L.Hash.parseHash(location.hash);
var mode_mapping = {
  'foot' : 'pedestrian',
  'car' : 'auto',
  'bicycle' : 'bicycle',
  'transit' : 'multimodal'
};
var date = new Date();
var isoDateTime = date.toISOString(); //"2015-06-12T15:28:46.493Z"
var serviceUrl;
var envToken;
var elevToken;
var envServer;
var elevServiceUrl;

function selectEnv() {
  $("option:selected").each(function() {
    envServer = $(this).text();
    serviceUrl = document.getElementById(envServer).value;
    getEnvToken();
  });
}

function getEnvToken() {
  switch (envServer) {
  case "localhost":
    envToken = accessToken.local;
    elevServiceUrl = elevationServer.local;
    elevToken = elevAccessToken.local;
    break;
  case "development":
    envToken = accessToken.dev;
    elevServiceUrl = elevationServer.dev;
    elevToken = elevAccessToken.dev;
    break;
  case "production":
    envToken = accessToken.prod;
    elevServiceUrl = elevationServer.prod;
    elevToken = elevAccessToken.prod;
    break;
  }
}

//sets ISO date time to 12:15 of current date on initial transit run
function parseIsoDateTime(dtStr) {
  var dt = dtStr.split("T");
  return dtStr.replace(dt[1], "12:15:00");
}
var dateStr = parseIsoDateTime(isoDateTime.toString());

app.run(function($rootScope) {
  var hash_loc = hash_params ? hash_params : {
    'center' : {
      'lat' : 40.7486,
      'lng' : -73.9690
    },
    'zoom' : 13
  };
  $rootScope.geobase = {
    'zoom' : hash_loc.zoom,
    'lat' : hash_loc.center.lat,
    'lon' : hash_loc.center.lng
  }
  $(document).on('new-location', function(e) {
    $rootScope.geobase = {
      'zoom' : e.zoom,
      'lat' : e.lat,
      'lon' : e.lon
    };
  })
});

app.controller('RouteController', function($scope, $rootScope, $sce, $http) {
  var roadmap = L.tileLayer('http://otile3.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', {
    attribution : 'Map data © OpenStreetMap'
  }), cyclemap = L.tileLayer('http://b.tile.thunderforest.com/cycle/{z}/{x}/{y}.png', {
    attribution : 'Maps © Thunderforest, ;Data © OpenStreetMap contributors'
  }), transitmap = L.tileLayer(' http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png', {
    attribution : 'Map data © OpenStreetMap'
  });

  var baseMaps = {
    "RoadMap" : roadmap,
    "CycleMap" : cyclemap,
    "TransitMap" : transitmap
  };

  var map = L.map('map', {
    zoom : $rootScope.geobase.zoom,
    zoomControl : false,
    layers : [ roadmap ],
    center : [ $rootScope.geobase.lat, $rootScope.geobase.lon ]
  });

  L.control.layers(baseMaps, null).addTo(map);

  $scope.route_instructions = '';

  var Locations = [];
  var mode = 'car';

  var icon = L.icon({
    iconUrl : 'resource/dot.png',

    iconSize : [ 38, 35 ], // size of the icon
    shadowSize : [ 50, 64 ], // size of the shadow
    iconAnchor : [ 22, 34 ], // point of the icon which will correspond to marker's location
    shadowAnchor : [ 4, 62 ], // the same for the shadow
    popupAnchor : [ -3, -76 ]
  // point from which the popup should open relative to the iconAnchor
  });

  var mode_icons = {
    'car' : 'js/images/drive.png',
    'foot' : 'js/images/walk.png',
    'bicycle' : 'js/images/bike.png'
  };

  var getStartIcon = function(icon) {
    return L.icon({
      iconUrl : 'resource/startmarker@2x.png',
      iconSize : [ 44, 56 ], // size of the icon
      iconAnchor : [ 22, 50 ]
    });
  };

  var getEndIcon = function(icon) {
    return L.icon({
      iconUrl : 'resource/destmarker@2x.png',
      iconSize : [ 44, 56 ], // size of the icon
      iconAnchor : [ 22, 50 ]
    });
  };

  // Set up the hash
  var hash = new L.Hash(map);
  var markers = [];
  var remove_markers = function() {
    for (i = 0; i < markers.length; i++) {
      map.removeLayer(markers[i]);
    }
    markers = [];
  };

  // Number of locations
  var locations = 0;

  var reset = function() {
    $('svg').html('');
    $('.leaflet-routing-container').remove();
    $('.leaflet-marker-icon.leaflet-marker-draggable').remove();
    $scope.$emit('resetRouteInstruction');
    remove_markers();
    locations = 0;
  };

  $rootScope.$on('map.setView', function(ev, geo, zoom) {
    map.setView(geo, zoom || 8);
  });
  $rootScope.$on('map.dropMarker', function(ev, geo, m) {

    if (locations == 0) {
      var marker = new L.marker(geo, {
        icon : getStartIcon(m || 'car')
      });
      marker.bindPopup("Edit POI here");
    } else {
      var marker = new L.marker(geo, {
        icon : getEndIcon(m || 'car')
      });
      marker.bindPopup("Edit POI here");
    }
    map.addLayer(marker);
    markers.push(marker);
  });

  $scope.renderHtml = function(html_code) {
    return $sce.trustAsHtml(html_code);
  };

  $scope.$on('setRouteInstruction', function(ev, instructions) {
    $scope.$apply(function() {
      $scope.route_instructions = instructions;
    });
  });

  $scope.$on('resetRouteInstruction', function(ev) {
    $scope.$apply(function() {
      $scope.route_instructions = '';
    });
  });

  map.on('click', function(e) {
    var geo = {
      'lat' : e.latlng.lat,
      'lon' : e.latlng.lng
    };

    if (locations == 0) {
      Locations.push({
        lat : geo.lat,
        lon : geo.lon
      })
      $rootScope.$emit('map.dropMarker', [ geo.lat, geo.lon ], mode);
      locations++;
      return;
    } else if (locations > 1) {
      Locations = [];
      reset();

      Locations.push({
        lat : geo.lat,
        lon : geo.lon
      })
      $rootScope.$emit('map.dropMarker', [ geo.lat, geo.lon ], mode);
      locations++;
      return;
    }

    $scope.$on('setRouteInstruction', function(ev, instructions) {
      $scope.$apply(function() {
        $scope.route_instructions = instructions;
      });
    });

    $scope.$on('resetRouteInstruction', function(ev) {
      $scope.$apply(function() {
        $scope.route_instructions = '';
      });
    });

    var waypoints = [];
    Locations.forEach(function(gLoc) {
      waypoints.push(L.latLng(gLoc.lat, gLoc.lon));
    });

    waypoints.push(L.latLng(geo.lat, geo.lon));

    $rootScope.$emit('map.dropMarker', [ geo.lat, geo.lon ], mode);
    locations++;

    valhalla_mode = mode_mapping[mode];

    var rr = L.Routing.control(
        {
          waypoints : waypoints,
          geocoder : null,
          transitmode : valhalla_mode,
          routeWhileDragging : false,
          router : L.Routing.valhalla(envToken, 'auto'),
          summaryTemplate : '
{name}

{distance}, {time}
',

          createMarker : function(i, wp, n) {
            var iconV;
            if (i == 0) {
              iconV = L.icon({
                iconUrl : 'resource/dot.png',
                iconSize : [ 24, 24 ]
              });
            } else {
              iconV = L.icon({
                iconUrl : 'resource/dot.png',
                iconSize : [ 24, 24 ]
              })
            }
            var options = {
              draggable : true,
              icon : iconV
            }
            var dot = L.marker(wp.latLng, options);
            return dot.bindPopup("Edit POI here");
          },
          formatter : new L.Routing.Valhalla.Formatter(),
          pointMarkerStyle : {
            radius : 6,
            color : '#25A5FA',
            fillColor : '#5E6472',
            opacity : 1,
            fillOpacity : 1
          }
        }).addTo(map);

    var driveBtn = document.getElementById("drive_btn");
    var bikeBtn = document.getElementById("bike_btn");
    var walkBtn = document.getElementById("walk_btn");
    var multiBtn = document.getElementById("multi_btn");
    var elevationBtn = document.getElementById("elevation_btn");
    var routeresponse;

    driveBtn.addEventListener('click', function(e) {
      getEnvToken();
      rr.route({
        transitmode : 'auto'
      });
    });

    bikeBtn.addEventListener('click', function(e) {
      getEnvToken();
      var bikeoptions = setBikeOptions();
      rr.route({
        transitmode : 'bicycle',
        costing_options : bikeoptions
      });
    });

    walkBtn.addEventListener('click', function(e) {
      getEnvToken();
      rr.route({
        transitmode : 'pedestrian'
      });
    });

  multiBtn.addEventListener('click', function (e) {
	getEnvToken();
    rr.route({transitmode: 'multimodal', date_time: dateStr});
  });

  elevationBtn.addEventListener('click', function (e) {
    selectEnv();
    var elev = (typeof rr._routes[0] != "undefined") ? L.elevation(elevToken, rr._routes[0].rrshape) : 0;
    elev.resetChart();
    elev.profile(elev._rrshape);
    document.getElementById('graph').style.display="block";
  });

  function setBikeOptions () {
	var btype = document.getElementsByName("btype");
	var bicycle_type = "Road";
	  for (var i=0;i<btype.length;i++){
	    if ( btype[i].checked ) {
	    	bicycle_type = btype[i].value;
	    }
	  }
	var use_roads = document.getElementById("use_roads").value;
	var cycling_speed = document.getElementById("cycle_speed").value;
	var use_hills = document.getElementById("use_hills").value;

	bikeoptions = {"bicycle":{
	  bicycle_type: bicycle_type,
	  use_roads: use_roads,
	  cycling_speed: cycling_speed,
	  use_hills: use_hills
	}}
	return bikeoptions;
  };

    /*
    function openWin(id) {
      var divText = document.getElementById(id).innerHTML;
      myWindow=window.open('','','height: 100; width:200;');
      var doc = myWindow.document;
      doc.open();
      doc.write(divText);
      doc.close();
    }*/

    function datetimeUpdate(datetime) {
      var changeDt = datetime;
      var inputDate, splitDate, year, month, day, time, hour, minute;
      if (changeDt != null) {
        if (changeDt.length >= 11) {
          inputDate = changeDt.split(" ");
          splitDate = inputDate[0].split("-");
          day = splitDate[0];
          if (day < 10) {
            day = '0' + day;
          }
          month = GetMonthIndex(splitDate[1]) + 1;
          if (month < 10) {
            month = '0' + month;
          }
          year = splitDate[2];

          time = inputDate[1].split(":");
          hour = time[0];
          minute = time[1];

          dateStr = year + "-" + month + "-" + day + "T" + hour + ":" + minute;
        } else {
          dateStr = parseIsoDateTime(isoDateTime.toString());
        }
        multiBtn.click();
      }
    }
    ;

    function setBikeOptions() {
      var btype = document.getElementsByName("btype");
      var bicycle_type = "Road";
      for (var i = 0; i < btype.length; i++) {
        if (btype[i].checked) {
          bicycle_type = btype[i].value;
        }
      }
      var use_roads = document.getElementById("use_roads").value;
      var cycling_speed = document.getElementById("cycle_speed").value;
      var hilliness_factor = document.getElementById("use_hills").value;

      bikeoptions = {
        "bicycle" : {
          bicycle_type : bicycle_type,
          use_roads : use_roads,
          cycling_speed : cycling_speed,
          hilliness_factor : hilliness_factor
        }
      }
      return bikeoptions;
    }
    ;

    $(document).on('mode-alert', function(e, m) {
      mode = m;
      reset();
      Locations = [];
    });

    $(document).on('route:time_distance', function(e, td) {
      var instructions = $('.leaflet-routing-container.leaflet-control').html();
      $scope.$emit('setRouteInstruction', instructions);
    });

    $("#datepicker").on("click", function() {
      datetimeUpdate(this.value);
    });
  });

  $("#showbtn").on("click", function() {
    document.getElementById('options').style.display = "block";
  });

  $("#hidebtn").on("click", function() {
    document.getElementById('options').style.display = "none";
  });

  $("#hidechart").on("click", function() {
    document.getElementById('graph').style.display = "none";
  });
})
<commit_msg>removed old duplicate bike option function<commit_after>var app = angular.module('routing', []);
var hash_params = L.Hash.parseHash(location.hash);
var mode_mapping = {
  'foot' : 'pedestrian',
  'car' : 'auto',
  'bicycle' : 'bicycle',
  'transit' : 'multimodal'
};
var date = new Date();
var isoDateTime = date.toISOString(); //"2015-06-12T15:28:46.493Z"
var serviceUrl;
var envToken;
var elevToken;
var envServer;
var elevServiceUrl;

function selectEnv() {
  $("option:selected").each(function() {
    envServer = $(this).text();
    serviceUrl = document.getElementById(envServer).value;
    getEnvToken();
  });
}

function getEnvToken() {
  switch (envServer) {
  case "localhost":
    envToken = accessToken.local;
    elevServiceUrl = elevationServer.local;
    elevToken = elevAccessToken.local;
    break;
  case "development":
    envToken = accessToken.dev;
    elevServiceUrl = elevationServer.dev;
    elevToken = elevAccessToken.dev;
    break;
  case "production":
    envToken = accessToken.prod;
    elevServiceUrl = elevationServer.prod;
    elevToken = elevAccessToken.prod;
    break;
  }
}

//sets ISO date time to 12:15 of current date on initial transit run
function parseIsoDateTime(dtStr) {
  var dt = dtStr.split("T");
  return dtStr.replace(dt[1], "12:15:00");
}
var dateStr = parseIsoDateTime(isoDateTime.toString());

app.run(function($rootScope) {
  var hash_loc = hash_params ? hash_params : {
    'center' : {
      'lat' : 40.7486,
      'lng' : -73.9690
    },
    'zoom' : 13
  };
  $rootScope.geobase = {
    'zoom' : hash_loc.zoom,
    'lat' : hash_loc.center.lat,
    'lon' : hash_loc.center.lng
  }
  $(document).on('new-location', function(e) {
    $rootScope.geobase = {
      'zoom' : e.zoom,
      'lat' : e.lat,
      'lon' : e.lon
    };
  })
});

app.controller('RouteController', function($scope, $rootScope, $sce, $http) {
  var roadmap = L.tileLayer('http://otile3.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', {
    attribution : 'Map data © OpenStreetMap'
  }), cyclemap = L.tileLayer('http://b.tile.thunderforest.com/cycle/{z}/{x}/{y}.png', {
    attribution : 'Maps © Thunderforest, ;Data © OpenStreetMap contributors'
  }), transitmap = L.tileLayer(' http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png', {
    attribution : 'Map data © OpenStreetMap'
  });

  var baseMaps = {
    "RoadMap" : roadmap,
    "CycleMap" : cyclemap,
    "TransitMap" : transitmap
  };

  var map = L.map('map', {
    zoom : $rootScope.geobase.zoom,
    zoomControl : false,
    layers : [ roadmap ],
    center : [ $rootScope.geobase.lat, $rootScope.geobase.lon ]
  });

  L.control.layers(baseMaps, null).addTo(map);

  $scope.route_instructions = '';

  var Locations = [];
  var mode = 'car';

  var icon = L.icon({
    iconUrl : 'resource/dot.png',

    iconSize : [ 38, 35 ], // size of the icon
    shadowSize : [ 50, 64 ], // size of the shadow
    iconAnchor : [ 22, 34 ], // point of the icon which will correspond to marker's location
    shadowAnchor : [ 4, 62 ], // the same for the shadow
    popupAnchor : [ -3, -76 ]
  // point from which the popup should open relative to the iconAnchor
  });

  var mode_icons = {
    'car' : 'js/images/drive.png',
    'foot' : 'js/images/walk.png',
    'bicycle' : 'js/images/bike.png'
  };

  var getStartIcon = function(icon) {
    return L.icon({
      iconUrl : 'resource/startmarker@2x.png',
      iconSize : [ 44, 56 ], // size of the icon
      iconAnchor : [ 22, 50 ]
    });
  };

  var getEndIcon = function(icon) {
    return L.icon({
      iconUrl : 'resource/destmarker@2x.png',
      iconSize : [ 44, 56 ], // size of the icon
      iconAnchor : [ 22, 50 ]
    });
  };

  // Set up the hash
  var hash = new L.Hash(map);
  var markers = [];
  var remove_markers = function() {
    for (i = 0; i < markers.length; i++) {
      map.removeLayer(markers[i]);
    }
    markers = [];
  };

  // Number of locations
  var locations = 0;

  var reset = function() {
    $('svg').html('');
    $('.leaflet-routing-container').remove();
    $('.leaflet-marker-icon.leaflet-marker-draggable').remove();
    $scope.$emit('resetRouteInstruction');
    remove_markers();
    locations = 0;
  };

  $rootScope.$on('map.setView', function(ev, geo, zoom) {
    map.setView(geo, zoom || 8);
  });
  $rootScope.$on('map.dropMarker', function(ev, geo, m) {

    if (locations == 0) {
      var marker = new L.marker(geo, {
        icon : getStartIcon(m || 'car')
      });
      marker.bindPopup("Edit POI here");
    } else {
      var marker = new L.marker(geo, {
        icon : getEndIcon(m || 'car')
      });
      marker.bindPopup("Edit POI here");
    }
    map.addLayer(marker);
    markers.push(marker);
  });

  $scope.renderHtml = function(html_code) {
    return $sce.trustAsHtml(html_code);
  };

  $scope.$on('setRouteInstruction', function(ev, instructions) {
    $scope.$apply(function() {
      $scope.route_instructions = instructions;
    });
  });

  $scope.$on('resetRouteInstruction', function(ev) {
    $scope.$apply(function() {
      $scope.route_instructions = '';
    });
  });

  map.on('click', function(e) {
    var geo = {
      'lat' : e.latlng.lat,
      'lon' : e.latlng.lng
    };

    if (locations == 0) {
      Locations.push({
        lat : geo.lat,
        lon : geo.lon
      })
      $rootScope.$emit('map.dropMarker', [ geo.lat, geo.lon ], mode);
      locations++;
      return;
    } else if (locations > 1) {
      Locations = [];
      reset();

      Locations.push({
        lat : geo.lat,
        lon : geo.lon
      })
      $rootScope.$emit('map.dropMarker', [ geo.lat, geo.lon ], mode);
      locations++;
      return;
    }

    $scope.$on('setRouteInstruction', function(ev, instructions) {
      $scope.$apply(function() {
        $scope.route_instructions = instructions;
      });
    });

    $scope.$on('resetRouteInstruction', function(ev) {
      $scope.$apply(function() {
        $scope.route_instructions = '';
      });
    });

    var waypoints = [];
    Locations.forEach(function(gLoc) {
      waypoints.push(L.latLng(gLoc.lat, gLoc.lon));
    });

    waypoints.push(L.latLng(geo.lat, geo.lon));

    $rootScope.$emit('map.dropMarker', [ geo.lat, geo.lon ], mode);
    locations++;

    valhalla_mode = mode_mapping[mode];

    var rr = L.Routing.control(
        {
          waypoints : waypoints,
          geocoder : null,
          transitmode : valhalla_mode,
          routeWhileDragging : false,
          router : L.Routing.valhalla(envToken, 'auto'),
          summaryTemplate : '
{name}

{distance}, {time}
',

          createMarker : function(i, wp, n) {
            var iconV;
            if (i == 0) {
              iconV = L.icon({
                iconUrl : 'resource/dot.png',
                iconSize : [ 24, 24 ]
              });
            } else {
              iconV = L.icon({
                iconUrl : 'resource/dot.png',
                iconSize : [ 24, 24 ]
              })
            }
            var options = {
              draggable : true,
              icon : iconV
            }
            var dot = L.marker(wp.latLng, options);
            return dot.bindPopup("Edit POI here");
          },
          formatter : new L.Routing.Valhalla.Formatter(),
          pointMarkerStyle : {
            radius : 6,
            color : '#25A5FA',
            fillColor : '#5E6472',
            opacity : 1,
            fillOpacity : 1
          }
        }).addTo(map);

    var driveBtn = document.getElementById("drive_btn");
    var bikeBtn = document.getElementById("bike_btn");
    var walkBtn = document.getElementById("walk_btn");
    var multiBtn = document.getElementById("multi_btn");
    var elevationBtn = document.getElementById("elevation_btn");
    var routeresponse;

    driveBtn.addEventListener('click', function(e) {
      getEnvToken();
      rr.route({
        transitmode : 'auto'
      });
    });

    bikeBtn.addEventListener('click', function(e) {
      getEnvToken();
      var bikeoptions = setBikeOptions();
      rr.route({
        transitmode : 'bicycle',
        costing_options : bikeoptions
      });
    });

    walkBtn.addEventListener('click', function(e) {
      getEnvToken();
      rr.route({
        transitmode : 'pedestrian'
      });
    });

  multiBtn.addEventListener('click', function (e) {
	getEnvToken();
    rr.route({transitmode: 'multimodal', date_time: dateStr});
  });

  elevationBtn.addEventListener('click', function (e) {
    selectEnv();
    var elev = (typeof rr._routes[0] != "undefined") ? L.elevation(elevToken, rr._routes[0].rrshape) : 0;
    elev.resetChart();
    elev.profile(elev._rrshape);
    document.getElementById('graph').style.display="block";
  });

  function setBikeOptions () {
	var btype = document.getElementsByName("btype");
	var bicycle_type = "Road";
	  for (var i=0;i<btype.length;i++){
	    if ( btype[i].checked ) {
	    	bicycle_type = btype[i].value;
	    }
	  }
	var use_roads = document.getElementById("use_roads").value;
	var cycling_speed = document.getElementById("cycle_speed").value;
	var use_hills = document.getElementById("use_hills").value;

	bikeoptions = {"bicycle":{
	  bicycle_type: bicycle_type,
	  use_roads: use_roads,
	  cycling_speed: cycling_speed,
	  use_hills: use_hills
	}}
	return bikeoptions;
  };

    /*
    function openWin(id) {
      var divText = document.getElementById(id).innerHTML;
      myWindow=window.open('','','height: 100; width:200;');
      var doc = myWindow.document;
      doc.open();
      doc.write(divText);
      doc.close();
    }*/

    function datetimeUpdate(datetime) {
      var changeDt = datetime;
      var inputDate, splitDate, year, month, day, time, hour, minute;
      if (changeDt != null) {
        if (changeDt.length >= 11) {
          inputDate = changeDt.split(" ");
          splitDate = inputDate[0].split("-");
          day = splitDate[0];
          if (day < 10) {
            day = '0' + day;
          }
          month = GetMonthIndex(splitDate[1]) + 1;
          if (month < 10) {
            month = '0' + month;
          }
          year = splitDate[2];

          time = inputDate[1].split(":");
          hour = time[0];
          minute = time[1];

          dateStr = year + "-" + month + "-" + day + "T" + hour + ":" + minute;
        } else {
          dateStr = parseIsoDateTime(isoDateTime.toString());
        }
        multiBtn.click();
      }
    }
    ;

    $(document).on('mode-alert', function(e, m) {
      mode = m;
      reset();
      Locations = [];
    });

    $(document).on('route:time_distance', function(e, td) {
      var instructions = $('.leaflet-routing-container.leaflet-control').html();
      $scope.$emit('setRouteInstruction', instructions);
    });

    $("#datepicker").on("click", function() {
      datetimeUpdate(this.value);
    });
  });

  $("#showbtn").on("click", function() {
    document.getElementById('options').style.display = "block";
  });

  $("#hidebtn").on("click", function() {
    document.getElementById('options').style.display = "none";
  });

  $("#hidechart").on("click", function() {
    document.getElementById('graph').style.display = "none";
  });
})
<|endoftext|>


<commit_before>frappe.ui.form.FormTour = class FormTour {
	constructor({ frm }) {
		this.frm = frm;
		this.driver_steps = [];

		this.init_driver();
	}

	init_driver() {
		this.driver = new frappe.Driver({
			className: 'frappe-driver',
			allowClose: false,
			padding: 10,
			overlayClickNext: true,
			keyboardControl: true,
			nextBtnText: 'Next',
			prevBtnText: 'Previous',
			opacity: 0.25,
			onHighlighted: (step) => {
				// if last step is to save, then attach a listener to save button
				if (step.options.is_save_step) {
					$(step.options.element).one('click', () => this.driver.reset());
				}

				// focus on input
				const $input = $(step.node).find('input').get(0);
				if ($input)
					frappe.utils.sleep(200).then(() => $input.focus());
			}
		});

		frappe.router.on('change', () => this.driver.reset());
		this.frm.layout.sections.forEach(section => section.collapse(false));
	}

	async init({ tour_name, on_finish }) {
		if (tour_name) {
			this.tour = await frappe.db.get_doc('Form Tour', tour_name);
		} else {
			this.tour = { steps: frappe.tour[this.frm.doctype] };
		}

		if (on_finish) this.on_finish = on_finish;

		this.build_steps();
		this.update_driver_steps();
	}

	build_steps() {
		this.driver_steps = [];
		this.tour.steps.forEach((step) => {
			const on_next = () => {
				if (!this.is_next_condition_satisfied(step)) {
					this.driver.preventMove();
				}

				if (!this.driver.hasNextStep()) {
					this.on_finish && this.on_finish();
				}
			};

			const driver_step = this.get_step(step, on_next);
			this.driver_steps.push(driver_step);

			if (step.fieldtype == 'Table') this.handle_table_step(step);
			if (step.is_table_field) this.handle_child_table_step(step);
		});

		if (this.tour.save_on_complete) {
			this.add_step_to_save();
		}
	}

	is_next_condition_satisfied(step) {
		const form = step.is_table_field ? this.frm.cur_grid.grid_form : this.frm;
		return form.layout.evaluate_depends_on_value(step.next_step_condition || true);
	}

	get_step(step_info, on_next) {
		const { name, fieldname, title, description, position, is_table_field } = step_info;
		const field = this.frm.get_field(fieldname);
		let element = field ? field.wrapper : `.frappe-control[data-fieldname='${fieldname}']`;

		if (is_table_field) {
			element = `.grid-row-open .frappe-control[data-fieldname='${fieldname}']`;
		}

		return {
			element,
			name,
			popover: { title, description, position: frappe.router.slug(position) },
			onNext: on_next
		};
	}

	update_driver_steps(steps = []) {
		if (steps.length == 0) {
			steps = this.driver_steps;
		}
		this.driver.defineSteps(steps);
	}

	start(idx = 0) {
		if (this.driver_steps.length == 0) {
			return;
		}
		this.driver.start(idx);
	}

	get_next_step() {
		// returns the next step only if driver is active
		if (this.driver.isActivated & this.driver.hasNextStep()) {
			const current_step = this.driver.currentStep;
			return this.driver.steps[current_step + 1];
		}
		return;
	}

	handle_table_step(step_info) {
		const is_last_step = step_info.idx == this.tour.steps.length;

		if (!is_last_step) {
			// if next step field is inside currently highlighted table field
			// then check if there is a row -> if not, then prompt to add row
			// then edit the first row and hightlight next step

			const curr_step = step_info;
			const next_step = this.tour.steps[curr_step.idx];
			const is_next_field_in_curr_table = next_step.parent_field == curr_step.field;

			if (!is_next_field_in_curr_table) return;

			const rows = this.frm.doc[curr_step.fieldname];
			const table_has_rows = rows && rows.length > 0;
			if (table_has_rows) {
				// table already has rows
				// then just edit the first one on next step
				const curr_driver_step = this.driver_steps.find(s => s.name == curr_step.name);
				curr_driver_step.onNext = () => {
					if (this.is_next_condition_satisfied(curr_step)) {
						this.expand_row_and_proceed(curr_step, curr_step.idx);
					} else {
						this.driver.preventMove();
					}
				};
				this.update_driver_steps();

			} else {
				this.add_new_row_step(curr_step);
			}
		}
	}

	add_new_row_step(step) {
		const $add_row = `.frappe-control[data-fieldname='${step.fieldname}'] .grid-add-row`;
		const add_row_step = {
			element: $add_row,
			popover: { title: __("Add a Row"), description: "" },
			onNext: () => {
				if (!cur_frm.cur_grid) {
					this.driver.preventMove();
				}
			}
		};
		this.driver_steps.push(add_row_step);

		// setup a listener on add row button
		// so, once the row is added, move to next step automatically
		$($add_row).one('click', () => {
			this.expand_row_and_proceed(step, step.idx + 1); // +1 since add row step is added
		});
	}

	expand_row_and_proceed(step, start_from) {
		this.open_first_row_of(step.fieldname);
		this.update_driver_steps(); // need to define again, since driver.js only considers steps which are inside DOM
		frappe.utils.sleep(300).then(() => this.driver.start(start_from));
	}

	open_first_row_of(fieldname) {
		this.frm.fields_dict[fieldname].grid.grid_rows[0].toggle_view();

		// setup a listener on close row button
		// so, once the row is closed, move to next step automatically
		const $close_row = '.grid-row-open .grid-collapse-row';
		$($close_row).one('click', () => {
			const next_step = this.get_next_step();
			const next_element = next_step.options.is_save_step ? null : next_step.node;

			frappe.utils.scroll_to(next_element, true, 150, null, () => {
				this.driver.moveNext();
				frappe.flags.disable_auto_scroll = false;
			});
			frappe.flags.disable_auto_scroll = true;
		});
	}

	handle_child_table_step(step_info) {
		const is_last_step = step_info.idx == this.tour.steps.length;

		if (!is_last_step) {
			const curr_step = step_info;
			const next_step = this.tour.steps[curr_step.idx];
			const field = this.frm.get_field(next_step.fieldname);

			if (!field) return;

			// next step highlights parent field
			// so, add a step to prompt user to collapse grid form
			this.add_collapse_row_step();

		} else if (this.tour.save_on_complete) {
			// if last step & save on complete is checked
			// add a step to prompt user to collapse grid form
			// to be able to save as a last step
			this.add_collapse_row_step();
		}
	}

	add_collapse_row_step() {
		const $close_row = '.grid-row-open .grid-collapse-row';
		const close_row_step = {
			element: $close_row,
			popover: { title: __("Collapse"), description: "", position: "left" },
			onNext: () => {
				if (cur_frm.cur_grid) {
					this.driver.preventMove();
				}
			}
		};
		this.driver_steps.push(close_row_step);
	}

	add_step_to_save() {
		const page_id = `#page-${this.frm.doctype}`;
		const $save_btn = `${page_id} .standard-actions .primary-action`;
		const save_step = {
			element: $save_btn,
			is_save_step: true,
			allowClose: false,
			overlayClickNext: false,
			popover: {
				title: __("Save"),
				description: "",
				position: "left",
				doneBtnText: __("Save")
			}
		};
		this.driver_steps.push(save_step);
		frappe.ui.form.on(this.frm.doctype, 'after_save', () => this.on_finish && this.on_finish());
	}
};<commit_msg>feat: get standard form tour for onboarding step<commit_after>frappe.ui.form.FormTour = class FormTour {
	constructor({ frm }) {
		this.frm = frm;
		this.driver_steps = [];

		this.init_driver();
	}

	init_driver() {
		this.driver = new frappe.Driver({
			className: 'frappe-driver',
			allowClose: false,
			padding: 10,
			overlayClickNext: true,
			keyboardControl: true,
			nextBtnText: 'Next',
			prevBtnText: 'Previous',
			opacity: 0.25,
			onHighlighted: (step) => {
				// if last step is to save, then attach a listener to save button
				if (step.options.is_save_step) {
					$(step.options.element).one('click', () => this.driver.reset());
				}

				// focus on input
				const $input = $(step.node).find('input').get(0);
				if ($input)
					frappe.utils.sleep(200).then(() => $input.focus());
			}
		});

		frappe.router.on('change', () => this.driver.reset());
		this.frm.layout.sections.forEach(section => section.collapse(false));
	}

	async init({ tour_name, on_finish }) {
		if (tour_name) {
			this.tour = await frappe.db.get_doc('Form Tour', tour_name);
		} else {
			const doctype_tour_exists = await frappe.db.exists('Form Tour', this.frm.doctype);
			if (doctype_tour_exists) {
				this.tour = await frappe.db.get_doc('Form Tour', this.frm.doctype)
			} else {
				this.tour = { steps: frappe.tour[this.frm.doctype] };
			}
		}

		if (on_finish) this.on_finish = on_finish;

		this.build_steps();
		this.update_driver_steps();
	}

	build_steps() {
		this.driver_steps = [];
		this.tour.steps.forEach((step) => {
			const on_next = () => {
				if (!this.is_next_condition_satisfied(step)) {
					this.driver.preventMove();
				}

				if (!this.driver.hasNextStep()) {
					this.on_finish && this.on_finish();
				}
			};

			const driver_step = this.get_step(step, on_next);
			this.driver_steps.push(driver_step);

			if (step.fieldtype == 'Table') this.handle_table_step(step);
			if (step.is_table_field) this.handle_child_table_step(step);
		});

		if (this.tour.save_on_complete) {
			this.add_step_to_save();
		}
	}

	is_next_condition_satisfied(step) {
		const form = step.is_table_field ? this.frm.cur_grid.grid_form : this.frm;
		return form.layout.evaluate_depends_on_value(step.next_step_condition || true);
	}

	get_step(step_info, on_next) {
		const { name, fieldname, title, description, position, is_table_field } = step_info;
		const field = this.frm.get_field(fieldname);
		let element = field ? field.wrapper : `.frappe-control[data-fieldname='${fieldname}']`;

		if (is_table_field) {
			element = `.grid-row-open .frappe-control[data-fieldname='${fieldname}']`;
		}

		return {
			element,
			name,
			popover: { title, description, position: frappe.router.slug(position) },
			onNext: on_next
		};
	}

	update_driver_steps(steps = []) {
		if (steps.length == 0) {
			steps = this.driver_steps;
		}
		this.driver.defineSteps(steps);
	}

	start(idx = 0) {
		if (this.driver_steps.length == 0) {
			return;
		}
		this.driver.start(idx);
	}

	get_next_step() {
		// returns the next step only if driver is active
		if (this.driver.isActivated & this.driver.hasNextStep()) {
			const current_step = this.driver.currentStep;
			return this.driver.steps[current_step + 1];
		}
		return;
	}

	handle_table_step(step_info) {
		const is_last_step = step_info.idx == this.tour.steps.length;

		if (!is_last_step) {
			// if next step field is inside currently highlighted table field
			// then check if there is a row -> if not, then prompt to add row
			// then edit the first row and hightlight next step

			const curr_step = step_info;
			const next_step = this.tour.steps[curr_step.idx];
			const is_next_field_in_curr_table = next_step.parent_field == curr_step.field;

			if (!is_next_field_in_curr_table) return;

			const rows = this.frm.doc[curr_step.fieldname];
			const table_has_rows = rows && rows.length > 0;
			if (table_has_rows) {
				// table already has rows
				// then just edit the first one on next step
				const curr_driver_step = this.driver_steps.find(s => s.name == curr_step.name);
				curr_driver_step.onNext = () => {
					if (this.is_next_condition_satisfied(curr_step)) {
						this.expand_row_and_proceed(curr_step, curr_step.idx);
					} else {
						this.driver.preventMove();
					}
				};
				this.update_driver_steps();

			} else {
				this.add_new_row_step(curr_step);
			}
		}
	}

	add_new_row_step(step) {
		const $add_row = `.frappe-control[data-fieldname='${step.fieldname}'] .grid-add-row`;
		const add_row_step = {
			element: $add_row,
			popover: { title: __("Add a Row"), description: "" },
			onNext: () => {
				if (!cur_frm.cur_grid) {
					this.driver.preventMove();
				}
			}
		};
		this.driver_steps.push(add_row_step);

		// setup a listener on add row button
		// so, once the row is added, move to next step automatically
		$($add_row).one('click', () => {
			this.expand_row_and_proceed(step, step.idx + 1); // +1 since add row step is added
		});
	}

	expand_row_and_proceed(step, start_from) {
		this.open_first_row_of(step.fieldname);
		this.update_driver_steps(); // need to define again, since driver.js only considers steps which are inside DOM
		frappe.utils.sleep(300).then(() => this.driver.start(start_from));
	}

	open_first_row_of(fieldname) {
		this.frm.fields_dict[fieldname].grid.grid_rows[0].toggle_view();

		// setup a listener on close row button
		// so, once the row is closed, move to next step automatically
		const $close_row = '.grid-row-open .grid-collapse-row';
		$($close_row).one('click', () => {
			const next_step = this.get_next_step();
			const next_element = next_step.options.is_save_step ? null : next_step.node;

			frappe.utils.scroll_to(next_element, true, 150, null, () => {
				this.driver.moveNext();
				frappe.flags.disable_auto_scroll = false;
			});
			frappe.flags.disable_auto_scroll = true;
		});
	}

	handle_child_table_step(step_info) {
		const is_last_step = step_info.idx == this.tour.steps.length;

		if (!is_last_step) {
			const curr_step = step_info;
			const next_step = this.tour.steps[curr_step.idx];
			const field = this.frm.get_field(next_step.fieldname);

			if (!field) return;

			// next step highlights parent field
			// so, add a step to prompt user to collapse grid form
			this.add_collapse_row_step();

		} else if (this.tour.save_on_complete) {
			// if last step & save on complete is checked
			// add a step to prompt user to collapse grid form
			// to be able to save as a last step
			this.add_collapse_row_step();
		}
	}

	add_collapse_row_step() {
		const $close_row = '.grid-row-open .grid-collapse-row';
		const close_row_step = {
			element: $close_row,
			popover: { title: __("Collapse"), description: "", position: "left" },
			onNext: () => {
				if (cur_frm.cur_grid) {
					this.driver.preventMove();
				}
			}
		};
		this.driver_steps.push(close_row_step);
	}

	add_step_to_save() {
		const page_id = `[id="page-${this.frm.doctype}"]`;
		const $save_btn = `${page_id} .standard-actions .primary-action`;
		const save_step = {
			element: $save_btn,
			is_save_step: true,
			allowClose: false,
			overlayClickNext: false,
			popover: {
				title: __("Save"),
				description: "",
				position: "left",
				doneBtnText: __("Save")
			}
		};
		this.driver_steps.push(save_step);
		frappe.ui.form.on(this.frm.doctype, 'after_save', () => this.on_finish && this.on_finish());
	}
};<|endoftext|>