<commit_before>/*
resources:
https://developers.google.com/identity/protocols/OAuth2UserAgent#validatetoken
https://developers.google.com/oauthplayground/

TODO: use switch to 2.4.
*/

import EnhancedError from '../../lib/EnhancedError';
import fs from 'fs';
import path from 'path';
import winston from 'winston';
import request from 'request';
import handlebars from 'handlebars';
import route from 'koa-route';
import queryString from 'querystring';
import { bus, config } from '../../core-server';
import encodeURITemplate from '../../lib/encodeURITemplate';
import * as Constants from '../../lib/Constants';

const redirectURL = `${config.server.protocol}//${config.server.host}:${config.server.port}${config.google.redirect_path}`;

const SCRIPT_REDIRECT = `\
location.href = 'https://accounts.google.com/o/oauth2/auth?\
scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile&\
client_id=${config.google.client_id}&\
redirect_uri=${redirectURL}&\
response_type=code&access_type=offline'`;

const SCRIPT_MESSAGE = `
	var code = (location.search.substring(1).split("&").filter(function(seg) {
		return seg.indexOf("code=") === 0;
	})[0] || "").substr(5);

	window.opener.postMessage({
		type: "auth",
		provider: "google",
		code: code
	}, "*");

	window.close();
`;

// TODO: most of the code is copy paste from facebook module. see if u can avoid that when u get time.
function getTokenFromCode(code) {
	return new Promise((resolve, reject) => {
		request.post({
			uri: 'https://accounts.google.com/o/oauth2/token',
			headers: {
				'content-type': 'application/x-www-form-urlencoded',
			},
			body: queryString.stringify({
				code,
				redirect_uri: redirectURL,
				client_id: config.google.client_id,
				client_secret: config.google.client_secret,
				grant_type: 'authorization_code',
			}),
		}, (err, res, t) => {

			if (err) {
				const error = new EnhancedError(err.message, err.message);

				winston.error(err);
				reject(error);
				return;
			}
			console.log("Google response: ", t);
			try {
				const tokenBody = JSON.parse(t), token = tokenBody.id_token;
				if (!token) throw new Error('INVALID_GOOGLE_CODE');
				resolve(token);
			} catch (e) {
				reject(e);
			}
		});
	});
}

function verifyToken(token, appId) {
	return new Promise((resolve, reject) => {
		request(encodeURITemplate `https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token}`,
		(err, res, body) => {
			winston.error(err, body);
			if (err || !res) {
				reject(err);
				return;
			}

			const response = JSON.parse(body);

			if (response.error) {
				reject(new Error(response.error.message));
				return;
			}

			if (response.aud === appId) resolve(token);
			else reject(new EnhancedError(Constants.ERRORS.AUDIENCE_MISMATCH_GOOGLE, 'AUDIENCE_MISMATCH_GOOGLE'));
		});
	});
}

function getDataFromToken(token) {
	const signin = {};

	return new Promise((resolve, reject) => {
		request(`https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token}`, (err, res, body) => {
			try {
				if (err) throw (err);
				const user = JSON.parse(body);
				console.log('Response: ', res);
				if (user.error) {
					throw (new EnhancedError(user.error || Constants.ERRORS.ERR_FACEBOOK_SIGNIN_FAILED, user.error || 'ERR_FACEBOOK_SIGNIN_FAILED'));
				} else if (!user.email) {
					throw (new Error(user.error || 'ERR_GOOGLE_SIGNIN_NO_EMAIL'));
				}
	//			response.signin.pictures.push('https://gravatar.com/avatar/' + crypto.createHash('md5').update(user.email).digest('hex') + '/?d=retro');

				(signin.identities = []).push('mailto:' + user.email);
				signin.params = {
					google: {
						idToken: token,
						verified: true,
						name: user.name,
						picture: user.picture,
					},
				};
				resolve(signin);
			} catch (e) {
				reject(e);
			}
		});
	});
}

function googleAuth(changes, n) {
	winston.debug('setstate: facebook module');

	function next(e) {
		if (e) {
			(changes.response = changes.response || {}).state = changes.auth;
			changes.response.state.google.error = e;
			console.log('Err: ', e);
			n(changes);
		} else {
			n();
		}
	}

	if (!changes.auth || !changes.auth.google) {
		return next();
	}

	/* TODO: how do we handle auth from already logged in user?*/
	const key = changes.auth.google.code || changes.auth.google.idToken;

	if (!key) {
		return next(new EnhancedError(Constants.ERRORS.INVALID_GOOGLE_KEY, 'INVALID_GOOGLE_KEY'));
	}

	winston.debug('Google Login Request', changes.auth.google);
	const promise = ((changes.auth.google.code) ? getTokenFromCode(key) : verifyToken(key, config.google.client_id));

	promise.then((token) => {
		winston.debug('T->D', token);
		if (!token) {
			return next(new EnhancedError(Constants.ERRORS.INVALID_GOOGLE_TOKEN, 'INVALID_GOOGLE_TOKEN'));
		}

		getDataFromToken(token).then((response) => {
			if (!response) {
				return next(new EnhancedError(Constants.ERRORS.GOOGLE_RESPONSE_PARSE_ERROR, 'GOOGLE_RESPONSE_PARSE_ERROR'));
			}
			winston.debug('response', changes);
			changes.auth.signin = response;
			return next();
		}).catch(error => next(error));

		return null;
	}).catch(error => next(error));
	return null;
}

bus.on('change', googleAuth, Constants.APP_PRIORITIES.AUTHENTICATION_GOOGLE);

const scriptTemplate = handlebars.compile(fs.readFileSync(path.join(__dirname, '../../../templates/script.hbs'), 'utf8').toString());

bus.on('http/init', app => {
	app.use(route.get(config.google.login_url, function *() {
		this.body = scriptTemplate({
			title: 'Logging in with Google',
			script: SCRIPT_REDIRECT,
		});
	}));

	app.use(route.get(config.google.redirect_path, function *() {
		this.body = scriptTemplate({
			title: 'Logging in with Google',
			script: SCRIPT_MESSAGE,
		});
	}));
});

winston.info('Google module ready.');
<commit_msg>Removed log statements<commit_after>/*
resources:
https://developers.google.com/identity/protocols/OAuth2UserAgent#validatetoken
https://developers.google.com/oauthplayground/

TODO: use switch to 2.4.
*/

import EnhancedError from '../../lib/EnhancedError';
import fs from 'fs';
import path from 'path';
import winston from 'winston';
import request from 'request';
import handlebars from 'handlebars';
import route from 'koa-route';
import queryString from 'querystring';
import { bus, config } from '../../core-server';
import encodeURITemplate from '../../lib/encodeURITemplate';
import * as Constants from '../../lib/Constants';

const redirectURL = `${config.server.protocol}//${config.server.host}:${config.server.port}${config.google.redirect_path}`;

const SCRIPT_REDIRECT = `\
location.href = 'https://accounts.google.com/o/oauth2/auth?\
scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile&\
client_id=${config.google.client_id}&\
redirect_uri=${redirectURL}&\
response_type=code&access_type=offline'`;

const SCRIPT_MESSAGE = `
	var code = (location.search.substring(1).split("&").filter(function(seg) {
		return seg.indexOf("code=") === 0;
	})[0] || "").substr(5);

	window.opener.postMessage({
		type: "auth",
		provider: "google",
		code: code
	}, "*");

	window.close();
`;

// TODO: most of the code is copy paste from facebook module. see if u can avoid that when u get time.
function getTokenFromCode(code) {
	return new Promise((resolve, reject) => {
		request.post({
			uri: 'https://accounts.google.com/o/oauth2/token',
			headers: {
				'content-type': 'application/x-www-form-urlencoded',
			},
			body: queryString.stringify({
				code,
				redirect_uri: redirectURL,
				client_id: config.google.client_id,
				client_secret: config.google.client_secret,
				grant_type: 'authorization_code',
			}),
		}, (err, res, t) => {

			if (err) {
				const error = new EnhancedError(err.message, err.message);

				winston.error(err);
				reject(error);
				return;
			}

			try {
				const tokenBody = JSON.parse(t), token = tokenBody.id_token;
				if (!token) throw new Error('INVALID_GOOGLE_CODE');
				resolve(token);
			} catch (e) {
				reject(e);
			}
		});
	});
}

function verifyToken(token, appId) {
	return new Promise((resolve, reject) => {
		request(encodeURITemplate `https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token}`,
		(err, res, body) => {
			winston.error(err, body);
			if (err || !res) {
				reject(err);
				return;
			}

			const response = JSON.parse(body);

			if (response.error) {
				reject(new Error(response.error.message));
				return;
			}

			if (response.aud === appId) resolve(token);
			else reject(new EnhancedError(Constants.ERRORS.AUDIENCE_MISMATCH_GOOGLE, 'AUDIENCE_MISMATCH_GOOGLE'));
		});
	});
}

function getDataFromToken(token) {
	const signin = {};

	return new Promise((resolve, reject) => {
		request(`https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token}`, (err, res, body) => {
			try {
				if (err) throw (err);
				const user = JSON.parse(body);

				if (user.error) {
					throw (new EnhancedError(user.error || Constants.ERRORS.ERR_FACEBOOK_SIGNIN_FAILED, user.error || 'ERR_FACEBOOK_SIGNIN_FAILED'));
				} else if (!user.email) {
					throw (new Error(user.error || 'ERR_GOOGLE_SIGNIN_NO_EMAIL'));
				}
	//			response.signin.pictures.push('https://gravatar.com/avatar/' + crypto.createHash('md5').update(user.email).digest('hex') + '/?d=retro');

				(signin.identities = []).push('mailto:' + user.email);
				signin.params = {
					google: {
						idToken: token,
						verified: true,
						name: user.name,
						picture: user.picture,
					},
				};
				resolve(signin);
			} catch (e) {
				reject(e);
			}
		});
	});
}

function googleAuth(changes, n) {
	winston.debug('setstate: facebook module');

	function next(e) {
		if (e) {
			(changes.response = changes.response || {}).state = changes.auth;
			changes.response.state.google.error = e;
			n(changes);
		} else {
			n();
		}
	}

	if (!changes.auth || !changes.auth.google) {
		return next();
	}

	/* TODO: how do we handle auth from already logged in user?*/
	const key = changes.auth.google.code || changes.auth.google.idToken;

	if (!key) {
		return next(new EnhancedError(Constants.ERRORS.INVALID_GOOGLE_KEY, 'INVALID_GOOGLE_KEY'));
	}

	winston.debug('Google Login Request', changes.auth.google);
	const promise = ((changes.auth.google.code) ? getTokenFromCode(key) : verifyToken(key, config.google.client_id));

	promise.then((token) => {
		winston.debug('T->D', token);
		if (!token) {
			return next(new EnhancedError(Constants.ERRORS.INVALID_GOOGLE_TOKEN, 'INVALID_GOOGLE_TOKEN'));
		}

		getDataFromToken(token).then((response) => {
			if (!response) {
				return next(new EnhancedError(Constants.ERRORS.GOOGLE_RESPONSE_PARSE_ERROR, 'GOOGLE_RESPONSE_PARSE_ERROR'));
			}
			winston.debug('response', changes);
			changes.auth.signin = response;
			return next();
		}).catch(error => next(error));

		return null;
	}).catch(error => next(error));
	return null;
}

bus.on('change', googleAuth, Constants.APP_PRIORITIES.AUTHENTICATION_GOOGLE);

const scriptTemplate = handlebars.compile(fs.readFileSync(path.join(__dirname, '../../../templates/script.hbs'), 'utf8').toString());

bus.on('http/init', app => {
	app.use(route.get(config.google.login_url, function *() {
		this.body = scriptTemplate({
			title: 'Logging in with Google',
			script: SCRIPT_REDIRECT,
		});
	}));

	app.use(route.get(config.google.redirect_path, function *() {
		this.body = scriptTemplate({
			title: 'Logging in with Google',
			script: SCRIPT_MESSAGE,
		});
	}));
});

winston.info('Google module ready.');
<|endoftext|>


<commit_before>'use strict';

let debug = require("debug")("skill");

module.exports = class SkillCompileMessage {

    constructor() {
        this.required_parameter = {
            line_text: { // Will be text in facebook.
                message_to_confirm: {
                    type: "text",
                    text: "ご注文のピザは？"
                }
            },
            line_template_button_postback: { // Will be quick reply in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文のピザをお選びください。",
                    template: {
                        type: "buttons",
                        text: "ご注文のピザをお選びください。",
                        actions: [
                            {type:"postback",label:"マルゲリータ",data:"マルゲリータ"},
                            {type:"postback",label:"マリナーラ",data:"マリナーラ"},
                            {type:"postback",label:"カプリチョーザ",data:"カプリチョーザ"},
                            {type:"postback",label:"クワトロフォルマッジ",data:"クワトロフォルマッジ"}
                        ]
                    }
                }
            },
            line_template_button_message: { // Will be quick reply in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文のピザをお選びください。",
                    template: {
                        type: "buttons",
                        text: "ご注文のピザをお選びください。",
                        actions: [
                            {type:"message",label:"マルゲリータ",text:"マルゲリータ"},
                            {type:"message",label:"マリナーラ",text:"マリナーラ"},
                            {type:"message",label:"カプリチョーザ",text:"カプリチョーザ"},
                            {type:"message",label:"クワトロフォルマッジ",text:"クワトロフォルマッジ"}
                        ]
                    }
                }
            },
            line_template_button_uri: { // Will be template button in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文のピザをお選びください。",
                    template: {
                        type: "buttons",
                        text: "ご注文のピザをお選びください。",
                        actions: [
                            {type:"postback",label:"マルゲリータ",data:"マルゲリータ"},
                            {type:"postback",label:"マリナーラ",data:"マリナーラ"},
                            {type:"uri", label: "すべてのメニュー", uri:"https://www.dominos.jp/order/pizza/search/"}
                        ]
                    }
                }
            },
            line_template_button_uri_more_than_3: { // Will catch exception in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文のピザをお選びください。",
                    template: {
                        type: "buttons",
                        text: "ご注文のピザをお選びください。",
                        actions: [
                            {type:"postback",label:"マルゲリータ",data:"マルゲリータ"},
                            {type:"postback",label:"マリナーラ",data:"マリナーラ"},
                            {type:"postback",label:"カプリチョーザ",data:"カプリチョーザ"},
                            {type:"uri", label: "すべてのメニュー", uri:"https://www.dominos.jp/order/pizza/search/"}
                        ]
                    }
                }
            },
            line_template_confirm: { // Will be quick repy in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文は以上ですか？",
                    template: {
                        type: "buttons",
                        text: "ご注文は以上ですか？",
                        actions: [
                            {type:"message",label:"はい",text:"はい"},
                            {type:"message",label:"いいえ",text:"いいえ"}
                        ]
                    }
                }
            },
            line_template_carousel: { // Will be template generic
                message_to_confirm: {
                    type: "template",
                    altText: "Carousel Template",
                    template: {
                        type: "carousel",
                        columns: [{
                            thumbnailImageUrl: "https://www.dominos.jp/common/img/itemimgsx/90.jpg?_=12016",
                            text: "マルゲリータ",
                            actions: [
                                {type:"postback", label:"注文する", data:"マルゲリータ"},
                                {type:"uri", label:"詳細", data:"https://www.dominos.jp/order/pizza/detail/99999/19001/90"}
                            ]
                        },{
                            thumbnailImageUrl: "https://www.dominos.jp/common/img/itemimgsx/216.jpg?_=12016",
                            text: "ジェノベーゼ",
                            actions: [
                                {type:"postback", label:"注文する", data:"ジェノベーゼ"},
                                {type:"uri", label:"詳細", data:"https://www.dominos.jp/order/pizza/detail/99999/19001/216"}
                            ]
                        }]
                    }
                }
            },
            facebook_text: { // Will be text in line.
                message_to_confirm: {
                    text: "ご注文のピザは？"
                }
            },
            facebook_quick_reply: { // Will be template button message in line.
                message_to_confirm: {
                    text: "ご注文のピザをお選びください。",
                    quick_replies: [
                        {content_type:"text",title:"マルゲリータ",payload:"マルゲリータ"},
                        {content_type:"text",title:"マリナーラ",payload:"マリナーラ"},
                    ]
                }
            },
            facebook_quick_reply_more_than_4: { // Will be text in line.
                message_to_confirm: {
                    text: "ご注文のピザをお選びください。",
                    quick_replies: [
                        {content_type:"text",title:"マルゲリータ",payload:"マルゲリータ"},
                        {content_type:"text",title:"マリナーラ",payload:"マリナーラ"},
                        {content_type:"text",title:"カプリチョーザ",payload:"カプリチョーザ"},
                        {content_type:"text",title:"アラビアータ",payload:"アラビアータ"},
                        {content_type:"text",title:"クアトロフォルマッジ",payload:"クアトロフォルマッジ"},
                    ]
                }
            },
            facebook_template_button_postback: { // Will be template button postback in line.
                message_to_confirm: {
                    attachment: {
                        type: "template",
                        payload: {
                            template_type: "button",
                            text: "ご注文のピザをお選びください。",
                            buttons: [
                                {type: "postback", title: "マルゲリータ", payload: "マルゲリータ"},
                                {type: "postback", title: "マリナーラ", payload: "マリナーラ"}
                            ]
                        }
                    }
                }
            },
            facebook_template_button_web_url: { // Will be template button postback and uri in line.
                message_to_confirm: {
                    attachment: {
                        type: "template",
                        payload: {
                            template_type: "button",
                            text: "ご注文のピザをお選びください。",
                            buttons: [
                                {type: "postback", title: "マルゲリータ", payload: "マルゲリータ"},
                                {type: "postback", title: "マリナーラ", payload: "マリナーラ"},
                                {type: "web_url", title: "すべてのメニュー", url: "https://www.dominos.jp/order/pizza/search/"}
                            ]
                        }
                    }
                }
            },
            facebook_template_generic: { // Will be template carousel in line.
                message_to_confirm: {
                    attachment: {
                        type: "template",
                        payload: {
                            template_type: "generic",
                            elements: [{
                                title: "マルゲリータ",
                                image_url: "https://www.dominos.jp/common/img/itemimgsx/90.jpg?_=12016",
                                buttons: [
                                    {type: "postback", title: "注文する", payload: "マルゲリータ"},
                                    {type: "web_url", title: "詳細", url: "https://www.dominos.jp/order/pizza/detail/99999/19001/90"}
                                ]
                            },{
                                title: "ジェノベーゼ",
                                image_url: "https://www.dominos.jp/common/img/itemimgsx/216.jpg?_=12016",
                                buttons: [
                                    {type: "postback", title: "注文する", payload: "ジェノベーゼ"},
                                    {type: "web_url", title: "詳細", url: "https://www.dominos.jp/order/pizza/detail/99999/19001/216"}
                                ]
                            }]
                        }
                    }
                }
            }/*,
            line_image: {
                message_to_confirm: {
                    type: "image",
                    originalContentUrl: "https://www.dropbox.com/sh/lbmx3s1yg392mvh/AAAwCzdEjO_I5OK9nrbDurdra?dl=1"
                }
            },
            line_video: {
                message_to_confirm: {
                    type: "video",
                    originalContentUrl: "https://www.dropbox.com/sh/lbmx3s1yg392mvh/AAAwCzdEjO_I5OK9nrbDurdra?dl=1"
                }
            },
            line_audio: {
                message_to_confirm: {
                    type: "sticker",
                    originalContentUrl: "https://www.dropbox.com/sh/lbmx3s1yg392mvh/AAAwCzdEjO_I5OK9nrbDurdra?dl=1"
                }
            },
            line_sticker: {
                message_to_confirm: {
                    type: "sticker",

                }
            },
            line_location: {
                message_to_confirm: {
                    type: "location",

                }
            },
            line_imagemap: {
                message_to_confirm: {
                    type: "imagemap",
                    originalContentUrl: "https://www.dropbox.com/sh/lbmx3s1yg392mvh/AAAwCzdEjO_I5OK9nrbDurdra?dl=1"
                }
            }
            */
        };

        this.clear_context_on_finish = true;
    }

    // パラメーターが全部揃ったら実行する処理を記述します。
    finish(bot, bot_event, context){
        let messages = [bot.create_text_message("完了")];
        return bot.reply(bot_event, messages);
    }
};
<commit_msg>adding test<commit_after>'use strict';

let debug = require("debug")("skill");

module.exports = class SkillCompileMessage {

    constructor() {
        this.required_parameter = {
            line_text: { // Will be text in facebook.
                message_to_confirm: {
                    type: "text",
                    text: "ご注文のピザは？"
                }
            },
            line_template_button_postback: { // Will be quick reply in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文のピザをお選びください。",
                    template: {
                        type: "buttons",
                        text: "ご注文のピザをお選びください。",
                        actions: [
                            {type:"postback",label:"マルゲリータ",data:"マルゲリータ"},
                            {type:"postback",label:"マリナーラ",data:"マリナーラ"},
                            {type:"postback",label:"カプリチョーザ",data:"カプリチョーザ"},
                            {type:"postback",label:"クワトロフォルマッジ",data:"クワトロフォルマッジ"}
                        ]
                    }
                }
            },
            line_template_button_message: { // Will be quick reply in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文のピザをお選びください。",
                    template: {
                        type: "buttons",
                        text: "ご注文のピザをお選びください。",
                        actions: [
                            {type:"message",label:"マルゲリータ",text:"マルゲリータ"},
                            {type:"message",label:"マリナーラ",text:"マリナーラ"},
                            {type:"message",label:"カプリチョーザ",text:"カプリチョーザ"},
                            {type:"message",label:"クワトロフォルマッジ",text:"クワトロフォルマッジ"}
                        ]
                    }
                }
            },
            line_template_button_uri: { // Will be template button in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文のピザをお選びください。",
                    template: {
                        type: "buttons",
                        text: "ご注文のピザをお選びください。",
                        actions: [
                            {type:"postback",label:"マルゲリータ",data:"マルゲリータ"},
                            {type:"postback",label:"マリナーラ",data:"マリナーラ"},
                            {type:"uri", label: "すべてのメニュー", uri:"https://www.dominos.jp/order/pizza/search/"}
                        ]
                    }
                }
            },
            line_template_button_uri_more_than_3: { // Will catch exception in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文のピザをお選びください。",
                    template: {
                        type: "buttons",
                        text: "ご注文のピザをお選びください。",
                        actions: [
                            {type:"postback",label:"マルゲリータ",data:"マルゲリータ"},
                            {type:"postback",label:"マリナーラ",data:"マリナーラ"},
                            {type:"postback",label:"カプリチョーザ",data:"カプリチョーザ"},
                            {type:"uri", label: "すべてのメニュー", uri:"https://www.dominos.jp/order/pizza/search/"}
                        ]
                    }
                }
            },
            line_template_confirm: { // Will be quick repy in facebook.
                message_to_confirm: {
                    type: "template",
                    altText: "ご注文は以上ですか？",
                    template: {
                        type: "confirm",
                        text: "ご注文は以上ですか？",
                        actions: [
                            {type:"message",label:"はい",text:"はい"},
                            {type:"message",label:"いいえ",text:"いいえ"}
                        ]
                    }
                }
            },
            line_template_carousel: { // Will be template generic
                message_to_confirm: {
                    type: "template",
                    altText: "Carousel Template",
                    template: {
                        type: "carousel",
                        columns: [{
                            thumbnailImageUrl: "https://www.dominos.jp/common/img/itemimgsx/90.jpg?_=12016",
                            text: "マルゲリータ",
                            actions: [
                                {type:"postback", label:"注文する", data:"マルゲリータ"},
                                {type:"uri", label:"詳細", uri:"https://www.dominos.jp/order/pizza/detail/99999/19001/90"}
                            ]
                        },{
                            thumbnailImageUrl: "https://www.dominos.jp/common/img/itemimgsx/216.jpg?_=12016",
                            text: "ジェノベーゼ",
                            actions: [
                                {type:"postback", label:"注文する", data:"ジェノベーゼ"},
                                {type:"uri", label:"詳細", uri:"https://www.dominos.jp/order/pizza/detail/99999/19001/216"}
                            ]
                        }]
                    }
                }
            },
            facebook_text: { // Will be text in line.
                message_to_confirm: {
                    text: "ご注文のピザは？"
                }
            },
            facebook_quick_reply: { // Will be template button message in line.
                message_to_confirm: {
                    text: "ご注文のピザをお選びください。",
                    quick_replies: [
                        {content_type:"text",title:"マルゲリータ",payload:"マルゲリータ"},
                        {content_type:"text",title:"マリナーラ",payload:"マリナーラ"},
                    ]
                }
            },
            facebook_quick_reply_more_than_4: { // Will be text in line.
                message_to_confirm: {
                    text: "ご注文のピザをお選びください。",
                    quick_replies: [
                        {content_type:"text",title:"マルゲリータ",payload:"マルゲリータ"},
                        {content_type:"text",title:"マリナーラ",payload:"マリナーラ"},
                        {content_type:"text",title:"カプリチョーザ",payload:"カプリチョーザ"},
                        {content_type:"text",title:"アラビアータ",payload:"アラビアータ"},
                        {content_type:"text",title:"クアトロフォルマッジ",payload:"クアトロフォルマッジ"},
                    ]
                }
            },
            facebook_template_button_postback: { // Will be template button postback in line.
                message_to_confirm: {
                    attachment: {
                        type: "template",
                        payload: {
                            template_type: "button",
                            text: "ご注文のピザをお選びください。",
                            buttons: [
                                {type: "postback", title: "マルゲリータ", payload: "マルゲリータ"},
                                {type: "postback", title: "マリナーラ", payload: "マリナーラ"}
                            ]
                        }
                    }
                }
            },
            facebook_template_button_web_url: { // Will be template button postback and uri in line.
                message_to_confirm: {
                    attachment: {
                        type: "template",
                        payload: {
                            template_type: "button",
                            text: "ご注文のピザをお選びください。",
                            buttons: [
                                {type: "postback", title: "マルゲリータ", payload: "マルゲリータ"},
                                {type: "postback", title: "マリナーラ", payload: "マリナーラ"},
                                {type: "web_url", title: "すべてのメニュー", url: "https://www.dominos.jp/order/pizza/search/"}
                            ]
                        }
                    }
                }
            },
            facebook_template_generic: { // Will be template carousel in line.
                message_to_confirm: {
                    attachment: {
                        type: "template",
                        payload: {
                            template_type: "generic",
                            elements: [{
                                title: "マルゲリータ",
                                image_url: "https://www.dominos.jp/common/img/itemimgsx/90.jpg?_=12016",
                                buttons: [
                                    {type: "postback", title: "注文する", payload: "マルゲリータ"},
                                    {type: "web_url", title: "詳細", url: "https://www.dominos.jp/order/pizza/detail/99999/19001/90"}
                                ]
                            },{
                                title: "ジェノベーゼ",
                                image_url: "https://www.dominos.jp/common/img/itemimgsx/216.jpg?_=12016",
                                buttons: [
                                    {type: "postback", title: "注文する", payload: "ジェノベーゼ"},
                                    {type: "web_url", title: "詳細", url: "https://www.dominos.jp/order/pizza/detail/99999/19001/216"}
                                ]
                            }]
                        }
                    }
                }
            }/*,
            line_image: {
                message_to_confirm: {
                    type: "image",
                    originalContentUrl: "https://www.dropbox.com/sh/lbmx3s1yg392mvh/AAAwCzdEjO_I5OK9nrbDurdra?dl=1"
                }
            },
            line_video: {
                message_to_confirm: {
                    type: "video",
                    originalContentUrl: "https://www.dropbox.com/sh/lbmx3s1yg392mvh/AAAwCzdEjO_I5OK9nrbDurdra?dl=1"
                }
            },
            line_audio: {
                message_to_confirm: {
                    type: "sticker",
                    originalContentUrl: "https://www.dropbox.com/sh/lbmx3s1yg392mvh/AAAwCzdEjO_I5OK9nrbDurdra?dl=1"
                }
            },
            line_sticker: {
                message_to_confirm: {
                    type: "sticker",

                }
            },
            line_location: {
                message_to_confirm: {
                    type: "location",

                }
            },
            line_imagemap: {
                message_to_confirm: {
                    type: "imagemap",
                    originalContentUrl: "https://www.dropbox.com/sh/lbmx3s1yg392mvh/AAAwCzdEjO_I5OK9nrbDurdra?dl=1"
                }
            }
            */
        };

        this.clear_context_on_finish = true;
    }

    // パラメーターが全部揃ったら実行する処理を記述します。
    finish(bot, bot_event, context){
        let messages = [bot.create_text_message("完了")];
        return bot.reply(bot_event, messages);
    }
};
<|endoftext|>


<commit_before>//GLOBALS
var G = {
  base_url: "http://localhost:37265"
};

var Symbiote = {
  is_error_response: function( response ){
    return 'ERROR' == response.outcome;
  },
  display_error_response: function( response ){
    alert( "Frank isn't happy: "+response.reason+"\n"
        +"details: "+response.details );
  },
  display_chatting_popup: function() {

    $('#loading').show();
  },
  hide_chatting_popup: function() {
    $('#loading').hide();
  }
}

function classClicked(link){
    var command = {
      query: "view marked:'" + link.innerHTML + "'",
      operation: {
        method_name: 'flash',
        arguments: []
      }
    };

    Symbiote.display_chatting_popup();
    $.ajax({
      type: "POST",
      dataType: "json",
      data: JSON.stringify( command ),
      url: G.base_url + "/map",
      success: function(data) {
        if( Symbiote.is_error_response( data ) )
          Symbiote.display_error_response( data );
      },
      error: function(xhr,status,error) {
        Symbiote.hide_chatting_popup();
        alert( "Error while talking to Frank: " + status );
      },
      complete: function(xhr,status) {
        Symbiote.hide_chatting_popup();
      }
    });
  }

$(document).ready(function() {
	$("#tabs").tabs();
	$('#loading').hide();
	$('#dump_button').click( function(){
	 Symbiote.display_chatting_popup();

    $.ajax({
      type: "POST",
      dataType: "json",
      data: '["DUMMY"]', // a bug in cocoahttpserver means it can't handle POSTs without a body
      url: G.base_url + "/dump",
      success: function(data) {
		$('div#dom_dump').append( JsonTools.convert_json_to_dom( data ) );
		   $("#dom_dump").treeview({
								   collapsed: false
								   });
		 Symbiote.hide_chatting_popup();
      },
      error: function(xhr,status,error) {
        $('#loading').hide();
        alert( "Error while talking to Frank: " + status );
      }
    });

    $.ajax({
      type: "POST",
      dataType: "json",
      data: '["DUMMY"]', // a bug in cocoahttpserver means it can't handle POSTs without a body
      url: G.base_url + "/inspect",
      success: function(data) {
		$('#loading').hide();
		var test = eval(data);
		var html = '
';
        html +=	'
Marked (accessibilityLabel)	Class
';

		for (index in test)
  		{
  			var elem = test[index];
  			html += '
' + elem.label+ '	' + elem.class + '
';
  		}
		html += '
';
		$('div#access_dump').html(html);
      },
      error: function(xhr,status,error) {
      $('#loading').hide();
        alert( "Error while talking to Frank: " + status );
      }
    });

  });

  $('#flash_button').click( function(){
    var command = {
      query: $("input#query").val(),
      operation: {
        method_name: 'flash',
        arguments: []
      }
    };

    Symbiote.display_chatting_popup();
    $.ajax({
      type: "POST",
      dataType: "json",
      data: JSON.stringify( command ),
      url: G.base_url + "/map",
      success: function(data) {
        if( Symbiote.is_error_response( data ) )
          Symbiote.display_error_response( data );
      },
      error: function(xhr,status,error) {
        alert( "Error while talking to Frank: " + status );
        $('#loading').hide();
      },
      complete: function(xhr,status) {
        Symbiote.hide_chatting_popup();
      }
    });
  });

});
<commit_msg>Don't append each DOM dump to the last one. This makes the browser get really slow really fast.<commit_after>//GLOBALS
var G = {
  base_url: "http://localhost:37265"
};

var Symbiote = {
  is_error_response: function( response ){
    return 'ERROR' == response.outcome;
  },
  display_error_response: function( response ){
    alert( "Frank isn't happy: "+response.reason+"\n"
        +"details: "+response.details );
  },
  display_chatting_popup: function() {

    $('#loading').show();
  },
  hide_chatting_popup: function() {
    $('#loading').hide();
  }
}

function classClicked(link){
    var command = {
      query: "view marked:'" + link.innerHTML + "'",
      operation: {
        method_name: 'flash',
        arguments: []
      }
    };

    Symbiote.display_chatting_popup();
    $.ajax({
      type: "POST",
      dataType: "json",
      data: JSON.stringify( command ),
      url: G.base_url + "/map",
      success: function(data) {
        if( Symbiote.is_error_response( data ) )
          Symbiote.display_error_response( data );
      },
      error: function(xhr,status,error) {
        Symbiote.hide_chatting_popup();
        alert( "Error while talking to Frank: " + status );
      },
      complete: function(xhr,status) {
        Symbiote.hide_chatting_popup();
      }
    });
  }

$(document).ready(function() {
	$("#tabs").tabs();
	$('#loading').hide();
	$('#dump_button').click( function(){
	 Symbiote.display_chatting_popup();

    $.ajax({
      type: "POST",
      dataType: "json",
      data: '["DUMMY"]', // a bug in cocoahttpserver means it can't handle POSTs without a body
      url: G.base_url + "/dump",
      success: function(data) {
		$('div#dom_dump').html( JsonTools.convert_json_to_dom( data ) );
		   $("#dom_dump").treeview({
								   collapsed: false
								   });
		 Symbiote.hide_chatting_popup();
      },
      error: function(xhr,status,error) {
        $('#loading').hide();
        alert( "Error while talking to Frank: " + status );
      }
    });

    $.ajax({
      type: "POST",
      dataType: "json",
      data: '["DUMMY"]', // a bug in cocoahttpserver means it can't handle POSTs without a body
      url: G.base_url + "/inspect",
      success: function(data) {
		$('#loading').hide();
		var test = eval(data);
		var html = '
';
        html +=	'
Marked (accessibilityLabel)	Class
';

		for (index in test)
  		{
  			var elem = test[index];
  			html += '
' + elem.label+ '	' + elem.class + '
';
  		}
		html += '
';
		$('div#access_dump').html(html);
      },
      error: function(xhr,status,error) {
      $('#loading').hide();
        alert( "Error while talking to Frank: " + status );
      }
    });

  });

  $('#flash_button').click( function(){
    var command = {
      query: $("input#query").val(),
      operation: {
        method_name: 'flash',
        arguments: []
      }
    };

    Symbiote.display_chatting_popup();
    $.ajax({
      type: "POST",
      dataType: "json",
      data: JSON.stringify( command ),
      url: G.base_url + "/map",
      success: function(data) {
        if( Symbiote.is_error_response( data ) )
          Symbiote.display_error_response( data );
      },
      error: function(xhr,status,error) {
        alert( "Error while talking to Frank: " + status );
        $('#loading').hide();
      },
      complete: function(xhr,status) {
        Symbiote.hide_chatting_popup();
      }
    });
  });

});
<|endoftext|>


<commit_before>'use strict';

// Karma
module.exports = function(config) {
	config.set({

		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: '',

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['jasmine'],

		// list of files / patterns to load in the browser
		files: [
			//'app/bower_components/jquery/jquery.js',
			'app/bower_components/angular/angular.js',
			'app/bower_components/angular-mocks/angular-*.js',
			'app/bower_components/angular-route/angular-route.js',
			'app/bower_components/angular-resource/angular-resource.js',
			'app/bower_components/socket.io-client/socket.io.js',
			'app/bower_components/angular-socket.io-mock.js',
			/*'app/bower_components/angular-sanitize/angular-sanitize.js',
			'app/bower_components/angular-cookies/angular-cookies.js',*/
			//'app/bower_components/underscore/underscore.js',
			'app/norris-nrti.js',
			'app/Model/*.js',
			'app/Model/GraphsModel/*.js',
			'app/Model/FlowsModel/*.js',
			'app/Model/PagesModel/*.js',
			'app/View/PagesView/*.js',
			'app/View/GraphsView/*.js',
			'app/Controller/*.js',
			'app/Controller/GraphsControllers/*.js',
			'app/Controller/PagesControllers/*.js',
			'test/unit/Model/*.js',
			'test/unit/Controller/*.js',
			'test/unit/View/*.js'
		],

		// list of files to exclude
		exclude: [

		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			// source files, that you wanna generate coverage for
			// do not include tests or libraries
			// (these files will be instrumented by Istanbul)
			'app/app.js': ['coverage'],
			'app/Model/*.js': ['coverage'],
			'app/Model/GraphsModel/*.js': ['coverage'],
			'app/Model/FlowsModel/*.js': ['coverage'],
			'app/Model/PagesModel/*.js': ['coverage'],
			'app/View/*.js': ['coverage'],
			'app/View/GraphsView/*.js': ['coverage'],
			'app/View/PagesView/*.js': ['coverage'],
			'app/Controller/*.js': ['coverage'],
			'app/Controller/GraphsControllers/*.js': ['coverage'],
			'app/Controller/PagesControllers/*.js': ['coverage']
		},

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['progress', 'junit', 'coverage'],

		junitReporter: {
			outputFile: '../gh-pages/reports-frontend/test-result.xml'
		},

		// configure the reporter
		coverageReporter: {
			dir: '../gh-pages/reports-frontend/coverage/',
			reporters:[
		        {
		        	type: 'text',
		        	subdir: '.',
		        	file: 'text.txt'
		        },
		        {
		        	type: 'text-summary',
		        	subdir: '.',
		        	file: 'text-summary.txt'
		        },
				{
					type: 'lcov',
					subdir: '.',
					suite: 'frontend'
				}
			],
		},

		// web server port
		port: 15077,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_DEBUG,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: false,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: ['PhantomJS'],

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: true
	});
};<commit_msg>some fixes<commit_after>'use strict';

// Karma
module.exports = function(config) {
	config.set({

		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: '',

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['jasmine'],

		// list of files / patterns to load in the browser
		files: [
			//'app/bower_components/jquery/jquery.js',
			'app/bower_components/angular/angular.js',
			'app/bower_components/angular-mocks/angular-*.js',
			'app/bower_components/angular-route/angular-route.js',
			'app/bower_components/angular-resource/angular-resource.js',
			'app/bower_components/socket.io-client/socket.io.js',
			'app/bower_components/angular-socket.io-mock.js',
			/*'app/bower_components/angular-sanitize/angular-sanitize.js',
			'app/bower_components/angular-cookies/angular-cookies.js',*/
			//'app/bower_components/underscore/underscore.js',
			'app/norris-nrti.js',
			'app/Model/*.js',
			'app/Model/GraphsModel/*.js',
			'app/Model/FlowsModel/*.js',
			'app/Model/PagesModel/*.js',
			'app/View/PagesView/*.js',
			'app/View/GraphsView/*.js',
			'app/Controller/*.js',
			'app/Controller/GraphsControllers/*.js',
			//'app/Controller/PagesControllers/*.js',
			'test/unit/Model/*.js',
			'test/unit/Controller/*.js',
			'test/unit/View/*.js'
		],

		// list of files to exclude
		exclude: [

		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			// source files, that you wanna generate coverage for
			// do not include tests or libraries
			// (these files will be instrumented by Istanbul)
			'app/app.js': ['coverage'],
			'app/Model/*.js': ['coverage'],
			'app/Model/GraphsModel/*.js': ['coverage'],
			'app/Model/FlowsModel/*.js': ['coverage'],
			'app/Model/PagesModel/*.js': ['coverage'],
			'app/View/*.js': ['coverage'],
			'app/View/GraphsView/*.js': ['coverage'],
			'app/View/PagesView/*.js': ['coverage'],
			'app/Controller/*.js': ['coverage'],
			'app/Controller/GraphsControllers/*.js': ['coverage'],
			'app/Controller/PagesControllers/*.js': ['coverage']
		},

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['progress', 'junit', 'coverage'],

		junitReporter: {
			outputFile: '../gh-pages/reports-frontend/test-result.xml'
		},

		// configure the reporter
		coverageReporter: {
			dir: '../gh-pages/reports-frontend/coverage/',
			reporters:[
		        {
		        	type: 'text',
		        	subdir: '.',
		        	file: 'text.txt'
		        },
		        {
		        	type: 'text-summary',
		        	subdir: '.',
		        	file: 'text-summary.txt'
		        },
				{
					type: 'lcov',
					subdir: '.',
					suite: 'frontend'
				}
			],
		},

		// web server port
		port: 15077,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_DEBUG,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: false,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: ['PhantomJS'],

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: true
	});
};<|endoftext|>


<commit_before>import Vue from 'vue'
import Router from 'vue-router'
import slugify from 'slugify'
import { refreshLoginStatus } from '@/auth'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/register',
      name: 'register',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/auth/TopBar'),
        default: () => import(/* webpackChunkName: "register" */ './views/auth/Register')
      }
    },
    {
      path: '/register-done',
      name: 'register-done',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/auth/TopBar'),
        default: () => import(/* webpackChunkName: "register" */ './views/auth/RegisterDone')
      }
    },
    {
      path: '/login',
      name: 'login',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/auth/TopBar'),
        default: () => import(/* webpackChunkName: "login" */ './views/auth/Login')
      }
    },
    {
      path: '/loginCallback',
      name: 'loginCallback',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/auth/TopBar'),
        default: () => import(/* webpackChunkName: "login" */ './views/auth/LoginCallback')
      }
    },
    {
      path: '/',
      name: 'home',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/TopBar'),
        default: () => import(/* webpackChunkName: "about" */ './views/Home'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/BottomBar')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/profile',
      name: 'profile',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/TopBar'),
        default: () => import(/* webpackChunkName: "about" */ './views/Profile'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/BottomBar')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps',
      name: 'camps',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/TopBar'),
        default: () => import(/* webpackChunkName: "camps" */ './views/Camps'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/BottomBar')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps/:campId/:campTitle?',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/camp/TopBar'),
        default: () => import(/* webpackChunkName: "camp" */ './views/camp/Camp'),
        aside: () => import(/* webpackChunkName: "periods" */ './views/camp/Periods'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/camp/BottomBar')
      },
      beforeEnter: requireAuth,
      props: {
        default: route => ({ camp: campFromRoute(route) }),
        aside: route => ({ camp: campFromRoute(route) })
      },
      children: [
        {
          path: 'collaborators',
          name: 'camp/collaborators',
          component: () => import(/* webpackChunkName: "campCollaborators" */ './components/camp/Collaborators.vue')
        },
        {
          path: 'overview',
          name: 'camp/overview',
          component: () => import(/* webpackChunkName: "campPicasso" */ './views/camp/CampOverview'),
          beforeEnter: parseBooleanInQuery
        },
        {
          path: 'admin',
          name: 'camp/admin',
          component: () => import(/* webpackChunkName: "campAdmin" */ './views/camp/Admin')
        }
      ]
    },
    {
      path: '/camps/:campId/:campTitle/events/:eventInstanceId/:eventName?',
      name: 'event',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/camp/TopBar'),
        default: () => import(/* webpackChunkName: "event" */ './views/event/Event'),
        aside: () => import(/* webpackChunkName: "day" */ './views/event/DayOverview'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/camp/BottomBar')
      },
      beforeEnter: requireAuth,
      props: {
        default: route => ({ eventInstance: eventInstanceFromRoute(route) }),
        aside: route => ({ day: dayFromEventInstanceInRoute(route) })
      }
    }
  ]
})

function requireAuth (to, from, next) {
  refreshLoginStatus(false).then(loggedIn => {
    if (loggedIn) {
      next()
    } else {
      next({ name: 'login', query: to.path === '/' ? {} : { redirect: to.fullPath } })
    }
  })
}

function parseBooleanInQuery (to, from, next) {
  for (const [key, val] of Object.entries(to.query)) {
    if (val === null || val === 'true') {
      to.query[key] = true
    } else if (val === 'false') {
      to.query[key] = false
    }
  }
  next()
}

export function campFromRoute (route) {
  return function () {
    return this.api.get('/camp/' + route.params.campId)
  }
}

function eventInstanceFromRoute (route) {
  return function () {
    return this.api.get('/event-instance/' + route.params.eventInstanceId)
  }
}

function dayFromEventInstanceInRoute (route) {
  return function () {
    return this.api.get('/event-instance/' + route.params.eventInstanceId).day()
  }
}

export function campRoute (camp, subroute) {
  if (camp._meta.loading) return {}
  const routeName = subroute ? 'camp/' + subroute : 'camp'
  return { name: routeName, params: { campId: camp.id, campTitle: slugify(camp.title) } }
}

export function eventInstanceRoute (camp, eventInstance) {
  if (camp._meta.loading || eventInstance._meta.loading || eventInstance.event()._meta.loading) return {}
  return {
    name: 'event',
    params: { campId: camp.id, campTitle: slugify(camp.title), eventInstanceId: eventInstance.id, eventName: slugify(eventInstance.event().title) }
  }
}
<commit_msg>Fixed campRoute without subroute<commit_after>import Vue from 'vue'
import Router from 'vue-router'
import slugify from 'slugify'
import { refreshLoginStatus } from '@/auth'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/register',
      name: 'register',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/auth/TopBar'),
        default: () => import(/* webpackChunkName: "register" */ './views/auth/Register')
      }
    },
    {
      path: '/register-done',
      name: 'register-done',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/auth/TopBar'),
        default: () => import(/* webpackChunkName: "register" */ './views/auth/RegisterDone')
      }
    },
    {
      path: '/login',
      name: 'login',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/auth/TopBar'),
        default: () => import(/* webpackChunkName: "login" */ './views/auth/Login')
      }
    },
    {
      path: '/loginCallback',
      name: 'loginCallback',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/auth/TopBar'),
        default: () => import(/* webpackChunkName: "login" */ './views/auth/LoginCallback')
      }
    },
    {
      path: '/',
      name: 'home',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/TopBar'),
        default: () => import(/* webpackChunkName: "about" */ './views/Home'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/BottomBar')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/profile',
      name: 'profile',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/TopBar'),
        default: () => import(/* webpackChunkName: "about" */ './views/Profile'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/BottomBar')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps',
      name: 'camps',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/TopBar'),
        default: () => import(/* webpackChunkName: "camps" */ './views/Camps'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/BottomBar')
      },
      beforeEnter: requireAuth
    },
    {
      path: '/camps/:campId/:campTitle?',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/camp/TopBar'),
        default: () => import(/* webpackChunkName: "camp" */ './views/camp/Camp'),
        aside: () => import(/* webpackChunkName: "periods" */ './views/camp/Periods'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/camp/BottomBar')
      },
      beforeEnter: requireAuth,
      props: {
        default: route => ({ camp: campFromRoute(route) }),
        aside: route => ({ camp: campFromRoute(route) })
      },
      children: [
        {
          path: 'collaborators',
          name: 'camp/collaborators',
          component: () => import(/* webpackChunkName: "campCollaborators" */ './components/camp/Collaborators.vue')
        },
        {
          path: 'overview',
          name: 'camp/overview',
          component: () => import(/* webpackChunkName: "campPicasso" */ './views/camp/CampOverview'),
          beforeEnter: parseBooleanInQuery
        },
        {
          path: 'admin',
          name: 'camp/admin',
          component: () => import(/* webpackChunkName: "campAdmin" */ './views/camp/Admin')
        }
      ]
    },
    {
      path: '/camps/:campId/:campTitle/events/:eventInstanceId/:eventName?',
      name: 'event',
      components: {
        topbar: () => import(/* webpackChunkName: "navigation" */ './views/camp/TopBar'),
        default: () => import(/* webpackChunkName: "event" */ './views/event/Event'),
        aside: () => import(/* webpackChunkName: "day" */ './views/event/DayOverview'),
        bottombar: () => import(/* webpackChunkName: "navigation" */ './views/camp/BottomBar')
      },
      beforeEnter: requireAuth,
      props: {
        default: route => ({ eventInstance: eventInstanceFromRoute(route) }),
        aside: route => ({ day: dayFromEventInstanceInRoute(route) })
      }
    }
  ]
})

function requireAuth (to, from, next) {
  refreshLoginStatus(false).then(loggedIn => {
    if (loggedIn) {
      next()
    } else {
      next({ name: 'login', query: to.path === '/' ? {} : { redirect: to.fullPath } })
    }
  })
}

function parseBooleanInQuery (to, from, next) {
  for (const [key, val] of Object.entries(to.query)) {
    if (val === null || val === 'true') {
      to.query[key] = true
    } else if (val === 'false') {
      to.query[key] = false
    }
  }
  next()
}

export function campFromRoute (route) {
  return function () {
    return this.api.get('/camp/' + route.params.campId)
  }
}

function eventInstanceFromRoute (route) {
  return function () {
    return this.api.get('/event-instance/' + route.params.eventInstanceId)
  }
}

function dayFromEventInstanceInRoute (route) {
  return function () {
    return this.api.get('/event-instance/' + route.params.eventInstanceId).day()
  }
}

export function campRoute (camp, subroute) {
  if (camp._meta.loading) return {}
  const routeName = subroute ? 'camp/' + subroute : 'camp/overview'
  return { name: routeName, params: { campId: camp.id, campTitle: slugify(camp.title) } }
}

export function eventInstanceRoute (camp, eventInstance) {
  if (camp._meta.loading || eventInstance._meta.loading || eventInstance.event()._meta.loading) return {}
  return {
    name: 'event',
    params: { campId: camp.id, campTitle: slugify(camp.title), eventInstanceId: eventInstance.id, eventName: slugify(eventInstance.event().title) }
  }
}
<|endoftext|>


<commit_before>import Ember from 'ember';

/* globals FB */

var facebookScriptPromise;

export default Ember.Object.extend({
  /*
   * A tracking object implementing `shared(serviceName, payload)` and/or
   * `clicked(serviceName, payload)` can be set on this object, and will
   * be delegated to if present. Not all Facebook
   * components support these events in all configurations.
   */
  tracking: null, // optional injection

  /*
   * This is required for certain uses of plugins, e.g. when using tagName="a"
   * for the facebook-share component.
   *
   * You can simply specify it in the handlebars when using this component, but this
   * component will also look for a global FACEBOOK_APP_ID or a meta tag of the form:
   *
   *   <meta property="fb:app_id" content="[FB_APP_ID]" />
   */
  appId: function(){
    return Ember.$("meta[property='fb:app_id']").attr('content') || window.FACEBOOK_APP_ID || 570211946345492;
  },

  load: function() {
    var self = this;
    if (!facebookScriptPromise) {
      facebookScriptPromise = new Ember.RSVP.Promise(function(resolve/*, reject*/) {
        window.fbAsyncInit = function() {
          FB.init({
            appId      : self.appId(),
            xfbml      : true,
            version    : 'v2.4'
          });
          Ember.run(function(){
            resolve(FB);
          });
        };

      (function(d, s, id){
         var js, fjs = d.getElementsByTagName(s)[0];
         if (d.getElementById(id)) {return;}
         js = d.createElement(s); js.id = id;
         js.src = "//connect.facebook.net/en_US/sdk.js";
         fjs.parentNode.insertBefore(js, fjs);
       }(document, 'script', 'facebook-jssdk'));
      });
    }
    return facebookScriptPromise;
  },

  clicked: function(payload) {
    var tracking = this.tracking;
    if(!tracking) { return; }
    if(tracking.clicked) {
      tracking.clicked('facebook', payload);
    }

  }
});
<commit_msg>FB App ID<commit_after>import Ember from 'ember';

/* globals FB */

var facebookScriptPromise;

export default Ember.Object.extend({
  /*
   * A tracking object implementing `shared(serviceName, payload)` and/or
   * `clicked(serviceName, payload)` can be set on this object, and will
   * be delegated to if present. Not all Facebook
   * components support these events in all configurations.
   */
  tracking: null, // optional injection

  /*
   * This is required for certain uses of plugins, e.g. when using tagName="a"
   * for the facebook-share component.
   *
   * You can simply specify it in the handlebars when using this component, but this
   * component will also look for a global FACEBOOK_APP_ID or a meta tag of the form:
   *
   *   <meta property="fb:app_id" content="[FB_APP_ID]" />
   */
  appId: function(){
    return Ember.$("meta[property='fb:app_id']").attr('content') || window.FACEBOOK_APP_ID || 555502871277304;
  },

  load: function() {
    var self = this;
    if (!facebookScriptPromise) {
      facebookScriptPromise = new Ember.RSVP.Promise(function(resolve/*, reject*/) {
        window.fbAsyncInit = function() {
          FB.init({
            appId      : self.appId(),
            xfbml      : true,
            version    : 'v2.4'
          });
          Ember.run(function(){
            resolve(FB);
          });
        };

      (function(d, s, id){
         var js, fjs = d.getElementsByTagName(s)[0];
         if (d.getElementById(id)) {return;}
         js = d.createElement(s); js.id = id;
         js.src = "//connect.facebook.net/en_US/sdk.js";
         fjs.parentNode.insertBefore(js, fjs);
       }(document, 'script', 'facebook-jssdk'));
      });
    }
    return facebookScriptPromise;
  },

  clicked: function(payload) {
    var tracking = this.tracking;
    if(!tracking) { return; }
    if(tracking.clicked) {
      tracking.clicked('facebook', payload);
    }

  }
});
<|endoftext|>


<commit_before>//b2b logic for hidden imputs
$("#Dr, #Mr, #Mrs, #Ms").change(function () {
    var titleOptionsArr = document.querySelectorAll('#physics_title > option');
    for(var i = 0; i < titleOptionsArr.length; i++){
        titleOptionsArr[i].removeAttribute('selected');
    };

    var valId = this.id;
    $("#physics_title option[value=" + valId + "]").attr('selected', 'true');
});

$("#hear1, #hear2, #hear3, #hear4").change(function () {
    var leadOptionsArr = document.querySelectorAll('#physics_lead > option');
    for(var i = 0; i < leadOptionsArr.length; i++){
        leadOptionsArr[i].removeAttribute('selected');
    };

    var valValue = this.value;
    $("#physics_lead option[value='" + valValue + "']").attr('selected', 'true');
    if(this.id == 'hear4'){
        $('input#other').removeAttr('disabled');
    } else{
        $('input#other').attr('disabled','disabled');
    }

});

 var form = document.querySelector('.form_accelerate');
    form.noValidate = true;
    form.addEventListener('submit', function(event) {
        if (!event.target.checkValidity()) {
            event.preventDefault();
            for(var i = 0; i < event.srcElement.length;i++){
                if(event.srcElement[i].value == '' && event.srcElement[i].required == true){
                    var alert = document.createElement('span');
                    alert.innerHTML = 'Please fill out this field';
                    alert.setAttribute('style','color:red;');
                    alert.setAttribute('class','temp-alert');

                    if (event.srcElement[i].parentElement.getAttribute('class') == 'select'){
                        console.log(event.srcElement[i].parentElement.parentElement.appendChild(alert));
                    } else{
                        console.log(event.srcElement[i].parentElement.appendChild(alert));
                    }

                    setTimeout(function() {
                        var tempAlert = document.querySelectorAll('.temp-alert');
                        for(var i = 0; i < tempAlert.length; i++){
                             tempAlert[i].remove();
                        }
                    }, 9000);
                }
            }
        }
    }, false);

window.onload = function(){
    $('input[name=COMPLETE_URL]').val(location.origin+'/b2b-thanks');
};

<commit_msg>Fix: b2b form required message<commit_after>//b2b logic for hidden imputs
$("#Dr, #Mr, #Mrs, #Ms").change(function () {
    var titleOptionsArr = document.querySelectorAll('#physics_title > option');
    for (var i = 0; i < titleOptionsArr.length; i++) {
        titleOptionsArr[i].removeAttribute('selected');
    }
    ;

    var valId = this.id;
    $("#physics_title option[value=" + valId + "]").attr('selected', 'true');
});

$("#hear1, #hear2, #hear3, #hear4").change(function () {
    var leadOptionsArr = document.querySelectorAll('#physics_lead > option');
    for (var i = 0; i < leadOptionsArr.length; i++) {
        leadOptionsArr[i].removeAttribute('selected');
    }
    ;

    var valValue = this.value;
    $("#physics_lead option[value='" + valValue + "']").attr('selected', 'true');
    if (this.id == 'hear4') {
        $('input#other').removeAttr('disabled');
    } else {
        $('input#other').attr('disabled', 'disabled');
    }

});

var form = document.querySelector('.form_accelerate');
form.noValidate = true;
form.addEventListener('submit', function (event) {
    if (!event.target.checkValidity()) {
        event.preventDefault();
        var tempAlert = document.querySelectorAll('.temp-alert');
        for (var i = 0; i < tempAlert.length; i++) {
            tempAlert[i].remove();
        }
        for (var i = 0; i < event.srcElement.length; i++) {
            if (event.srcElement[i].value == '' && event.srcElement[i].required == true) {
                var alert = document.createElement('span');
                alert.innerHTML = 'Please fill out this field';
                alert.setAttribute('style', 'color:red;');
                alert.setAttribute('class', 'temp-alert');

                if (event.srcElement[i].parentElement.getAttribute('class') == 'select') {
                    console.log(event.srcElement[i].parentElement.parentElement.appendChild(alert));
                } else {
                    console.log(event.srcElement[i].parentElement.appendChild(alert));
                }

            }
        }
    }
}, false);

window.onload = function () {
    $('input[name=COMPLETE_URL]').val(location.origin + '/b2b-thanks');
};

<|endoftext|>


<commit_before>define(['regs-data', 'samplejson'], function(RegsData, testjson) {
  describe("RegsData module", function() {
    RegsData.parse(testjson);

    it("should have a regStructure array", function() {
      expect(RegsData.regStructure).toBeTruthy();
    });

    it("should have a regStructure array with 7 values", function() {
      expect(RegsData.regStructure.length).toEqual( 7 );
    });

    it("should have content", function() {
      expect(RegsData.content).toBeTruthy();
    });

    it("should have content for 2345-9-a", function() {
      var content = "
<dfn>placerat in egestas.</dfn> Sed erat enim, hendrerit mollis tempus et, consequat et ante. Donec imperdiet orci eget nisi lobortis molestie. Nullam pellentesque scelerisque hendrerit
";
      expect(RegsData.content['2345-9-a'].valueOf()).toEqual(content);
    });

    it("should retrieve 2345-9", function() {
      expect(RegsData.retrieve('2345-9')).toEqual("asdfksjflksjdf");
    });

    it("should get children", function() {
      var arr = ["2345-9-a-2", "2345-9-a-1"];

      expect(RegsData.getChildren('2345-9-a')).toEqual(arr);
    });

    it("should not get children", function() {
      expect(RegsData.getChildren('233')).toEqual([]);
    });

    it("should get parent", function() {
      expect(RegsData.getParent('2345-9-b')).toEqual("asdfksjflksjdf");
    });

    it("should differentiate between regStructure presence and being loaded", function() {
      expect(RegsData.isLoaded("2345-9-b-1")).toEqual(false);
    });

  });
});
<commit_msg>update tests to reflect new model api<commit_after>define(['regs-data', 'samplejson'], function(RegsData, testjson) {
  describe("RegsData module", function() {
    RegsData.parse(testjson);

    it("should have a regStructure array", function() {
      expect(RegsData.regStructure).toBeTruthy();
    });

    it("should have a regStructure array with 8 values", function() {
      expect(RegsData.regStructure.length).toEqual( 8 );
    });

    it("should have content", function() {
      expect(RegsData.content).toBeTruthy();
    });

    it("should have content for 2345-9-a", function() {
      var content = "
<dfn>placerat in egestas.</dfn> Sed erat enim, hendrerit mollis tempus et, consequat et ante. Donec imperdiet orci eget nisi lobortis molestie. Nullam pellentesque scelerisque hendrerit
";
      expect(RegsData.content['2345-9-a'].valueOf()).toEqual(content);
    });

    it("should get 2345-9", function() {
      expect(RegsData.get('2345-9')).toEqual("asdfksjflksjdf");
    });

    it("should get children", function() {
      var arr = ["2345-9-a-2", "2345-9-a-1"];

      expect(RegsData.getChildren('2345-9-a')).toEqual(arr);
    });

    it("should not get children", function() {
      expect(RegsData.getChildren('233')).toEqual([]);
    });

    it("should get parent", function() {
      expect(RegsData.getParent('2345-9-b')).toEqual("asdfksjflksjdf");
    });

    it("should differentiate between regStructure presence and being loaded", function() {
      expect(RegsData.has("2345-9-b-1")).toEqual(false);
    });

  });
});
<|endoftext|>


<commit_before>angular.module('app.contacts').config(contactsConfig);

contactsConfig.$inject = ['$stateProvider'];
function contactsConfig($stateProvider) {
    $stateProvider.state('base.contacts', {
        url: '/contacts',
        views: {
            '@': {
                templateUrl: 'contacts/controllers/list.html',
                controller: ContactListController,
            },
        },
        ncyBreadcrumb: {
            label: 'Contacts',
        },
    });
}

angular.module('app.contacts').controller('ContactListController', ContactListController);

ContactListController.$inject = ['$scope', '$window', 'Settings', 'Account', 'Contact', 'LocalStorage'];
function ContactListController($scope, $window, Settings, Account, Contact, LocalStorage) {
    var storage = new LocalStorage('contactList');

    Settings.page.setAllTitles('list', 'contacts');

    /**
     * table object: stores all the information to correctly display the table
     */
    $scope.table = {
        page: 1,  // current page of pagination: 1-index
        pageSize: 20,  // number of items per page
        totalItems: 0, // total number of items
        filter: storage.get('filter', ''),  // search filter
        order: storage.get('order', {
            descending: true,
            column: 'modified',  // string: current sorted column
        }),
        visibility: storage.get('visibility', {
            name: true,
            contactInformation: true,
            worksAt: true,
            created: true,
            modified: true,
            tags: true,
        })};

    $scope.removeFromList = function(contact) {
        var index = $scope.table.items.indexOf(contact);
        $scope.table.items.splice(index, 1);
        $scope.$apply();
    };

    /**
     * updateTableSettings() puts the scope variables in local storage
     */
    function updateTableSettings() {
        storage.put('filter', $scope.table.filter);
        storage.put('order', $scope.table.order);
        storage.put('visibility', $scope.table.visibility);
    }

    /**
     * updateContacts() reloads the contacts through a service
     *
     * Updates table.items and table.totalItems
     */
    function updateContacts() {
        var sort = $scope.table.order.column;
        if ($scope.table.order.descending) {
            sort = '-'.concat(sort);
        }

        Contact.search({
            q: $scope.table.filter,
            page: $scope.table.page,
            size: $scope.table.pageSize,
            sort: sort,
        }, function(data) {
            $scope.table.items = data.objects;

            $scope.table.items.forEach(function(contact) {
                if (contact.accounts) {
                    contact.accounts.forEach(function(account) {
                        Account.get({id: account.id}, {cache: true}).$promise.then(function(accountData) {
                            account.primary_email_address = accountData.primary_email_address;
                            account.phone_numbers = accountData.phone_numbers;
                        });
                    });
                }
            });

            $scope.table.totalItems = data.total;
        });
    }

    /**
     * Watches the model info from the table that, when changed,
     * needs a new set of contacts
     */
    $scope.$watchGroup([
        'table.page',
        'table.order.column',
        'table.order.descending',
        'table.filter',
    ], function() {
        updateTableSettings();
        updateContacts();
    });

    /**
     * Watches the model info from the table that, when changed,
     * needs to store the info to the cache
     */
    $scope.$watchCollection('table.visibility', function() {
        updateTableSettings();
    });

    /**
     * setFilter() sets the filter of the table
     *
     * @param queryString string: string that will be set as the new filter on the table
     */
    $scope.setFilter = function(queryString) {
        $scope.table.filter = queryString;
    };

    /**
     * exportToCsv() creates an export link and opens it
     */
    $scope.exportToCsv = function() {
        var getParams = '';

        // Setup url.
        var url = '/contacts/export/';

        // If there is a filter, add it.
        if ($scope.table.filter) {
            getParams += '&export_filter=' + $scope.table.filter;
        }

        // Add all visible columns.
        angular.forEach($scope.table.visibility, function(value, key) {
            if (value) {
                getParams += '&export_columns=' + key;
            }
        });

        if (getParams) {
            url += '?' + getParams.substr(1);
        }

        // Open url.
        $window.open(url);
    };
}
<commit_msg>Fixed contact list<commit_after>angular.module('app.contacts').config(contactsConfig);

contactsConfig.$inject = ['$stateProvider'];
function contactsConfig($stateProvider) {
    $stateProvider.state('base.contacts', {
        url: '/contacts',
        views: {
            '@': {
                templateUrl: 'contacts/controllers/list.html',
                controller: ContactListController,
            },
        },
        ncyBreadcrumb: {
            label: 'Contacts',
        },
    });
}

angular.module('app.contacts').controller('ContactListController', ContactListController);

ContactListController.$inject = ['$scope', '$window', 'Settings', 'Account', 'Contact', 'LocalStorage'];
function ContactListController($scope, $window, Settings, Account, Contact, LocalStorage) {
    var storage = new LocalStorage('contactList');

    Settings.page.setAllTitles('list', 'contacts');

    /**
     * table object: stores all the information to correctly display the table
     */
    $scope.table = {
        page: 1,  // current page of pagination: 1-index
        pageSize: 20,  // number of items per page
        totalItems: 0, // total number of items
        filter: storage.get('filter', ''),  // search filter
        order: storage.get('order', {
            descending: true,
            column: 'modified',  // string: current sorted column
        }),
        visibility: storage.get('visibility', {
            name: true,
            contactInformation: true,
            worksAt: true,
            created: true,
            modified: true,
            tags: true,
        })};

    $scope.removeFromList = function(contact) {
        var index = $scope.table.items.indexOf(contact);
        $scope.table.items.splice(index, 1);
        $scope.$apply();
    };

    /**
     * updateTableSettings() puts the scope variables in local storage
     */
    function updateTableSettings() {
        storage.put('filter', $scope.table.filter);
        storage.put('order', $scope.table.order);
        storage.put('visibility', $scope.table.visibility);
    }

    /**
     * updateContacts() reloads the contacts through a service
     *
     * Updates table.items and table.totalItems
     */
    function updateContacts() {
        var sort = $scope.table.order.column;
        if ($scope.table.order.descending) {
            sort = '-'.concat(sort);
        }

        Contact.search({
            q: $scope.table.filter,
            page: $scope.table.page - 1,
            size: $scope.table.pageSize,
            sort: sort,
        }, function(data) {
            $scope.table.items = data.objects;

            $scope.table.items.forEach(function(contact) {
                if (contact.accounts) {
                    contact.accounts.forEach(function(account) {
                        Account.get({id: account.id}, {cache: true}).$promise.then(function(accountData) {
                            account.primary_email_address = accountData.primary_email_address;
                            account.phone_numbers = accountData.phone_numbers;
                        });
                    });
                }
            });

            $scope.table.totalItems = data.total;
        });
    }

    /**
     * Watches the model info from the table that, when changed,
     * needs a new set of contacts
     */
    $scope.$watchGroup([
        'table.page',
        'table.order.column',
        'table.order.descending',
        'table.filter',
    ], function() {
        updateTableSettings();
        updateContacts();
    });

    /**
     * Watches the model info from the table that, when changed,
     * needs to store the info to the cache
     */
    $scope.$watchCollection('table.visibility', function() {
        updateTableSettings();
    });

    /**
     * setFilter() sets the filter of the table
     *
     * @param queryString string: string that will be set as the new filter on the table
     */
    $scope.setFilter = function(queryString) {
        $scope.table.filter = queryString;
    };

    /**
     * exportToCsv() creates an export link and opens it
     */
    $scope.exportToCsv = function() {
        var getParams = '';

        // Setup url.
        var url = '/contacts/export/';

        // If there is a filter, add it.
        if ($scope.table.filter) {
            getParams += '&export_filter=' + $scope.table.filter;
        }

        // Add all visible columns.
        angular.forEach($scope.table.visibility, function(value, key) {
            if (value) {
                getParams += '&export_columns=' + key;
            }
        });

        if (getParams) {
            url += '?' + getParams.substr(1);
        }

        // Open url.
        $window.open(url);
    };
}
<|endoftext|>